Search code examples
sqlpostgresqlstring-aggregation

How to remove empty Bs from PostgreSQL query only if other Bs have a value (for each A)?


I have a table with scientific and common (vernacular) names for plants.

               gensp       vernacular
Allamanda blanchetii                 
Allamanda blanchetii  Alamanda morada
Allamanda blanchetii Purple allamanda
  Allamanda puberula                 
  Aspidosperma cuspa                 
  Aspidosperma cuspa    Guatambuzinho

I need this to become:

               gensp                        vernacular
Allamanda blanchetii Alamanda morada, Purple allamanda
  Allamanda puberula                                  
  Aspidosperma cuspa                     Guatambuzinho

I have tried the following SQL (on Postgres).

select gensp,string_agg(vernacular,', ') as vernacular from (
    select distinct gen||' '||sp as gensp,spm.vernacular from sp
    join col on idsp = sp.id
    join spm on idcol = col.id
    where fam = 'Apocynaceae' and gen||' '||sp != ''
    order by gensp,vernacular
) sub
group by gensp

But it returns:

               gensp                          vernacular
Allamanda blanchetii , Alamanda morada, Purple allamanda
  Allamanda puberula                                    
  Aspidosperma cuspa                     , Guatambuzinho

Notice how the empty vernacular names appear on the aggregate string. So I need to remove these lines, but only if some other line have a name (or else Allamanda puberula would be removed from the query).

How can I do that?


Solution

  • Use filter and coalesce to deal with empty or null vernacular values.

    select sp, 
           coalesce(
             string_agg(vernacular, ', ') 
               filter (where coalesce(vernacular, '') != ''),
           '')
      from plants
     group by sp;
    

    The simpler answer devised by OP, @Rodrigo:

    select sp,
           coalesce(string_agg(nullif(vernacular,''),', '))
      from plants
      group by sp;