问题
I have a bunch of table that have a "stat" column (stat for status ;-)
I would like the count of each stats, and see it!
My tables look like this
create table a (
a_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
a_stat status_t
);
create table b (
b_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
b_stat status_t
);
status_t
is an enum.
So I did this:
DO $$
DECLARE
tableName RECORD;
result RECORD;
BEGIN
SET SEARCH_PATH = projet, public;
FOR tableName IN SELECT
c.relname,
a.attname
FROM pg_class AS c
INNER JOIN pg_attribute AS a ON a.attrelid = c.oid
WHERE a.attname LIKE '%stat' AND c.relkind = 'r' LOOP
EXECUTE format('SELECT %I, count(%I) FROM %I GROUP BY %I',
tableName.attname, tableName.attname, tableName.relname, tableName.attname) INTO result;
SELECT * FROM result;
END LOOP;
END;
$$;
There are things that I think I'm not doing well here.
- There is maybe a better form of format
- I can't select a RECORD, I think the datatype is not good (but can't figure out what type I should use)
- A select inside a for loop is not a good idea (I think ?) But I didn't found How to put
result
into a array of result, and display it after the for loop.
How to do this properly?
回答1:
You cannot return from a DO
command. You could raise notices or write to a temporary table to work around this. But rather use a proper function instead. Like this:
CREATE OR REPLACE FUNCTION foo()
RETURNS TABLE (sch_name text, tbl_name text, col_name text, row_count_notnull int8) AS
$func$
DECLARE
_sch text;
_tbl text;
_col text;
BEGIN
FOR _sch, _tbl, _col IN
SELECT c.relnamespace::regnamespace, c.relname, a.attname
FROM pg_class c
JOIN pg_attribute a ON a.attrelid = c.oid
WHERE c.relnamespace = ANY ('{projet, public}'::regnamespace[]) -- project?
AND c.relkind = 'r' -- only regular tables
AND a.attname LIKE '%\_stat' -- a_stat, b_stat
AND a.attnum > 0 -- only user columns (redundant here)
AND NOT a.attisdropped -- exclude dropped columns
LOOP
RETURN QUERY EXECUTE format(
'SELECT $1, $2, $3, count(%I) FROM %I.%I GROUP BY 1'
, _col, _sch, _tbl)
USING _sch, _tbl, _col;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM foo();
Major points:
You cannot not
SELECT
without target (), neither in aSELECT * FROM result;
DO
command nor in a plpgsql function. (You could in a plain SQL function, but you have no loop there.) I return results withRETURN QUERY EXECUTE
.Pass values to
EXECUTE
with theUSING
clause.Schema-qualify tables in the dynamic query. Else, you might query the wrong table by accident.
Does not include the temporary schema like your attempt (even if you were not aware of it). You could add it using
pg_my_temp_schema()
if you want, but you probably did not want it anyway:- How does the search_path influence identifier resolution and the "current schema"
- Temporary schema per connection?
Related:
- Table name as a PostgreSQL function parameter
Search for more examples with dynamic SQL in plpgsql here on SO.
来源:https://stackoverflow.com/questions/46282882/handle-result-when-dynamic-sql-is-in-a-loop