我在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;
SELECT * FROM get_attendance(ARRAY[[1,15],[2,15],[3,8]]);
问题是查询正在扫描考勤表中的所有考勤,而不会过滤它们直到加入。有什么方法可以解决这个问题吗?
答案 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}}');
您创建了VOLATILE
函数,但它可以是STABLE
。 Per documentation:
由于此快照行为,可以安全地将仅包含
SELECT
命令的函数标记为STABLE
。
相关:
您还使用LANGUAGE plpgsql
而不是sql
,如果您在同一会话中多次执行该功能,这是有意义的。但是,您还必须使其成为STABLE
,否则您将失去潜在的性能优势。 The manual once more:
STABLE
和IMMUTABLE
函数使用从中创建的快照 调用查询的开始,而VOLATILE函数获得新鲜 快照在每个查询开始时执行。
您的EXPLAIN
输出显示仅索引扫描,而非您在评论中怀疑的顺序扫描。
您的EXPLAIN
输出中还有一个与您显示的代码不匹配的排序步骤。您确定复制了正确的EXPLAIN
输出吗?你是怎么得到它的? PL / pgSQL函数是EXPLAIN
的黑盒子。你使用auto_explain
了吗?详细说明:
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