Search code examples
sqlarrayspostgresqlplpgsqlpostgresql-8.3

Iterating on each element from an array of arrays on Postgresql


I have a VARCHAR of numbers inside my stored procedure, these numbers are organized as arrays, I will show an example below:

{1,2,3,4,5,6,7,8,9},{1,2,3,4,5},{1,2,3},{9} -- This is a STRING

Now with a help from another guy from here I'm using this to get integer arrays integer[]

SELECT string_to_array(regexp_split_to_table(
      trim('{1,2,3,4,5,6,7,8,9},{1,2,3,4,5},{1,2,3},{9}', '{}')
      , '},{'), ',')::int[]

I will have a set of records, each of them with an array, see below:

{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5}
{1,2,3}
{9}

I was trying but I cannot figure out how can I make a FOR to iterate over each element from these arrays to call another procecure to do with each element from each array.

An example for my array {1,2,3,4,5,6,7,8,9} that I will call my_array:

rec record;
arr integer[];

FOR rec IN SELECT string_to_array(unnest(string_to_array(trim(text_nodes_for_connectivity, '{}'), '},{')), ',')::int[] LOOP
    arr := array_cat(arr, rec);
END LOOP;

I'm getting this error:

function array_cat(integer[], record) does not exist

I need to convert each of my record results to an array, so I can use array_cat or another functions to iterate over array elements

My proc code goes below:

DROP FUNCTION IF EXISTS clustering_nodes();
CREATE OR REPLACE FUNCTION clustering_nodes() RETURNS integer[] AS $$

DECLARE
my_array integer[];
rec record;
arr integer[];
my_var varchar[500];
len integer;

BEGIN

my_var = '{1,2,3,4,5,6,7,8,9},{1,2,3,4,5},{1,2,3},{8}';


FOR rec IN SELECT string_to_array(unnest(string_to_array(trim(my_var, '{}'), '},{')), ',')::int[] LOOP
    len = array_length(rec);
    arr := array_append(arr, len);
END LOOP;

RETURN arr;

END;

$$ LANGUAGE 'plpgsql' STRICT;

select clustering_nodes();

Tips or triks?


Solution

  • CREATE OR REPLACE FUNCTION clustering_nodes()
      RETURNS integer[] AS
    $func$
    DECLARE
       my_var   text  := '{1,2,3,4,5,6,7,8,9},{1,2,3,4,5},{1,2,3},{8}';
       my_array integer[];
       arr_len  integer[];
    BEGIN
    
    FOR my_array IN
       SELECT string_to_array(regexp_split_to_table(
                trim(my_var, '{}'), '},{'), ',')::int[]
    LOOP
       arr_len := array_append(arr_len, array_upper(my_array, 1));
    END LOOP;
    
    RETURN arr_len;
    
    END
    $func$ LANGUAGE plpgsql;
    

    Major points

    • array_length() doesn't work on records, only on array.
      What's more, there is no array_length() in Postgres 8.3. The manual is instrumental in figuring that out yourself. Using the less favorable array_upper() instead.

    • Assignment operator in plpgsql is :=. Use of = is undocumented.

    • plpgsql is an identifier in LANGUAGE plpgsql, not a string. Do not quote it. May lead to sneaky errors.

    • You can assign variables at declaration time.

    • STRICT modifier is pointless without parameters.

    -> SQLfiddle demo for Postgres 8.3.

    Simpler with modern Postgres

    Again, this could be had in a single (if somewhat complex) call:

    SELECT array_agg(array_length(string_to_array(txt, ','), 1))
    FROM   unnest(string_to_array(
              trim('{1,2,3,4,5,6,7,8,9},{1,2,3,4,5},{1,2,3},{8}', '{}')
              , '},{')
           ) AS sub(txt);
    

    You need to upgrade to a current version.