4

This is a question about COALESCE in PostgreSQL. In a current view I am using COALESCE to grab the first NOT NULL value in a list:

COALESCE(vw_header_to_node_13.subsetname,
vw_header_to_node_12.subsetname, 
vw_header_to_node_11.subsetname,
vw_header_to_node_10.subsetname,
vw_header_to_node_9.subsetname,
vw_header_to_node_8.subsetname,
vw_header_to_node_7.subsetname,
vw_header_to_node_6.subsetname,
vw_header_to_node_5.subsetname,
vw_header_to_node_4.subsetname,
vw_header_to_node_3.subsetname,
vw_header_to_node_2.subsetname,
vw_header_to_node_1.subsetname,
vw_header_to_node.subsetname,
vw_header_to_node.setname)
AS prctr1

I have just been informed that instead of grabbing just the first NOT NULL field, I now need to grab the first NOT NULL field and the following 2 fields after the first NOT NULL field. For instance, if vw_header_to_node_8.subsetname was the first NOT NULL field I would want to grab vw_header_to_node_8.subsetname, vw_header_to_node_7.subsetname, & vw_header_to_node_6.subsetname. I know this is not how COALESCE normally functions, but does anyone know of any variations or ways to make this happen?

klin
  • 112,967
  • 15
  • 204
  • 232
user3329160
  • 165
  • 2
  • 13
  • Is it necessary to do this in SQL? Could you simply SELECT all of those values and use application code to figure out which of the values to use? – Aaron May 20 '15 at 20:15
  • 1
    Can the second and third values be nulls, or nulls should be excluded at all? – klin May 20 '15 at 20:26
  • The way this list is the 2nd and 3rd values won't be nulls. – user3329160 May 21 '15 at 17:35

1 Answers1

4

If all nulls should be excluded from the list, this method should work:

  1. convert the list to array,
  2. remove nulls from the array,
  3. select three first elements of the array.

Example:

with test as (
    select 
        null::text     as v1,
        'apple'::text  as v2,
        null::text     as v3,
        'banana'::text as v4,
        'pear'::text   as v5)

select a[1] val1, a[2] val2, a[3] val3
from (      
    select 
        array_remove(
            array[v1, v2, v3, v4, v5], null) a
    from test
    ) alias

 val1  |  val2  | val3
-------+--------+------
 apple | banana | pear

If the second and third values can be null, we should trim first nulls from the array in step 2. There is no appropriate function in Postgres, but you can write it yourself.

create function array_ltrim_nulls(arr anyarray)
returns anyarray language plpgsql immutable
as $$
declare 
    i integer;
    l integer = array_length(arr, 1);
begin
    for i in 1..l loop
        if arr[i] is not null then
            return arr[i:l];
        end if;
    end loop;
    return null;
end $$;

with test as (
    select 
        null::text     as v1,
        'apple'::text  as v2,
        null::text     as v3,
        'banana'::text as v4,
        'pear'::text   as v5)

select a[1] val1, a[2] val2, a[3] val3
from (      
    select 
        array_ltrim_nulls(
            array[v1, v2, v3, v4, v5]) a
    from test
    ) alias

 val1  | val2 |  val3
-------+------+--------
 apple |      | banana      

For easier use I have added the following function to my standard set of functions. Perhaps this function will be most useful to you. The number of function arguments is variable. You only need to ensure that all arguments are of the same type.

create function array_coalesce (variadic arr anyarray)
returns anyarray language sql immutable
as $$
    select array_remove(arr, null);
$$;

select array_coalesce(null::text, 'apple', null, 'banana', 'pear') arr;
         arr         
---------------------
 {apple,banana,pear}

select array_coalesce(null::int, 1, null, 2, 3, 4, null, 5) arr;
     arr     
-------------
 {1,2,3,4,5}

select (array_coalesce(null::int, 1, null, 2, 3, 4, null, 5))[1:3] arr;
     arr     
-------------
 {1,2,3}
klin
  • 112,967
  • 15
  • 204
  • 232
  • Thanks for taking the time to answer this. This is currently done in a view in which I'm creating the hierarchy that I am running the COALESCE on by multiple joins to the same table. I am not sure if I can do an array on this. Wouldn't I have to create a table and input all of the values into the array? – user3329160 May 21 '15 at 18:02
  • You don't need any special tables. You can just use an array in your query and elements of this array can be from any joined tables or views, e.g. `select array[view1.field2, view2.field2, table1.field3]` – klin May 21 '15 at 18:13
  • Okay. I will give it a try. This is pretty advanced for me! Thanks for taking the time to help out. – user3329160 May 21 '15 at 18:17
  • do you mind to elaborate on the first block of code? – user3329160 May 21 '15 at 18:35