我正在尝试将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
答案 0 :(得分:6)
了解这五种不同类型的数据/符号的主要性质非常重要:
'my_tbl'
unknown
类型的字符串文字。当在SQL中使用(嵌入在plpgsql代码中)时,它被强制转换为从上下文派生的类型。如果无法确定类型,则可能需要显式强制转换。喜欢:'my_tbl'::text
。
'my_tbl'::text
转换为类型text
的相同字符串文字。它可以保存表的名称,但它实际上只是文本。
'my_tbl'::regclass
object identifier (OID)注册的类 。它显示并可以作为表示有效对象名称('my_tbl'
)的字符串输入。如果输出是模糊的或非法的,则输出将自动进行模式限定('my_schema.my_tbl'
)和/或双引号('"mY_TbL"'
)。它可以是常规表,序列,视图,物化视图,复合类型等。相关答案中的详细信息:
my_tbl_var my_tbl
(my_tbl_var my_tbl%ROWTYPE
的缩写)在plpgsql代码块的DECLARE
部分中,这是一个带有众所周知的row type (a.k.a。复合类型)的变量声明。该类型必须在系统表pg_class
中注册(与regclass
变量相同)。它不是引用对象的OID,而是它的实际行类型。此处my_tbl_var
和my_tbl
都是标识符,无法参数化。您还可以直接投射任何行或记录:(123, 'foo')::my_tbl
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
甚至更棘手。详情如下:
我在下面的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的演示)。
使用GET DIAGNOSTICS
查看是否在EXECUTE
的动态语句中找到了任何行。
IF
表达式可以简化。
参数的类型为regclass
,而不仅仅是表名。我不会对此参数使用误导性名称“tablename”。这只会增加你最初的困惑。改为称呼_tbl
。
如果您还想返回一组变量行类型:
答案 1 :(得分:0)
正如a_horse_with_no_name指出的那样 - 使用:
DECLARE valid_row RECORD;
有效:)