2014-02-12 50 views
1

我有students多對多關聯groups通過連接表groups_students。每個group都有一個group_type,它可以是permission_group或不是(group_types表上的布爾值)。MySQL關係部門查詢性能

我也有users,這也是多對多與groups通過groups_users相關聯。

enter image description here

我要回:某一特定user所有學生的權限組相關聯的所有students

我已經導致相信這需要相關的部門,這裏是我與它其中:

SELECT DISTINCT gs.student_id 
FROM groups_students AS gs 
INNER JOIN groups ON groups.id = gs.group_id 
INNER JOIN groups_users gu ON gu.group_id = groups.id 
INNER JOIN group_types ON group_types.id = groups.group_type_id 
WHERE group_types.permission_group = 1 
AND gu.user_id = 37 
AND NOT EXISTS (
    SELECT * FROM groups_students AS gs2 
    WHERE gs2.student_id = gs.student_id 
    AND NOT EXISTS (
    SELECT gu2.group_id 
    FROM groups_users AS gu2 
    WHERE gu2.group_id = gs2.group_id AND gu2.user_id = gu.user_id 
) 
) 

這工作,但我在groups_students超過20,000行實時的數據庫,它需要3秒。

我可以做得更快嗎?我讀了關於與COUNT做關係部門,但我無法將其與我的情況。我能夠以低廉的收益在半秒的執行時間內完成這個查詢嗎?還是我正在尋找一個重大的重組?

編輯 - 英文描述:學生屬於班級(組),用戶有權查看某些班級。我需要知道特定用戶有權查看所有(許可)類的學生。

EXPLAIN的慢查詢:

+----+--------------------+-------------+--------+--------------------------------------------------------------+--------------------------------------------------+---------+-----------------------------+------+--------------------------------+ 
| id | select_type  | table  | type | possible_keys            | key            | key_len | ref       | rows | Extra       | 
+----+--------------------+-------------+--------+--------------------------------------------------------------+--------------------------------------------------+---------+-----------------------------+------+--------------------------------+ 
| 1 | PRIMARY   | gu   | ref | index_groups_users_on_user_id,index_groups_users_on_group_id | index_groups_users_on_user_id     | 5  | const      | 1181 | Using where; Using temporary | 
| 1 | PRIMARY   | groups  | eq_ref | PRIMARY              | PRIMARY           | 4  | my_db.gu.group_id   | 1 |        | 
| 1 | PRIMARY   | group_types | ALL | PRIMARY              | NULL            | NULL | NULL      | 3 | Using where; Using join buffer | 
| 1 | PRIMARY   | gs   | ref | index_groups_students_on_group_id_and_student_id    | index_groups_students_on_group_id_and_student_id | 4  | my_db.groups.id    | 9 | Using where; Using index  | 
| 2 | DEPENDENT SUBQUERY | gs2   | ref | index_groups_students_on_student_id_and_group_id    | index_groups_students_on_student_id_and_group_id | 4  | my_db.gs.student_id   | 8 | Using where; Using index  | 
| 3 | DEPENDENT SUBQUERY | gu2   | ref | index_groups_users_on_user_id,index_groups_users_on_group_id | index_groups_users_on_group_id     | 5  | my_db.gs2.group_id   | 99 | Using where     | 
+----+--------------------+-------------+--------+--------------------------------------------------------------+--------------------------------------------------+---------+-----------------------------+------+--------------------------------+ 

SQL Fiddle

+0

去正規化和屬性添加權限組,以​​組 –

+0

@SamD - 我試過,但它並沒有noticable差異的執行速度。問題出在嵌套的子查詢和大的'groups_students'連接表中。 –

+0

17和18出現在您的結果中,但它們似乎不符合標準 – Strawberry

回答

2

「我想返回特定用戶與所有學生的權限組相關聯的所有學生。」

我並不真正關注你的查詢;這個目的似乎很複雜。相反,我認爲它是如下:

  1. 生成所有的學生和他們的權限
  2. 生成用戶的所有權限37
  3. (外)加入這些結合在一起的權限
  4. 確保所有權限特定學生是u37組中

所得查詢是:

select student_id 
from (SELECT gs.student_id, g.id as group_id 
     FROM groups_students gs INNER JOIN 
      groups g 
      ON g.id = gs.group_id INNER JOIN 
      groups_users gu 
      ON gu.group_id = g.id INNER JOIN 
      group_types gt 
      ON gt.id = g.group_type_id 
     where gt.permission_group = 1 
    ) s left outer join 
    (select g.id as group_id 
     from groups_users gu INNER JOIN 
      groups g 
      on gu.group_id = g.id INNER JOIN 
      group_types gt 
      ON gt.id = g.group_type_id 
     where gu.user_id = 37 and gt.permission_group = 1 
    ) u37 
    on s.group_id = u37.group_id 
group by s.student_id 
having count(*) = count(u37.group_id); 

注意:您可以在沒有子查詢的情況下執行此操作。儘管他們有開銷,但我認爲他們讓查詢更容易理解。

+0

嗨戈登 - 我沒有過濾任何特定的組,「37」是一個用戶的任意ID,因爲這應該產生學生,用戶有許可查看。我正在查看您的查詢,看看我是否可以調整它! –

+0

啊,我想我誤解了。查詢看起來不錯,已經從整體時間上減少了十分之一,但它仍然運行在3.25s .. –

+0

@MikeCampbell。 。 。再試一次。我想我開始瞭解這些數據和問題。這個連接在'group_type_id'上,這會導致一個大的笛卡兒積。我把它切換到'group_id',更合理和正確。 –

0

我不明白你爲什麼使用子查詢。他們通常很慢,應儘可能避免。也許我沒有正確理解您的需求,但我會想出這樣的事情:

SELECT DISTINCT gs.student_id 
FROM groups_students AS gs 
INNER JOIN groups ON groups.id = gs.group_id 
INNER JOIN groups_users gu ON gu.group_id = groups.id 
INNER JOIN group_types ON group_types.id = groups.group_type_id 
LEFT JOIN groups_students AS gs2 ON gs2.student_id = gs.student_id 
LEFT JOIN groups_users AS gu2 ON gu2.group_id = gs2.group_id AND gu2.user_id = gu.user_id 
WHERE group_types.permission_group = 1 
AND gu.user_id = 37 
AND gs2.student_id IS NULL 
AND gu2.group_id IS NULL 

您可以強制東西不使用左連接和檢查存在,即右表列(使用主鍵)包含null。

2

戈登的想法的一個簡化版本...

SELECT gs.student_id 
    FROM groups_students gs 
    JOIN groups g 
    ON g.id = gs.group_id 
    JOIN group_types gt 
    ON gt.id = g.group_type_id 
    LEFT 
    JOIN groups_users gu 
    ON gu.group_id = gs.group_id 
    AND gu.user_id = 37 
WHERE gt.permission_group 
GROUP 
    BY student_id 
HAVING COUNT(student_id) = COUNT(user_id) 
+0

嗯,我想我可以在'groups_users'上做一個INNER JOIN實際上:s。明天我會再次訪問,但是如果我可以,那麼你的很棒 - 謝謝! –

+0

我懷疑你的樣本數據集不能正確代表手頭的問題,這就是爲什麼你可以用INNER JOIN來做到這一點。 – Strawberry

+0

你說得對,我的數據不足以認識到INNER JOIN不起作用。如果學生在多個權限組中,則INNER JOIN不會強制用戶有權查看全部權限。考慮到這一點,在第一個子查詢中,Gordon的解決方案沒有(似乎?)不必要地加入到'groups_users'中的速度是當前速度的兩倍多。 –