将多个值集或值数组传递给函数

时间:2016-01-04 21:54:14

标签: sql postgresql stored-procedures multidimensional-array plpgsql

我在PostgreSQL 9.3.10中编写PL / pgSQL函数,以返回参加下表中某些类/会话的人员:

Attendance
+-------+---------+---------+
| Class | Section |  Name   |
+-------+---------+---------+
|    1  |      1  | Amy     |
|    1  |      1  | Bill    |
|    1  |      2  | Charlie |
|    1  |      2  | Dan     |
|    2  |      1  | Emily   |
|    2  |      1  | Fred    |
|    2  |      2  | George  |
+-------+---------+---------+

我想要做的是,给定一组类/节ID对(int[][]),返回这些类/节中的所有人。例如my_func(ARRAY[[1,1],[2,2]])返回:

+-------+---------+---------+
| Class | Section |  Name   |
+-------+---------+---------+
|    1  |      1  | Amy     |
|    1  |      1  | Bill    |
|    2  |      2  | George  |
+-------+---------+---------+

如果我事先知道对,那就简单了:

SELECT * FROM attendance 
WHERE ((class = 1 AND section = 1) OR (class = 2 AND section = 2));

相反,这些对将是函数的参数。

现在,我能想到的唯一方法就是让函数通过在查询末尾附加一堆WHERE子句然后调用{{1}来构建一个SQL查询字符串。 }。有没有更好的方法来获得我的结果?

编辑: 我实施了@Erwin的建议,我现在能够得到我想要的结果。不幸的是,它看起来好像很慢。这是我正在运行的功能:

EXECUTE

像这样查询:

CREATE OR REPLACE FUNCTION public.get_attendance(int[])
  RETURNS  TABLE(
    class_c int,
    section_c int
  )
AS
$BODY$
  BEGIN
    RETURN QUERY
      SELECT class, section
      FROM generate_subscripts($1, 1) as i 
      INNER JOIN attendance ON attendance.class = $1[i][1]
                            AND  attendance.section = $1[i][2];

  END;
$BODY$
LANGUAGE plpgsql VOLATILE;

我得到以下EXPLAIN ANALYZE output

SELECT *  FROM get_attendance(ARRAY[[1,15],[2,15],[3,8]]);

问题是查询正在扫描考勤表中的所有考勤,而不会过滤它们直到加入。有什么方法可以解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

您可以使用简单的SQL函数实现此目的。主要功能是功能generate_subscripts()

CREATE OR REPLACE FUNCTION f_attendance(_arr2d int[])
  RETURNS SETOF attendance AS
$func$
   SELECT a.*
   FROM   generate_subscripts($1, 1) i
   JOIN   attendance a ON a.class   = $1[i][1]
                      AND a.section = $1[i][2]
$func$  LANGUAGE ROWS 10 sql STABLE;

呼叫:

SELECT * FROM f_attendance(ARRAY[[1,1],[2,2]]);

或者与数组 literal 相同 - 在某些情况下更方便,特别是对于预处理语句:

SELECT * FROM f_attendance('{{1,1},{2,2}}');

函数总是需要一个2D数组。即使您通过一对,也要嵌套它:

SELECT * FROM f_attendance('{{1,1}}');

审核您的实施

  1. 您创建了VOLATILE函数,但它可以是STABLEPer documentation:

      

    由于此快照行为,可以安全地将仅包含SELECT命令的函数标记为STABLE

    相关:

  2. 您还使用LANGUAGE plpgsql而不是sql,如果您在同一会话中多次执行该功能,这是有意义的。但是,您还必须使其成为STABLE,否则您将失去潜在的性能优势。 The manual once more:

      

    STABLEIMMUTABLE函数使用从中创建的快照   调用查询的开始,而VOLATILE函数获得新鲜   快照在每个查询开始时执行。

  3. 您的EXPLAIN输出显示仅索引扫描,而非您在评论中怀疑的顺序扫描。

  4. 您的EXPLAIN输出中还有一个与您显示的代码不匹配的排序步骤。您确定复制了正确的EXPLAIN输出吗?你是怎么得到它的? PL / pgSQL函数是EXPLAIN的黑盒子。你使用auto_explain了吗?详细说明:

  5. Postgres查询规划器不知道传递的参数将包含多少个数组元素,因此很难规划查询,它可能默认为顺序扫描(取决于更多因素)。您可以通过声明预期的行数来提供帮助。如果您通常不会超过10个项目添加ROWS 10,就像我上面所做的那样。再次测试。

答案 1 :(得分:1)

如果将一组记录传递给函数,则很简单:

with attendance (class, section, name) as(values
    (1, 1, 'Amy'),
    (1, 1, 'Bill'),
    (1, 2, 'Charlie'),
    (1, 2, 'Dan'),
    (2, 1, 'Emily'),
    (2, 1, 'Fred'),
    (2, 2, 'George')
)
select *
from attendance
where (class, section) = any(array[(1,1),(2,2)])
;
 class | section |  name  
-------+---------+--------
     1 |       1 | Amy
     1 |       1 | Bill
     2 |       2 | George