I'm writing a PL/pgSQL function in PostgreSQL 9.3.10 to return who has attended certain classes/sessions from the following table:
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 |
+-------+---------+---------+
What I want to do is, given a array of class/section id pairs (int[][]
), return all people who are in those classes/sections. For example my_func(ARRAY[[1,1],[2,2]])
returns:
+-------+---------+---------+
| Class | Section | Name |
+-------+---------+---------+
| 1 | 1 | Amy |
| 1 | 1 | Bill |
| 2 | 2 | George |
+-------+---------+---------+
If I knew the pairs beforehand, it would be a simple:
SELECT * FROM attendance
WHERE ((class = 1 AND section = 1) OR (class = 2 AND section = 2));
Instead, the pairs will be a parameter of the function.
Right now, the only way I can think of doing this is to have the function essentially build an SQL query string by appending a bunch of WHERE
clauses to the end of the query and then calling EXECUTE
. Is there a better way to get my result?
EDIT:
I implemented the suggestion @Erwin's suggestion and am currently able to get the results I want. Unfortunately, it appears as though it is fairly slow. Here is the function I am running:
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;
Querying it like so:
SELECT * FROM get_attendance(ARRAY[[1,15],[2,15],[3,8]]);
And I am getting the following EXPLAIN ANALYZE output
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)
The problem is that the query is scanning through all attendances in the attendance table without filtering them until the join. Is there any way to fix this?
See Question&Answers more detail:
os