【问题标题】:How to get primary key's column values in SQL query (PostgreSQL)如何在 SQL 查询(PostgreSQL)中获取主键列值
【发布时间】:2021-11-28 17:18:15
【问题描述】:

我正在创建一个函数来获取我的表的主键列的所有值。该函数接收我要工作的表的名称。问题是表有不同的主键名称,所以我不能这样做

SELECT car_id FROM table   /* receiving "car" as the table*/

因为如果我收到“船”表,它会改变。

所以,我想做的是类似

SELECT primary_key FROM received table

查询存储在一个不同的字符中,我将在其中插入接收到的表名,然后执行它

【问题讨论】:

  • 它不是 Postgres 但也许这会有所帮助 stackoverflow.com/questions/95967/…
  • 这个设计模式有一些与之相关的警钟,您将不得不通过查询系统表的适当键列名称来动态构建要执行的 sql 字符串,可能 是函数中的一个问题。如果是复合键,您想做什么?
  • 只是一个注释。我的表的大多数主键都称为id,它们的类型是BIGINT。有时例外是关系表,但事实证明这种情况不再常见。几乎没有理由在表 car 的所有列之前添加 car_ 之类的内容。
  • 如果有多个主键列,输出是什么?或者您的桌子从来都不是这种情况?

标签: sql postgresql


【解决方案1】:

正如@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)

对于任何有或没有复合主键的表都是如此。

这个解决方案可能被认为是相当复杂的。任何简化或使其更智能的想法将不胜感激。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-25
    相关资源
    最近更新 更多