MySQL关系部门查询性能

时间:2014-02-12 15:56:08

标签: mysql performance

我有students通过联接表groupsgroups_students进行多对多关联。每个group都有一个group_type,可以是permission_group或不是group_types表上的布尔值。

我还有users,它还通过groupsgroups_users进行多对多关联。

enter image description here

我想返回特定students所有学生的权限组相关联的所有user

我一直认为这需要关系师,而这就是我所处的位置:

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

3 个答案:

答案 0 :(得分:2)

“我希望将特定用户与所有学生的权限组相关联的所有学生退回。”

我真的没有按照您的查询;为此目的似乎很复杂。相反,我认为如下:

  1. 生成所有学生及其权限
  2. 为用户37生成所有权限
  3. (外部)将这些权限加在一起
  4. 确保特定学生的所有权限都在u37群组
  5. 生成的查询是:

    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);
    

    注意:您可以在没有子查询的情况下执行此操作。尽管有他们的开销,我认为他们使查询更容易理解。

答案 1 :(得分: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)

答案 2 :(得分: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。