一尘不染

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

sql

我在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子句然后调用来构建SQL查询字符串EXECUTE。有没有更好的方法来得到我的结果?

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

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;

像这样查询它:

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

我得到以下EXPLAIN ANALYZE输出

Merge Join  (cost=60.26..50139.72 rows=30840 width=8) (actual time=44.174..142.100 rows=25290 loops=1)
  Merge Cond: ((attendance.class = (('{{1,15},{2,15},{3,8}}'::integer[])[i.i][1])) AND (attendance.section = (('{{1,15},{2,15},{3,8}}'::integer[])[i.i][2])))
  ->  Index Only Scan using class_section_idx on attendance  (cost=0.43..43372.25 rows=1233588 width=8) (actual time=0.009..86.625 rows=1145046 loops=1)
        Heap Fetches: 0
  ->  Sort  (cost=59.83..62.33 rows=1000 width=4) (actual time=0.010..0.757 rows=10031 loops=1)
        Sort Key: (('{{1,15},{2,15},{3,8}}'::integer[])[i.i][1]), (('{{1,15},{2,15},{3,8}}'::integer[])[i.i][2])
        Sort Method: quicksort  Memory: 25kB
        ->  Function Scan on generate_subscripts i  (cost=0.00..10.00 rows=1000 width=4) (actual time=0.006..0.007 rows=3 loops=1)

问题在于查询正在扫描考勤表中的所有考勤,而没有过滤它们直到加入。有没有什么办法解决这一问题?


阅读 172

收藏
2021-03-17

共1个答案

一尘不染

您可以使用简单的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]]);

或与数组 文字 相同-在某些情况下,尤其是在准备好的语句中,这更方便:

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

该函数 始终 期望使用2D数组。即使您传递一对,也要嵌套:

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

审核您的实施

  1. 您已完成该功能VOLATILE,但可以STABLE每个文档:

由于这种快照行为,SELECT可以安全地标记仅包含命令的功能STABLE

有关的:

* [如何将参数传递给日期函数](https://stackoverflow.com/questions/30758691/how-to-pass-a-parameter-into-a-date-function/30763347#30763347)
  1. 如果您在同一会话中多次执行该函数,则还可以使用LANGUAGE plpgsql代替sql,这很有意义。但是,那么您还必须做到这一点,STABLE否则您将失去潜在的性能优势。手册再次:

STABLEIMMUTABLE函数使用在调用查询开始时建立的快照,而VOLATILE函数在执行的每个查询开始时获取一个新的快照。

  1. 您的EXPLAIN输出显示“ 仅索引扫描” ,而不是您在注释中怀疑的顺序扫描。

  2. 您的EXPLAIN输出中还有一个排序步骤,与您显示的代码不匹配。您确定复制了正确的EXPLAIN输出吗?无论如何,您是如何获得它的?PL / pgSQL函数是的黑匣子EXPLAIN。你用了auto_explain吗?细节:

    • 用pgpsql编写的UDF调用的Postgres查询计划
    • Postgres查询计划程序不知道传递的参数将包含多少个数组元素,因此很难计划查询,并且它可能默认为顺序扫描(取决于更多因素)。您可以通过声明预期的行数来提供帮助。如果您通常没有超过10个项目,请ROWS 10像我上面所做的那样添加。并再次测试。
2021-03-17