正如@Stu 所说,您可以基于一个字符串创建一个动态查询,该字符串表示表的主键列列表,其名称被传递给一个函数,该函数返回这些主键列的值集:
CREATE OR REPLACE FUNCTION keycolumns_values (IN table_name text)
RETURNS setof record LANGUAGE plpgsql AS
$$
DECLARE
primarykey_columns text ;
BEGIN
-- build the list of primary key columns
SELECT string_agg(a.attname, ',' ORDER BY a.attname)
INTO primarykey_columns
FROM pg_index i
JOIN pg_attribute a
ON a.attrelid = i.indrelid
AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = quote_ident(table_name) ::regclass
AND i.indisprimary;
-- dynamic query to return the values of the primary key columns
RETURN QUERY EXECUTE
'SELECT ' || primarykey_columns ' FROM ' || quote_ident(table_name);
END ;
$$ ;
问题是当调用这个函数时:
SELECT * FROM keycolumns_values (your_table_name)
你得到错误:
错误:返回的函数需要列定义列表
“记录”
为了定义keycolumns_values函数返回的列列表,我建议创建一组复合类型,每个表一个,其内容对应主键,名称为以 'pk_' 开头的表名(仅调用此过程一次):
CREATE OR REPLACE PROCEDURE create_pk_types ()
LANGUAGE plpgsql AS
$$
DECLARE
rec record ;
BEGIN
FOR rec IN
SELECT t.table_name, string_agg(a.attname || ' ' || format_type(a.atttypid, a.atttypmod), ',' ORDER BY a.attname) AS pk_list
FROM information_schema.tables AS t
JOIN pg_index i
ON i.indrelid = t.table_name::regclass
JOIN pg_attribute a
ON a.attrelid = i.indrelid
AND a.attnum = ANY(i.indkey)
WHERE t.table_schema='public'
AND t.table_type='BASE TABLE'
AND i.indisprimary
GROUP BY t.table_name
LOOP
EXECUTE format('DROP TYPE IF EXISTS pk_%s', rec.table_name) ;
EXECUTE format('CREATE TYPE pk_%s AS (%s)', rec.table_name, rec.pk_list) ;
END LOOP ;
END ;
$$ ;
CALL create_pk_types () ;
然后更新keycolumns_values函数的动态查询,以便整合正确的复合类型:
RETURN QUERY EXECUTE
'SELECT row(' || primarykey_columns || ') :: pk_' || table_name || ' FROM ' || quote_ident(table_name);
最后,以下查询应该为表 car 提供预期结果
SELECT (x.y).* FROM keycolumns_values('car') AS x(y pk_car)
对于任何有或没有复合主键的表都是如此。
这个解决方案可能被认为是相当复杂的。任何简化或使其更智能的想法将不胜感激。