0

I have developed a function to UNION ALL tables from a list of table names (a table called tablelist below) inspired by this SO post.

The initial function just returns a selection, but now I'd like to write a new table with a name taken from a parameter new_table_name.

I'm struggling with the syntax to insert the parameter into the DROP TABLE AND CREATE TABLE statements. Here's one of the attempts which returns ERROR: mismatched parentheses at or near ";"

DROP FUNCTION IF EXISTS f_multi_union(text);

CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
  RETURNS Table (my_id int, metric double precision, geom geometry)
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY EXECUTE
   (
   DROP TABLE IF EXISTS working.'' || new_tab_name || '';
   CREATE TABLE working.'' || new_tab_name || '' AS (
   SELECT string_agg(format('SELECT * FROM %s', tbl), ' UNION ALL ')
   FROM (SELECT tbl FROM working.tablelist) sub 
    )
   );
END
$func$;
4
  • RETURN QUERY EXECUTE can execute just one single query, not multiple. And you have a DROP and a CREATE statement, that makes two. Place the DROP statement before the RETURN part and you're fine. Commented Jun 29, 2022 at 9:10
  • @FrankHeikens I moved DROP TABLE IF EXISTS working.'' || new_tab_name || ''; between BEGIN and RETURN QUERY EXECUTE. Now gives ERROR: syntax error at or near "''" LINE 8: DROP TABLE IF EXISTS working.'' || new_tab_name || ''; Something wrong with syntax for calling parameter. Commented Jun 29, 2022 at 9:20
  • 1
    Use something like this: EXECUTE format('DROP TABLE IF EXISTS working.%I', new_tab_name); -- avoid SQL injection However, I don't think this functions will do you want it to do, it creates a table with a single record holding a SQL statement. Is that what you want? Commented Jun 29, 2022 at 9:41
  • @FrankHeikens using format passes the DROP statement but falls over @ ERROR: syntax error at or near "CREATE" LINE 11: CREATE TABLE working.'' || new_tab_name || '' AS ( My aim is to create a table that holds the contents of multiple UNION ALLs from a list of table names tablelist. Commented Jun 29, 2022 at 10:25

1 Answer 1

1

Something like this?

DROP FUNCTION IF EXISTS f_multi_union(text);

CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
  RETURNS void -- nothing to return
  LANGUAGE plpgsql AS
$func$
DECLARE
    _sql            TEXT;
BEGIN
    _sql    := format('DROP TABLE IF EXISTS working.%I;', new_tab_name); -- avoid SQL injection
    EXECUTE _sql;

    _sql    :=  'SELECT string_agg(format(''SELECT * FROM %I'', tbl), '' UNION ALL '')
                FROM (SELECT tbl FROM working.tablelist) sub;';
    EXECUTE _sql 
        INTO _sql; -- overwrite current _sql content

    _sql    := format('CREATE TABLE working.%I AS %s;', new_tab_name, _sql);
    EXECUTE _sql;
   
END
$func$;

I would replace the * in the SELECT statement with the columns that you need.

Sign up to request clarification or add additional context in comments.

4 Comments

That worked but I had to swap format(''SELECT * FROM %I'', tbl) to format(''SELECT * FROM %s'', tbl) because the relation wasn't recognised with double quotes wrapping.
@mark: That means the content in your table was wrong. Using %s could cause new problems. When the content matches the table name, %I is the only thing you need to be save.
what do you mean by content of the table?
In your table working.tablelist you have a column tbl. The content in there must match the exact name of the tables you want to use in your SELECT statement. If you have a table "foo" (lower case), you should store it as "foo" and not "Foo" with an upper case. Also no additional spaces or other stuff, it has to match 100%.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.