嵌入式 SQL

您可以在存储过程中嵌入和执行 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;
$$;

EXECUTE

EXECUTE 允许您在执行期间动态构造 SQL 查询:

EXECUTE command_expression [ USING expression [, ... ] ];

command_expression 是一个 SQL 表达式,它可以将 PL/vSQL 变量和求值引向字符串字面量。该字符串字面量作为 SQL 语句执行,$1、$2、... 替换为对应的表达式

使用 PL/vSQL 变量构造查询可能有危险,会将您的系统暴露给 SQL 注入,因此请使用 QUOTE_IDENTQUOTE_LITERALQUOTE_NULLABLE 对其进行包装。

以下过程使用 WHERE 子句来构造查询:

DO $$
BEGIN
    EXECUTE 'SELECT * FROM t1 WHERE x = $1' USING 10; -- becomes WHERE x = 10
END;
$$;

以下过程使用 usernamepassword 实参中的密码创建用户。由于构造的 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