2016-09-18 58 views

回答

1

您可以使用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; 

row_number將計算其組內每行的數量。然後,我們使用該數字來過濾每個組的前2行(即最低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所有不同的值,併爲他們每個人都會使用子查詢LATERAL找到前2行。

+0

感謝。沒關係 – duyanhphamkiller

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_idm_option唯一列表會招致昂貴的順序掃描過m_option並使任何性能優勢。

這排除沒有任何相關行的學生m_option。使用LEFT JOIN LATERAL() ON true包括這些學生的結果(擴展了NULL值失蹤的選項):

如果你沒有一個student表中,其他快速選擇是遞歸的CTE 。
詳細的變種兩種解釋:

+0

SQL92已經出來一段時間了,有時間擺脫舊式聯接? – Andomar

+0

@Andomar:'CROSS JOIN'的簡短符號[記錄](https://www.postgresql.org/docs/current/static/sql-select.html)(綁定不太緊密)。它沒有錯。顯式連接條件比'WHERE'子句中的泛型謂詞更可取。但沒有連接條件,這是沒有意義的。 –

+0

我同意'交叉連接'就是舊式逗號語法有很多缺點的情況。但我不認爲保持SQL89的合理性。混合兩種風格對於學習SQL的人來說是不一致的和混亂的。 – Andomar