Home > OS >  Combining various rows in a table based on a condition
Combining various rows in a table based on a condition

Time:01-18

need some help to construct a PostgreSQL query. I am trying to combine various rows in a Postgres table based on a certain condition.

Here's what the table looks like

Roll_No Role Address Type Address Value
0538 Home Address Line 1 123 Main Street
0538 Home Address Line 2 London
0538 Home Address Line 3 Rogers Street
0538 Home Address Line 4 United Kingdom
0538 Office Address Line 1 Adam Land
0538 Office Address Line 2 Valley Forge PA 19482
0538 Office Address Line 3 U.S.A
0738 School Address Line 1 Rogers Street
0738 School Address Line 2 London
0738 School Address Line 3 Holland Lane
0738 School Address Line 4 United Kingdom

I want to concatenate all address values of a specific role (eg. home, school, office) into one column. Address type can contain values like Address Line 1 to 8. Here, Home has Address Line 1 to 4 whereas office has Address Line 1 to 3.

Roll_No Role Address Type Address Value
0538 Home Home Address 123 Main Street, London, Rogers Street, United Kingdom
0538 Office Office Address Adam Land, Valley Forge PA 19482, U.S.A
0738 School School Address Rogers Street, London, Holland Lane, United Kingdom

CodePudding user response:

Use array_agg() function for combining column value with comma. Here ORDER BY clause isn't used because address line 4 wouldn't come before address line 1. Extra ORDER BY clause can degrade query performance for a large data sets

-- PostgreSQL
SELECT roll_no, role
     , role || ' Address' address_type
     , array_to_string(array_agg(address_value), ', ') address_value  
FROM test
GROUP BY roll_no, role
ORDER BY roll_no, role;

Please check this url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=e46cf351452a2715258b69afeea5c742

If ORDER BY must needed inside array_agg() function then use the below query

-- after applying order by inside array_agg()
SELECT roll_no, role
     , role || ' Address' address_type
     , array_to_string(array_agg(address_value order by address_type), ', ') address_value  
FROM test
GROUP BY roll_no, role
ORDER BY roll_no, role;

Please check the url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=c196a9a2ca71bd886e4750b935d23040

If same address stored for multiple address line of a particular role for specific roll_no then DISTINCT keyword will use inside array_agg().

-- after applying distinct inside array_agg()
SELECT roll_no, role
     , role || ' Address' address_type
     , array_to_string(array_agg(DISTINCT address_value), ', ') address_value  
FROM test
GROUP BY roll_no, role
ORDER BY roll_no, role;

Please check this url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=e4f0de7c37e003133e2539149db245f8

CodePudding user response:

You may use ARRAY_AGG() inside ARRAY_TO_STRING() but with caution. Because generally, SQL tables are unordered sets. Hence explicitly mentioning ORDER BY inside array_agg() is very very important.

Code:

SELECT 
    Roll_No, 
    role,
    role || ' address' AS address_type, 
    array_to_string(array_agg(addressvalue ORDER BY roll_no, role, addresstype), ', ') as address_value
FROM t
GROUP BY Roll_No, role
ORDER BY roll_no, role, address_value

Look at the db<>fiddle. Take a look how results vary with and without ORDER BY inside array_agg().

  •  Tags:  
  • Related