带有 IN 条件的存储过程语法

Posted

技术标签:

【中文标题】带有 IN 条件的存储过程语法【英文标题】:Stored procedure syntax with IN condition 【发布时间】:2015-08-01 20:19:33 【问题描述】:

(1)

=>CREATE TABLE T1(id BIGSERIAL PRIMARY KEY, name TEXT);
CREATE TABLE

(2)

=>INSERT INTO T1
(name) VALUES
('Robert'),
('Simone');
INSERT 0 2

(3)

SELECT * FROM T1;
 id |  name  
----+--------
  1 | Robert
  2 | Simone
(2 rows)

(4)

CREATE OR REPLACE FUNCTION test_me(id_list BIGINT[]) 
RETURNS BOOLEAN AS
$$
BEGIN
  PERFORM * FROM T1 WHERE id IN ($1);
  IF FOUND THEN
    RETURN TRUE;
  ELSE
    RETURN FALSE;
  END IF;
END;
$$
  LANGUAGE 'plpgsql';
CREATE FUNCTION

我的问题是调用程序时。我无法在网上找到一个示例,显示如何传递 BIGINT 类型(或整数,无论​​如何)的值列表。

我尝试了以下内容但没有成功(语法错误):

第一种语法:

eway=> SELECT * FROM test_me('1,2'::BIGINT[]);
ERROR:  operator does not exist: bigint = bigint[]
LINE 1: SELECT * FROM T1 WHERE id IN ($1)
                                  ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
QUERY:  SELECT * FROM T1 WHERE id IN ($1)
CONTEXT:  PL/pgSQL function test_me(bigint[]) line 3 at PERFORM

第二种语法:

eway=> SELECT * FROM test_me('1,2');
ERROR:  operator does not exist: bigint = bigint[]
LINE 1: SELECT * FROM T1 WHERE id IN ($1)
                                  ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
QUERY:  SELECT * FROM T1 WHERE id IN ($1)
CONTEXT:  PL/pgSQL function test_me(bigint[]) line 3 at PERFORM

第三种语法:

eway=> SELECT * FROM test_me(ARRAY [1,2]);
ERROR:  operator does not exist: bigint = bigint[]
LINE 1: SELECT * FROM T1 WHERE id IN ($1)
                                  ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
QUERY:  SELECT * FROM T1 WHERE id IN ($1)
CONTEXT:  PL/pgSQL function test_me(bigint[]) line 3 at PERFORM

关于有效语法的任何线索?

就像解析器试图在 PEFORM REQUEST 中将 BIGINT 转换为 BIGINT[] 但对我来说没有任何意义......

【问题讨论】:

【参考方案1】:

检查项目是否在数组中的最简单方法是使用= ANY

CREATE OR REPLACE FUNCTION test_me(id_list BIGINT[]) 
RETURNS BOOLEAN AS
$$
BEGIN
  PERFORM * FROM T1 WHERE id = ANY ($1);
  IF FOUND THEN
    RETURN TRUE;
  ELSE
    RETURN FALSE;
  END IF;
END;
$$
LANGUAGE 'plpgsql';

【讨论】:

【参考方案2】:

您传递数组的所有语法变体都是正确的

Pass array literal to PostgreSQL function

问题在于函数内部的表达式。您可以使用ANY construct 像@Mureinik provided 或许多其他语法变体进行测试。在任何情况下都使用EXISTS 表达式运行测试:

CREATE OR REPLACE FUNCTION test_me(id_list bigint[]) 
  RETURNS bool AS
$func$
BEGIN
   IF EXISTS (SELECT 1 FROM t1 WHERE id = ANY ($1)) THEN
      RETURN true;
   ELSE
      RETURN false;
   END IF;
END
$func$ LANGUAGE plpgsql STABLE;

注意事项

EXISTS 最短且效率最高:

PL/pgSQL checking if a row exists - SELECT INTO boolean

应用于数组的ANY 构造仅对小型数组有效。对于更长的数组,其他语法变体更快。喜欢:

IF EXISTS (SELECT 1 FROM unnest($1) id JOIN t1 USING (id)) THEN ...
How to do WHERE x IN (val1, val2,…) in plpgsql

不要引用语言名称,它是标识符,而不是字符串:LANGUAGE plpgsql

简单变体

当您返回 boolean 值时,它可以更简单。这可能只是为了演示,但作为概念证明:

CREATE OR REPLACE FUNCTION test_me(id_list bigint[]) 
  RETURNS bool AS
$func$
SELECT EXISTS (SELECT 1 FROM t1 WHERE id = ANY ($1))
$func$ LANGUAGE sql STABLE;

同样的结果。

【讨论】:

尽管您的解决方案非常好,但我想知道我是否无法将标量表达式列表传递给存储过程,从而直接将它们与“IN”选项一起使用。在这里(postgresql.org/docs/9.1/static/extend-type-system.html#AEN49791)他们似乎说标量以某种方式转换为一种数组“对于每种标量类型,都会自动创建相应的数组类型”。所以我想有一种方法可以从标量传递到 ARRAY,反之亦然。 或者甚至以某种方式直接将标量列表传递给过程。 @Nitseg:您可以使用VARIADIC 参数(作为最后一个参数)将数组作为标量值的动态列表传递。这有时对调用很方便,但它仍然是函数内部的一个普通数组:***.com/questions/19202832/…. @Nitseg:您还可以传递任意数量的标量参数,并通过单独列出它们在IN 表达式中使用它们,但这对于动态数量的元素来说非常笨拙(或不可能)。如果您知道最大值,则只有一个选项。编写函数时的元素数。此外,IN (row of values) 对于长列表的表现同样糟糕,并且在处理 null 时很棘手,因此真的没有充分的理由强制它。 好的,非常感谢您回答我的附属问题。标量将与静态数量的表达式一起使用,这是有道理的。

以上是关于带有 IN 条件的存储过程语法的主要内容,如果未能解决你的问题,请参考以下文章

存储过程语法

oracle存储过程创建语法及常见异常

MySQL - 存储过程语法问题

MySQL-进阶18 存储过程- 创建语句-参数模式(in/out/inout-对应三个例子) -调用语法-delimiter 结束标记'$'

游标选择的 MySQL 存储过程语法错误

MySQL:存储过程语法错误