如何在 plpgsql 中使用变量作为 table 名称

How to use variable as table name in plpgsql

我是 plpgsql 的新手。我正在尝试 运行 plpgsql 中的一个简单查询,使用变量作为 plpgsql 中的 table 名称。但是变量被解释为 table 名称,而不是变量的值被解释为变量名称。

DECLARE
  v_table text;
  z_table text;
  max_id bigint;

BEGIN

FOR v_table IN
    SELECT table_name  
    FROM information_schema.tables 
    WHERE table_catalog = 'my_database' 
    AND table_schema = 'public'
    AND table_name not like 'z_%'
LOOP
    z_table := 'z_' || v_table;
    SELECT max(id) from z_table INTO max_id;
    DELETE FROM v_table where id > max_id;
END LOOP;

一些背景信息。对于我数据库中的每个 table,我都有另一个以 "z_" 开头的 table。例如。对于名为 "employee" 的 table 我有相同的 table 名为 "z_employee"。 z_employee 包含与员工相同的数据集。我用它在每次测试开始时恢复员工 table。

当我运行这个函数时,我得到以下错误:

ERROR:  relation "z_table" does not exist
LINE 1: SELECT max(id) from z_table

我的猜测是不允许我在 SQL 查询中使用变量 z_table。至少不是我在这里使用它的方式。但是我不知道应该怎么做。

正确使用dynamic SQL with EXECUTE、简化和转义标识符:

CREATE OR REPLACE FUNCTION f_test()
  RETURNS void AS
$func$
DECLARE
   v_table text;
BEGIN
   FOR v_table IN
      SELECT table_name  
      FROM   information_schema.tables 
      WHERE  table_catalog = 'my_database' 
      AND    table_schema = 'public'
      AND    table_name NOT LIKE 'z_%'
   LOOP
      EXECUTE format('DELETE FROM %I v WHERE v.id > (SELECT max(id) FROM %I)'
                    , v_table, 'z_' || v_table);
   END LOOP;
END
$func$  LANGUAGE plpgsql;

Table 名称可能需要被引用以防止语法错误甚至 SQL 注入!我使用方便的 format() 连接 DELETE 语句并正确转义标识符。

  • 一个单独的 SELECT 会更贵。您可以使用一个 DELETE 语句完成所有操作。

相关:

  • Table name as a PostgreSQL function parameter

旁白:

您可以改用(稍微快一些的)系统目录 pg_tables

      SELECT tablename
      FROM   pg_catalog.pg_tables
      WHERE  schemaname = 'public'
      AND    tablename NOT LIKE 'z_%'

参见:

  • How to check if a table exists in a given schema

table_catalog in information_schema.tables 在这里没有等价物。无论如何,只有 current 数据库的表是可见的。所以当连接到错误的数据库时,上面的谓词 WHERE table_catalog = 'my_database' 会产生一个空的结果集。