plpgsql - 在declare语句中使用动态表名

问题描述 投票:2回答:2

我正在尝试编写plpgsql以下形式的函数(注意这是一个简化版本):

CREATE FUNCTION check_valid(tablename regclass) RETURNS boolean AS $$

DECLARE valid_row tablename%ROWTYPE;

BEGIN

EXECUTE format('SELECT * FROM %s', tablename) into valid_row;

IF valid_row IS NULL THEN
      RETURN QUERY SELECT false;

ELSIF valid_row.is_valid = false;
      RETURN QUERY SELECT false;

ELSIF valid_row.hit_count > valid_row.hit_limit;
      RETURN QUERY SELECT false;

ELSE
      RETURN QUERY SELECT true;

END IF;

END

$$ LANGUAGE plpgsql;

失败的部分是DECLARE线。如何根据变量表名声明类型?或许我需要以某种方式施展它?

DECLARE mytable%ROWTYPE;这样的东西工作正常,但如果我使用像tablename%ROWTYPE这样的变量名:

ERROR: relation "tablename" does not exist

sql postgresql plpgsql
2个回答
6
投票

理解这五种不同类型的数据/符号的主要特性非常重要:

1. 'my_tbl'

unknown类型的字符串文字。当在SQL中使用(嵌入在plpgsql代码中)时,它被强制转换为从上下文派生的类型。如果无法确定类型,则可能需要显式强制转换。喜欢:'my_tbl'::text

2. 'my_tbl'::text

相同的字符串文字强制类型为text。它可以保存表的名称,但它实际上只是文本。

3. 'my_tbl'::regclass

注册班级的object identifier (OID)。它显示并可以作为表示有效对象名称('my_tbl')的字符串输入。如果输出是模糊的或非法的,则输出将自动进行模式限定('my_schema.my_tbl')和/或双引号('"mY_TbL"')。它可以是常规表,序列,视图,物化视图,复合类型等。此相关答案中的详细信息:

4. my_tbl_var my_tbl (short for my_tbl_var my_tbl%ROWTYPE)

在plpgsql代码块的DECLARE部分,它是一个具有众所周知的row type(a.k.a。复合类型)的变量声明。该类型必须在系统表pg_class中注册(与regclass变量相同)。它不是引用对象的OID,而是它的实际行类型。 my_tbl_varmy_tbl都是这里的标识符,不能参数化。您也可以直接投射任何行或记录:(123, 'foo')::my_tbl

5. my_tbl_var record

在plpgsql代码块的DECLARE部分中,这是匿名record的声明。基本上,一个占位符表示尚未知的行类型/尚未定义的结构。它可以在大多数地方使用,可以使用行类型。但是,在分配记录变量之前,您无法从中访问字段。

你很困惑1.,3。和4.并用5来解决它。 但这里出现了更多错误:

  • 您正在选择整个表,但行(记录)变量一次只能容纳一行。所以只分配和返回第一个。虽然没有ORDER BY子句,但结果是任意的,可以随时更改。邪恶的陷阱。
  • 由于您现在使用的是record类型,因此您需要确保在对其字段运行测试之前已分配它,或者您将获得空表的例外。在你的情况下,检查record_var IS NULL几乎做同样的工作。但是对于所有字段中都为NULL的行存在一个极端情况:然后record_var IS NULL计算结果为true。甚至比测试IS NOT NULL更棘手。详细信息: IS NOT NULL test for a record does not return TRUE when variable is set 我在下面的SQL fiddle中添加了一个演示。
  • 该函数返回单个标量(boolean)值。使用: RETURN false; 代替: RETURN QUERY SELECT false;

功能

CREATE FUNCTION check_valid(_tbl regclass)
  RETURNS bool AS
$func$
DECLARE
   r record;
   _row_ct int;
BEGIN
   EXECUTE '
   SELECT is_valid, hit_count, hit_limit
   FROM  ' || _tbl || '
   ORDER  <whatever>
   LIMIT  1'            -- replace <whatever> with your sort criteria
   INTO r;              -- only needed columns

   GET DIAGNOSTICS _row_ct = ROW_COUNT;

   IF _row_ct = 0 THEN  -- necessary, because r may not be assigned
      RETURN false;
   ELSIF NOT r.is_valid OR r.hit_count > r.hit_limit THEN
      RETURN false;
   END IF;

   RETURN true;
END
$func$  LANGUAGE plpgsql;

SQL Fiddle(函数的两个变体和行IS NULL的演示)。

Major points

  • 使用GET DIAGNOSTICS查找是否在使用EXECUTE的动态语句中找到了任何行。
  • IF表达式可以简化。
  • 该参数的类型为regclass,而不仅仅是表名。我不会对此参数使用误导性名称“tablename”。这只会增加你最初的困惑。称之为_tbl而不是。

如果您还想返回一组变量行类型:


0
投票

正如a_horse_with_no_name所指出的 - 使用:

DECLARE valid_row RECORD;

工作:)

© www.soinside.com 2019 - 2024. All rights reserved.