5

In PostgreSQL exists some ways to make a statement using bulk collect into like in Oracle?

Example in Oracle:

create or replace procedure prc_tst_bulk_test is

type typ_person is table of tb_person%rowtype;
v_tb_person typ_person;

begin

select *
bulk collect into v_tb_person
from tb_person;

-- make a selection in v_tb_person, for instance    
select name, count(*) from v_tb_person where age > 50
union 
select name, count(*) from v_tb_person where gender = 1

end;
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Leandro
  • 195
  • 2
  • 2
  • 8

4 Answers4

9

In PostgreSQL 10 you can use array_agg:

declare
    v_ids int[];   
begin
    select array_agg(id) INTO v_ids
      from mytable1
     where host = p_host;

    --use v_ids...

end;

You'll have array and it can be used to make select from it using unnest:

select * from unnest(v_ids) where ...
Topper Harley
  • 266
  • 5
  • 8
  • For the record: [`array_agg()` has been in Postgres since version 8.4](https://www.postgresql.org/docs/8.4/functions-aggregate.html). And before that, you could use an [ARRAY constructor](https://www.postgresql.org/docs/current/sql-expressions.html#SQL-SYNTAX-ARRAY-CONSTRUCTORS) - like you still can today. See: https://dba.stackexchange.com/a/159717/3684 – Erwin Brandstetter Dec 11 '22 at 02:09
5

There is no such syntax in PostgreSQL, nor a close functional equivalent.

You can create a temporary table in your PL/PgSQL code and use that for the desired purpose. Temp tables in PL/PgSQL are a little bit annoying because the names are global within the session, but they work correctly in PostgreSQL 8.4 and up.

A better alternative for when you're doing all the work within a single SQL statement is to use a common table expression (CTE, or WITH query). This won't be suitable for all situations.

The example above would be much better solved by a simple RETURN QUERY in PL/PgSQL, but I presume your real examples are more complex.

Assuming that tb_person is some kind of expensive-to-generate view that you don't just want to scan in each branch of the union, you could do something like:

CREATE OR REPLACE FUNCTION prc_tst_bulk()
RETURNS TABLE (name text, rowcount integer) AS 
$$
BEGIN
    RETURN QUERY
    WITH v_tb_person AS (SELECT * FROM tb_person)
    select name, count(*) from v_tb_person where age > 50
    union 
    select name, count(*) from v_tb_person where gender = 1;
END;
$$ LANGUAGE plpgsql;

This particular case can be further simplified into a plain SQL function:

CREATE OR REPLACE FUNCTION prc_tst_bulk()
RETURNS TABLE (name text, rowcount integer) AS 
$$
    WITH v_tb_person AS (SELECT * FROM tb_person)
    select name, count(*) from v_tb_person where age > 50
    union 
    select name, count(*) from v_tb_person where gender = 1;
$$ LANGUAGE sql;
Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • Craig Ringer, thanks for your reply. Thats is all need. Is it possible to change (select * from tb_person) for a dinamic statement like (execute sql_command). I tried it but with no success. – Leandro Apr 30 '13 at 02:35
  • @Leandro Yes, in PL/PgSQL only. Use `RETURN QUERY EXECUTE`. If you continue to have issues post a new *detailed* question with the full text of your code the exact error messages, etc and link back to this one for context. – Craig Ringer Apr 30 '13 at 02:55
2

You can use a PostgreSQL arrays too - it is similar to Oracle's collections:

postgres=# create table _foo(a int, b int);
CREATE TABLE
postgres=# insert into _foo values(10,20);
INSERT 0 1

postgres=# create or replace function multiply()
returns setof _foo as $$
/*
 * two tricks are here
 * table name can be used as type name
 * table name can be used as fictive column that packs all fields
 */
declare a _foo[] = (select array(select _foo from _foo));
begin
  return query select * from unnest(a) 
           union
           all select * from unnest(a);
end;
$$ language plpgsql;

CREATE FUNCTION
postgres=# select * from multiply();
 a  | b  
----+----
 10 | 20
 10 | 20
(2 rows)

But in your case Craig Ringer's proposal is perfect and should be preferable.

Pavel Stehule
  • 42,331
  • 5
  • 91
  • 94
0
-- Fetch the next 5 rows in the cursor_01:
FETCH FORWARD 5 FROM cursor_01;

PostgreSQL 10+ works.

https://www.postgresql.org/docs/10/sql-fetch.html

Charlie 木匠
  • 2,234
  • 19
  • 19