MySQL 加入限制动态查询优化
MySQL JOIN LIMIT DYNAMIC QUERY OPTIMIZATION
这里的架构:-
Student_Details
ID | Student_id | Name | Value
1 | 2 | City | NewYork
2 | 2 | Height | 5'11'
3 | 2 | Class | B
4 | 2 | RollNo | 265454
5 | 2 | Credit | 800
6 | 3 | City | Manila
7 | 3 | Height | 5'10'
8 | 3 | Type | International
9 | 3 | RollNo | 2653
10 | 1 | Address | Main Street
11 | 1 | Height | 5'9'
12 | 1 | TST | 21.54
13 | 1 | CCA | 242
13 | 1 | SEX | Male
Students
ID | Name | Age | AddedOn
1 | Alpha | 27 | 20 Jan 2019
2 | Beta | 15 | 19 Jan 2019
3 | Gamma | 18 | 20 Jan 2019
Student_Dep_Map
ID |DEP_ID | Student_id
1 | 4 | 2
2 | 2 | 1
Department
ID| Name
1 | Science
2 | Physics
3 | Chemistry
4 | Psychology
所以搜索围绕学生展开,我们需要使用各种搜索条件搜索学生,例如 'City' ,'Height' 或 'TST' ,学生在哪个部门,添加时间,年龄,等等..,基本上所有这些 tables。
动态搜索。此外,输出搜索还应包含每个 tables 中的一些固定详细信息。所以我用 pivot 做了这个查询。
select distinct s.*
FROM students s
LEFT JOIN ( SELECT s.ID as s_id, group_concat(distinct d.name SEPARATOR ', ') as ASSIGNED_DEPT FROM students s
JOIN Student_Dep_Map sdp JOIN Department d on sdp.DEP_ID = d.ID ON sdp.Student_id=s.id GROUP BY s.id ) aa ON aa.s_id = s.ID
LEFT JOIN ( SELECT DISTINCT student_id,
MAX(if(Name = 'TST', value, null)) as TST,
MAX(if(Name = 'Height', value, null)) as HEIGHT,
MAX(if(Name = 'CITY', value, null)) as CITY,
MAX(if(Name = 'SEX', value, null)) as SEX,
MAX(if(Name = 'RollNo', value, null)) as ROLLNO
FROM student_detials tip GROUP BY student_id ) sd ON sd.student_id = s.ID
WHERE 1=1 AND SD.SEX = 'Male' LIMIT 0,100 ;
AND 条件是通过查询生成器动态添加的。
挑战是!这个查询太慢了,随着 tables 中记录的增加,查询变得非常慢。我确实使用 explain 来了解查询计划。如果我离开索引并只谈论优化查询计划。
我注意到当我使用 pivot 时,即使搜索条件来自 main table student,查询计划仍然会在旋转学生详细信息时遍历几乎所有记录。
有什么方法可以限制加入,直到找到 100 条学生记录。
一种方法是为每个 table 编写子查询到 LIMIT 结果,但是有没有更好的方法来处理这个问题?
我需要限制连接,而不仅仅是连接后的结果,因此查询会导致更快的获取。
注意:抱歉,我无法 post 真实 tables ,它只是模拟数据。
我建议使用 exists
而不是聚合:
SELECT s.*,
(SELECT GROUP_CONCAT(d.name SEPARATOR ', ')
FROM Student_Dep_Map sdp JOIN
Department d
ON sdp.DEP_ID = d.ID
WHERE sdp.Student_id = s.id
) as ASSIGNED_DEPT
FROM students s
WHERE EXISTS (SELECT 1
FROM student_details sd
WHERE sd.student_id = s.id AND
sd.Name = 'SEX' AND
sd.Value = 'Male'
)
LIMIT 0, 100 ;
为了提高性能,您需要索引:
Student_Dep_Map(student_id, dep_id)
Departments(id, name)
(可能存在因为id
应该是主键)
student_details(student_id, name, value)
.
要添加更多条件,请添加更多 EXISTS
个子查询。
如果您有兴趣从 student_details
中选择某些字段,则可以采用另一种可能的方法。
SELECT s.*
FROM students s
LEFT OUTER JOIN
(
SELECT student_id
, MAX(if(Name = 'TST', value, null)) as TST
, MAX(if(Name = 'Height', value, null)) as HEIGHT
, MAX(if(Name = 'CITY', value, null)) as CITY
, MAX(if(Name = 'SEX', value, null)) as SEX
, MAX(if(Name = 'RollNo', value, null)) as ROLLNO
FROM student_details tip
GROUP BY student_id
) sd
ON s.ID = sd.student_ID
AND sd.SEX = 'Male'
LEFT OUTER JOIN
(
SELECT sdp.ID as s_id
, group_concat(d.name SEPARATOR ', ') as ASSIGNED_DEPT
FROM Student_Dep_Map sdp
INNER JOIN Department d ON sdp.DEP_ID = d.ID
GROUP BY sdp.id
) aa
ON aa.s_id = s.ID
;
您的原始查询也有 LEFT JOIN(...) WHERE sd.sex = 'Male'
,这将消除整个 LEFT JOIN
部分。您可以通过以下方式简化它:
SELECT s.ID
, s.Name
, s.Age
, s.AddedOn
, group_concat(d.name SEPARATOR ', ') as ASSIGNED_DEPT
, sd.City
FROM students s
INNER JOIN student_dep_map sdp ON s.ID = sdp.ID
INNER JOIN Department d ON sdp.DEP_ID = d.ID
INNER JOIN
(
SELECT student_id
, MAX(if(Name = 'TST', value, null)) as TST
, MAX(if(Name = 'Height', value, null)) as HEIGHT
, MAX(if(Name = 'CITY', value, null)) as CITY
, MAX(if(Name = 'SEX', value, null)) as SEX
, MAX(if(Name = 'RollNo', value, null)) as ROLLNO
FROM student_details tip
GROUP BY student_id
) sd
ON s.ID = sd.student_ID
WHERE sd.SEX = 'Male'
GROUP BY s.ID
, s.Name
, s.Age
, s.AddedOn
, sd.City;
这里的架构:-
Student_Details
ID | Student_id | Name | Value
1 | 2 | City | NewYork
2 | 2 | Height | 5'11'
3 | 2 | Class | B
4 | 2 | RollNo | 265454
5 | 2 | Credit | 800
6 | 3 | City | Manila
7 | 3 | Height | 5'10'
8 | 3 | Type | International
9 | 3 | RollNo | 2653
10 | 1 | Address | Main Street
11 | 1 | Height | 5'9'
12 | 1 | TST | 21.54
13 | 1 | CCA | 242
13 | 1 | SEX | Male
Students
ID | Name | Age | AddedOn
1 | Alpha | 27 | 20 Jan 2019
2 | Beta | 15 | 19 Jan 2019
3 | Gamma | 18 | 20 Jan 2019
Student_Dep_Map
ID |DEP_ID | Student_id
1 | 4 | 2
2 | 2 | 1
Department
ID| Name
1 | Science
2 | Physics
3 | Chemistry
4 | Psychology
所以搜索围绕学生展开,我们需要使用各种搜索条件搜索学生,例如 'City' ,'Height' 或 'TST' ,学生在哪个部门,添加时间,年龄,等等..,基本上所有这些 tables。 动态搜索。此外,输出搜索还应包含每个 tables 中的一些固定详细信息。所以我用 pivot 做了这个查询。
select distinct s.*
FROM students s
LEFT JOIN ( SELECT s.ID as s_id, group_concat(distinct d.name SEPARATOR ', ') as ASSIGNED_DEPT FROM students s
JOIN Student_Dep_Map sdp JOIN Department d on sdp.DEP_ID = d.ID ON sdp.Student_id=s.id GROUP BY s.id ) aa ON aa.s_id = s.ID
LEFT JOIN ( SELECT DISTINCT student_id,
MAX(if(Name = 'TST', value, null)) as TST,
MAX(if(Name = 'Height', value, null)) as HEIGHT,
MAX(if(Name = 'CITY', value, null)) as CITY,
MAX(if(Name = 'SEX', value, null)) as SEX,
MAX(if(Name = 'RollNo', value, null)) as ROLLNO
FROM student_detials tip GROUP BY student_id ) sd ON sd.student_id = s.ID
WHERE 1=1 AND SD.SEX = 'Male' LIMIT 0,100 ;
AND 条件是通过查询生成器动态添加的。
挑战是!这个查询太慢了,随着 tables 中记录的增加,查询变得非常慢。我确实使用 explain 来了解查询计划。如果我离开索引并只谈论优化查询计划。 我注意到当我使用 pivot 时,即使搜索条件来自 main table student,查询计划仍然会在旋转学生详细信息时遍历几乎所有记录。 有什么方法可以限制加入,直到找到 100 条学生记录。 一种方法是为每个 table 编写子查询到 LIMIT 结果,但是有没有更好的方法来处理这个问题? 我需要限制连接,而不仅仅是连接后的结果,因此查询会导致更快的获取。
注意:抱歉,我无法 post 真实 tables ,它只是模拟数据。
我建议使用 exists
而不是聚合:
SELECT s.*,
(SELECT GROUP_CONCAT(d.name SEPARATOR ', ')
FROM Student_Dep_Map sdp JOIN
Department d
ON sdp.DEP_ID = d.ID
WHERE sdp.Student_id = s.id
) as ASSIGNED_DEPT
FROM students s
WHERE EXISTS (SELECT 1
FROM student_details sd
WHERE sd.student_id = s.id AND
sd.Name = 'SEX' AND
sd.Value = 'Male'
)
LIMIT 0, 100 ;
为了提高性能,您需要索引:
Student_Dep_Map(student_id, dep_id)
Departments(id, name)
(可能存在因为id
应该是主键)student_details(student_id, name, value)
.
要添加更多条件,请添加更多 EXISTS
个子查询。
如果您有兴趣从 student_details
中选择某些字段,则可以采用另一种可能的方法。
SELECT s.*
FROM students s
LEFT OUTER JOIN
(
SELECT student_id
, MAX(if(Name = 'TST', value, null)) as TST
, MAX(if(Name = 'Height', value, null)) as HEIGHT
, MAX(if(Name = 'CITY', value, null)) as CITY
, MAX(if(Name = 'SEX', value, null)) as SEX
, MAX(if(Name = 'RollNo', value, null)) as ROLLNO
FROM student_details tip
GROUP BY student_id
) sd
ON s.ID = sd.student_ID
AND sd.SEX = 'Male'
LEFT OUTER JOIN
(
SELECT sdp.ID as s_id
, group_concat(d.name SEPARATOR ', ') as ASSIGNED_DEPT
FROM Student_Dep_Map sdp
INNER JOIN Department d ON sdp.DEP_ID = d.ID
GROUP BY sdp.id
) aa
ON aa.s_id = s.ID
;
您的原始查询也有 LEFT JOIN(...) WHERE sd.sex = 'Male'
,这将消除整个 LEFT JOIN
部分。您可以通过以下方式简化它:
SELECT s.ID
, s.Name
, s.Age
, s.AddedOn
, group_concat(d.name SEPARATOR ', ') as ASSIGNED_DEPT
, sd.City
FROM students s
INNER JOIN student_dep_map sdp ON s.ID = sdp.ID
INNER JOIN Department d ON sdp.DEP_ID = d.ID
INNER JOIN
(
SELECT student_id
, MAX(if(Name = 'TST', value, null)) as TST
, MAX(if(Name = 'Height', value, null)) as HEIGHT
, MAX(if(Name = 'CITY', value, null)) as CITY
, MAX(if(Name = 'SEX', value, null)) as SEX
, MAX(if(Name = 'RollNo', value, null)) as ROLLNO
FROM student_details tip
GROUP BY student_id
) sd
ON s.ID = sd.student_ID
WHERE sd.SEX = 'Male'
GROUP BY s.ID
, s.Name
, s.Age
, s.AddedOn
, sd.City;