为每个student_id获取具有最小值的2个选项

时间:2016-09-18 01:24:40

标签: sql postgresql greatest-n-per-group

我有表名m_option

m_option_id  m_student_id  value
1             1             5
2             1             5
3             1             6
4             1             7
5             2             1
6             2             2
7             2             3
8             2             3
9             2             4

我想为每个value获得最少m_student_id的2行:

m_option_id  m_student_id  value
1             1             5
2             1             5
5             2             1
6             2             2

2 个答案:

答案 0 :(得分:1)

您可以使用<Directory /> Options FollowSymLinks AllowOverride All </Directory> 窗口功能:

row_number

SELECT m_option_id, m_student_id, value FROM ( SELECT m_option_id, m_student_id, value, row_number() OVER (PARTITION BY m_student_id ORDER BY value) FROM m_option ) t WHERE row_number <= 2; 将计算其组中每行的数量。然后我们使用该数字来过滤每组中的前2行(即最低row_number)。

或者,您可以使用value子查询:

LATERAL

这将遍历SELECT m_option_id, m_student_id, value FROM (SELECT DISTINCT m_student_id FROM m_option) s, LATERAL ( SELECT m_option_id, value FROM m_option WHERE s.m_student_id=m_student_id ORDER BY value LIMIT 2 ) t; 的所有不同值,并且每个值都会使用m_student_id子查询找到前2行。

答案 1 :(得分:1)

假设表m_option中每个学生可以有 多个 行,性能的关键是索引使用情况。如果你有一个单独的student列出所有学生的唯一(你通常会有),这是最有效的。然后:

SELECT m.m_option_id, s.student_id AS m_student_id, m.value
FROM   student s
    ,  LATERAL (
   SELECT m_option_id, value
   FROM   m_option
   WHERE  m_student_id = s.student_id  -- PK of table student
   ORDER  BY value
   LIMIT  2
   ) m;

m_option上的多列索引使

CREATE INDEX m_option_combo_idx ON m_option (m_student_id, value);

如果您可以使用 index-only scans ,请将列m_option_id添加为最后一个索引项:

CREATE INDEX m_option_combo_idx ON m_option (m_student_id, value, m_option_id)

按此顺序索引列。

student_id中提取m_option的唯一列表会导致对m_option进行昂贵的顺序扫描,并使任何性能优势无效。

这排除了m_option中没有任何相关行的学生。使用LEFT JOIN LATERAL () ON true将这些学生包含在结果中(使用NULL值扩展为缺失选项):

如果您没有student表,则另一个快速选项是递归CTE 两种变体的详细解释: