plpgsql中的语法错误

Ahm*_*mad 1 sql postgresql exists plpgsql postgresql-8.4

我在PostgreSQL中有一个存储过程

CREATE OR REPLACE FUNCTION show_senti_lang_setting(IN _senti_id bigint)
RETURNS TABLE(lang_code character, native_name character varying, is_active boolean) AS
$BODY$
BEGIN
RETURN QUERY 
    SELECT
        l.lang_code,
        l.native_name,
        (CASE WHEN s.senti_id is NULL THEN FALSE
            ELSE TRUE
        END) is_active
    FROM
        language l
    LEFT JOIN senti_lang s
    ON s.lang_code=l.lang_code
    AND s.senti_id=_senti_id;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT;
Run Code Online (Sandbox Code Playgroud)

错误是:

ERROR:  syntax error at or near "$1"
LINE 1: ...HEN s.senti_id is NULL THEN FALSE ELSE TRUE END)  $1  FROM l...
                                                         ^
QUERY:   SELECT l.lang_code, l.native_name, (CASE WHEN s.senti_id is NULL THEN FALSE ELSE TRUE END)  $1  FROM language l LEFT JOIN senti_lang s ON s.lang_code=l.lang_code AND s.senti_id= $2 
CONTEXT:  SQL statement in PL/PgSQL function "show_senti_lang_setting" near line 13

********** Error **********

ERROR: syntax error at or near "$1"
SQL state: 42601
Context: SQL statement in PL/PgSQL function "show_senti_lang_setting" near line 13
Run Code Online (Sandbox Code Playgroud)

似乎错误是由CASEplpgsql引起的.相同的函数在SQL中运行良好:

CREATE OR REPLACE FUNCTION show_senti_lang_setting(bigint)
    RETURNS TABLE(lang_code character, native_name character varying, is_active boolean) AS
$BODY$

    SELECT
        l.lang_code,
        l.native_name,
        CASE WHEN s.senti_id is NULL THEN FALSE
            ELSE TRUE
        END is_active
    FROM
        language l
    LEFT JOIN senti_lang s
    ON s.lang_code=l.lang_code
    AND s.senti_id=$1;

$BODY$
  LANGUAGE sql VOLATILE STRICT;
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 6

至于你有什么:

版本8.4的OUT参数与返回的行(is_active)中的列别名相同.这已经被修改了,它适用于PostgreSQL 9.1或更高版本(甚至可能是9.0).这就是你的语法错误的原因.

列别名只是这个星座中的噪音.它们被丢弃以支持声明的OUT参数.他们唯一的目的可能是文档,所以只需将其作为评论并避免冲突开始:

    CASE WHEN s.senti_id is NULL THEN FALSE ELSE TRUE END -- AS is_active
Run Code Online (Sandbox Code Playgroud)

也:

  • 您应该使用AS带有列别名的关键字(虽然通常可以跳过表别名).
  • CASE声明周围没有括号.

更好的形式

你拥有它的方式,总是返回表中的所有行language- 以及senti_lang找到匹配项的一个或多个实例.同时定义函数STRICT,因此在为NULL提供NULL值时根本没有行_senti_id.很难想象一个明智的用例.

如果找到多个匹配项,我不希望您想要为每种语言返回多行senti_lang.所以你可以简化为:

CREATE OR REPLACE FUNCTION show_senti_lang_setting(IN _senti_id bigint)
RETURNS TABLE(lang_code character, native_name varchar, is_active boolean) AS
$func$
BEGIN
RETURN QUERY 
   SELECT l.lang_code
         ,l.native_name
         ,EXISTS (SELECT 1 FROM senti_lang s
                  WHERE  s.lang_code = l.lang_code
                  AND    s.senti_id = _senti_id) -- AS is_active
   FROM   language l;
END
$func$ LANGUAGE plpgsql VOLATILE STRICT;
Run Code Online (Sandbox Code Playgroud)

我会提出一个问题是否需要STRICT.

的意思 Select 1

在下面的评论中回答后续问题.我引用手册EXISTS:

由于结果仅取决于是否返回任何行,而不取决于这些行的内容,因此子查询的输出列表通常不重要.常见的编码约定是以EXISTS(SELECT 1 WHERE ...)的形式编写所有EXISTS测试.

基本上你可以编写任何语法上有效的表达式.无论如何它都被丢弃了.
我们一直在讨论这个相关问题的可读性.