嵌入式 SQL
赋值
要保存表达式的值或返回的值,您可以将其赋值给变量:
variable_name := expression;
variable_name := statement;
例如,此过程将 3 赋值给 i
,将 'message' 赋值给 v
。
=> CREATE PROCEDURE performless_assignment() LANGUAGE PLvSQL AS $$
DECLARE
i int;
v varchar;
BEGIN
i := SELECT 3;
v := 'message';
END;
$$;
如果查询未返回任何行或者返回多个行,则此类赋值失败。要返回多个行,请使用 LIMIT 或截断赋值:
=> SELECT * FROM t1;
b
---
t
f
f
(3 rows)
=> CREATE PROCEDURE more_than_one_row() LANGUAGE PLvSQL as $$
DECLARE
x boolean;
BEGIN
x := SELECT * FROM t1;
END;
$$;
CREATE PROCEDURE
=> CALL more_than_one_row();
ERROR 10332: Query returned multiple rows where 1 was expected
截断赋值
截断赋值将查询返回的第一行存储在变量中。行排序是不确定的,除非您指定了 ORDER BY 子句:
variable_name <- expression;
variable_name <- statement;
以下过程获取指定的查询所返回结果的第一行,并将其赋值给 x
:
=> CREATE PROCEDURE truncating_assignment() LANGUAGE PLvSQL AS $$
DECLARE
x boolean;
BEGIN
x <- SELECT * FROM t1 ORDER BY b DESC; -- x is now assigned the first row returned by the SELECT query
END;
$$;
PERFORM
PERFORM 关键字运行 SQL 语句或 表达式,放弃返回的结果。
PERFORM statement;
PERFORM expression;
例如,此过程将一个值插入表。INSERT 返回插入的行数,因此您必须将其与 PERFORM 配对。
=> DO $$
BEGIN
PERFORM INSERT INTO coordinates VALUES(1,2,3);
END;
$$;
注意
如果 SQL 语句未返回任何行,或者您未将返回值赋值给变量,则必须使用 PERFORM。EXECUTE
EXECUTE 允许您在执行期间动态构造 SQL 查询:
EXECUTE command_expression [ USING expression [, ... ] ];
command_expression 是一个 SQL 表达式,它可以将 PL/vSQL 变量和求值引向字符串字面量。该字符串字面量作为 SQL 语句执行,$1、$2、... 替换为对应的表达式。
使用 PL/vSQL 变量构造查询可能有危险,会将您的系统暴露给 SQL 注入,因此请使用 QUOTE_IDENT、QUOTE_LITERAL 和 QUOTE_NULLABLE 对其进行包装。
以下过程使用 WHERE 子句来构造查询:
DO $$
BEGIN
EXECUTE 'SELECT * FROM t1 WHERE x = $1' USING 10; -- becomes WHERE x = 10
END;
$$;
以下过程使用 username
和 password
实参中的密码创建用户。由于构造的 CREATE USER 语句使用变量,请使用函数 QUOTE_IDENT 和 QUOTE_LITERAL,用 || 连接它们。
=> CREATE PROCEDURE create_user(username varchar, password varchar) LANGUAGE PLvSQL AS $$
BEGIN
EXECUTE 'CREATE USER ' || QUOTE_IDENT(username) || ' IDENTIFIED BY ' || QUOTE_LITERAL(password);
END;
$$;
EXECUTE 是 SQL 语句,因此您可以将其赋值给变量或与 PERFORM 配对:
variable_name:= EXECUTE command_expression;
PERFORM EXECUTE command_expression;
FOUND(特殊变量)
特殊布尔变量 FOUND 初始化为 false,然后根据是否发生以下操作确定是赋值为 true 还是 false:
-
语句(不是表达式)返回具有非零行数的结果,或者
-
FOR 循环至少迭代一次
您可以使用 FOUND 区分返回 NULL 和返回 0 行。
在过程实参的作用域与其定义的最外层块之间存在特殊变量。这意味着:
-
特殊变量影射过程实参
-
在存储过程主体中声明的变量将影射特殊变量
下面的过程演示 FOUND 如何更改。在 SELECT 语句之前,FOUND 为 false;在 SELECT 语句之后,FOUND 为 true。
=> DO $$
BEGIN
RAISE NOTICE 'Before SELECT, FOUND = %', FOUND;
PERFORM SELECT 1; -- SELECT returns 1
RAISE NOTICE 'After SELECT, FOUND = %', FOUND;
END;
$$;
NOTICE 2005: Before SELECT, FOUND = f
NOTICE 2005: After SELECT, FOUND = t
同样,UPDATE、DELETE 和 INSERT 返回受影响的行数。在下一示例中,UPDATE 不更改任何行,但是返回 0 值以表示没有任何行受影响,因此 FOUND 设置为 true:
=> SELECT * t1;
a | b
-----+-----
100 | abc
(1 row)
DO $$
BEGIN
PERFORM UPDATE t1 SET a=200 WHERE b='efg'; -- no rows affected since b doesn't contain 'efg'
RAISE INFO 'FOUND = %', FOUND;
END;
$$;
INFO 2005: FOUND = t
FOUND 开始时设置为 false,如果循环至少迭代一次,则设为 true:
=> DO $$
BEGIN
RAISE NOTICE 'FOUND = %', FOUND;
FOR i IN RANGE 1..1 LOOP -- RANGE is inclusive, so iterates once
RAISE NOTICE 'i = %', i;
END LOOP;
RAISE NOTICE 'FOUND = %', FOUND;
END;
$$;
NOTICE 2005: FOUND = f
NOTICE 2005: FOUND = t
DO $$
BEGIN
RAISE NOTICE 'FOUND = %', FOUND;
FOR i IN RANGE 1..0 LOOP
RAISE NOTICE 'i = %', i;
END LOOP;
RAISE NOTICE 'FOUND = %', FOUND;
END;
$$;
NOTICE 2005: FOUND = f
NOTICE 2005: FOUND = f