SQL 使用 UNION ALL 进行查询优化
SQL Query optimization with UNION ALL
我按照 This link
的说明进行操作
好像没有太大的提升(只提升了0.09秒)。还不够,我的目标是再优化一下。
表格
state: id,title,abbr
regions: id,title
regions_suburbs: region_id,suburb_id
suburbs: id,state_id,region_id,postcode
properties: id,title
我试图在 proprety.title、suburb.title、state.abbr、state.title 和 suburb.postcode 中找到任何关键字。
查询如下-
[Perivous] 显示第 0 - 4 行(共 5 行,查询耗时 4.7122 秒)
SELECT * FROM (SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p LEFT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id UNION ALL SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p RIGHT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id UNION ALL SELECT CONCAT('state_id|',id),'' As SuburbName,'' AS postcode,abbr,title AS SearchTerm FROM state) AS U WHERE 1 AND (SuburbName LIKE 'Newtown%' OR postcode='Newtown' OR LOWER(SearchTerm) LIKE LOWER('Newtown%') OR abbr LIKE 'Newtown%') ORDER BY SearchTerm ASC LIMIT 0,10
解释结果
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 16657 Using where; Using filesort
2 DERIVED p ALL NULL NULL NULL NULL 3
2 DERIVED s eq_ref PRIMARY PRIMARY 4 residential.p.suburb_id 1
2 DERIVED rs ALL NULL NULL NULL NULL 383
2 DERIVED r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
2 DERIVED st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1
3 UNION s ALL NULL NULL NULL NULL 16640
3 UNION p ALL NULL NULL NULL NULL 3
3 UNION rs ALL NULL NULL NULL NULL 383
3 UNION r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
3 UNION st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1
4 UNION state ALL NULL NULL NULL NULL 8
NULL UNION RESULT <union2,3,4> ALL NULL NULL NULL NULL NULL
[现在] 显示第 0 - 4 行(共 5 行,查询耗时 4.6246 秒)
SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p LEFT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id WHERE 1 AND (s.title LIKE 'Newtown%' OR postcode='Newtown' OR LOWER(CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END)) LIKE LOWER('Newtown%') OR st.abbr LIKE 'Newtown%') UNION ALL SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p RIGHT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id WHERE 1 AND (s.title LIKE 'Newtown%' OR postcode='Newtown' OR LOWER(CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END)) LIKE LOWER('Newtown%') OR st.abbr LIKE 'Newtown%') UNION ALL SELECT CONCAT('state_id|',id),'' As SuburbName,'' AS postcode,abbr,title AS SearchTerm FROM state WHERE 1 AND title LIKE 'Newtown%' ORDER BY SearchTerm ASC LIMIT 0,10
解释结果
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY p ALL NULL NULL NULL NULL 3
1 PRIMARY s eq_ref PRIMARY PRIMARY 4 residential.p.suburb_id 1
1 PRIMARY rs ALL NULL NULL NULL NULL 383
1 PRIMARY r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
1 PRIMARY st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1 Using where
2 UNION s ALL title_postcode NULL NULL NULL 16640
2 UNION p ALL NULL NULL NULL NULL 3
2 UNION rs ALL NULL NULL NULL NULL 383
2 UNION r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
2 UNION st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1 Using where
3 UNION state range title title 102 NULL 1 Using where
NULL UNION RESULT <union1,2,3> ALL NULL NULL NULL NULL NULL Using filesort
把它翻过来。而不是计算你想要的一切,然后查看每个结果片段中是否有 'Newtown%',...
- 对于每个 table,在相关字段中搜索 'Newtown%'。
- 将步骤 1 中的每个 SELECT 加入回
properties
(假设这是该数据集的焦点);得到只是属性的主键。
- UNION DISTINCT 来自步骤 2 的所有 SELECT。
- 现在加入任何需要的 tables,并根据第 3 步中的 ID 执行 knarly CONCAT 等以生成输出。
一旦你这样做了,如果你愿意在 ORDER BY 中使用 SearchTerm
以外的东西,你可以获得更多的性能。我会简单地建议 properties.id
。这样,您就可以在第 2 步和第 3 步中执行 ORDER BY,以减少一步一步铲除的数量。
(当您有 UNION
时,OFFSET
会导致另一个问题;我们可以单独解决该问题。)
在此感谢大家的帮助。我终于找到了拖慢查询速度的罪魁祸首。
我试图从我的 SQL 语句中一点一点地删除,并且 运行 一遍又一遍。最后在删除以下两个 LEFT JOIN 部分后,
LEFT JOIN regions_suburbs rs
ON rs.suburb_id = s.id
LEFT JOIN regions r
ON rs.region_id = r.id
它改进了
Showing rows 0 - 1 (2 total, Query took 4.8538 sec)
至
Showing rows 0 - 1 (2 total, Query took 0.2337 sec)
为了实现这一点,我必须对数据库设计进行一些更改。最初,属性 table 只存储 suburb_id,因为如果我们知道 suburb_id,我们可以得到它的 region_id 和 state_id。为避免加入 regions_suburbs 和区域 table,我现在存储 state_id、region_id 和 suburb_id 而不是仅存储一个 suburb_id。在此我们可以从查询中取出两个 LEFT JOIN,它通过利用两个额外的列存储极大地缩短了查询响应时间,这是值得的。
编辑:
在我选择的列上创建一些索引也大大缩短了响应时间。
我按照 This link
的说明进行操作好像没有太大的提升(只提升了0.09秒)。还不够,我的目标是再优化一下。
表格
state: id,title,abbr
regions: id,title
regions_suburbs: region_id,suburb_id
suburbs: id,state_id,region_id,postcode
properties: id,title
我试图在 proprety.title、suburb.title、state.abbr、state.title 和 suburb.postcode 中找到任何关键字。
查询如下-
[Perivous] 显示第 0 - 4 行(共 5 行,查询耗时 4.7122 秒)
SELECT * FROM (SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p LEFT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id UNION ALL SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p RIGHT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id UNION ALL SELECT CONCAT('state_id|',id),'' As SuburbName,'' AS postcode,abbr,title AS SearchTerm FROM state) AS U WHERE 1 AND (SuburbName LIKE 'Newtown%' OR postcode='Newtown' OR LOWER(SearchTerm) LIKE LOWER('Newtown%') OR abbr LIKE 'Newtown%') ORDER BY SearchTerm ASC LIMIT 0,10
解释结果
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 16657 Using where; Using filesort
2 DERIVED p ALL NULL NULL NULL NULL 3
2 DERIVED s eq_ref PRIMARY PRIMARY 4 residential.p.suburb_id 1
2 DERIVED rs ALL NULL NULL NULL NULL 383
2 DERIVED r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
2 DERIVED st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1
3 UNION s ALL NULL NULL NULL NULL 16640
3 UNION p ALL NULL NULL NULL NULL 3
3 UNION rs ALL NULL NULL NULL NULL 383
3 UNION r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
3 UNION st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1
4 UNION state ALL NULL NULL NULL NULL 8
NULL UNION RESULT <union2,3,4> ALL NULL NULL NULL NULL NULL
[现在] 显示第 0 - 4 行(共 5 行,查询耗时 4.6246 秒)
SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p LEFT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id WHERE 1 AND (s.title LIKE 'Newtown%' OR postcode='Newtown' OR LOWER(CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END)) LIKE LOWER('Newtown%') OR st.abbr LIKE 'Newtown%') UNION ALL SELECT CASE WHEN p.id IS NOT NULL THEN CONCAT('project_id|',p.id) WHEN s.id IS NOT NULL THEN CONCAT('suburb_id|',s.id) ELSE '0' END AS id,s.title As SuburbName,s.postcode,st.abbr,CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END) AS SearchTerm FROM properties p RIGHT OUTER JOIN suburbs s ON p.suburb_id=s.id LEFT JOIN regions_suburbs rs ON rs.suburb_id=s.id LEFT JOIN regions r ON rs.region_id=r.id LEFT JOIN state st ON st.id=s.state_id WHERE 1 AND (s.title LIKE 'Newtown%' OR postcode='Newtown' OR LOWER(CONCAT(CASE WHEN p.propertyname IS NULL THEN '' ELSE CONCAT(p.propertyname,', ') END,CASE WHEN s.title IS NULL THEN'' ELSE CONCAT(UPPER(s.title), ' ') END,CASE WHEN st.abbr IS NULL THEN '' ELSE CONCAT(UPPER(st.abbr), ' ') END,CASE WHEN s.postcode IS NULL THEN '' ELSE CONCAT(s.postcode, '') END)) LIKE LOWER('Newtown%') OR st.abbr LIKE 'Newtown%') UNION ALL SELECT CONCAT('state_id|',id),'' As SuburbName,'' AS postcode,abbr,title AS SearchTerm FROM state WHERE 1 AND title LIKE 'Newtown%' ORDER BY SearchTerm ASC LIMIT 0,10
解释结果
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY p ALL NULL NULL NULL NULL 3
1 PRIMARY s eq_ref PRIMARY PRIMARY 4 residential.p.suburb_id 1
1 PRIMARY rs ALL NULL NULL NULL NULL 383
1 PRIMARY r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
1 PRIMARY st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1 Using where
2 UNION s ALL title_postcode NULL NULL NULL 16640
2 UNION p ALL NULL NULL NULL NULL 3
2 UNION rs ALL NULL NULL NULL NULL 383
2 UNION r eq_ref PRIMARY PRIMARY 4 residential.rs.region_id 1 Using index
2 UNION st eq_ref PRIMARY PRIMARY 4 residential.s.state_id 1 Using where
3 UNION state range title title 102 NULL 1 Using where
NULL UNION RESULT <union1,2,3> ALL NULL NULL NULL NULL NULL Using filesort
把它翻过来。而不是计算你想要的一切,然后查看每个结果片段中是否有 'Newtown%',...
- 对于每个 table,在相关字段中搜索 'Newtown%'。
- 将步骤 1 中的每个 SELECT 加入回
properties
(假设这是该数据集的焦点);得到只是属性的主键。 - UNION DISTINCT 来自步骤 2 的所有 SELECT。
- 现在加入任何需要的 tables,并根据第 3 步中的 ID 执行 knarly CONCAT 等以生成输出。
一旦你这样做了,如果你愿意在 ORDER BY 中使用 SearchTerm
以外的东西,你可以获得更多的性能。我会简单地建议 properties.id
。这样,您就可以在第 2 步和第 3 步中执行 ORDER BY,以减少一步一步铲除的数量。
(当您有 UNION
时,OFFSET
会导致另一个问题;我们可以单独解决该问题。)
在此感谢大家的帮助。我终于找到了拖慢查询速度的罪魁祸首。
我试图从我的 SQL 语句中一点一点地删除,并且 运行 一遍又一遍。最后在删除以下两个 LEFT JOIN 部分后,
LEFT JOIN regions_suburbs rs
ON rs.suburb_id = s.id
LEFT JOIN regions r
ON rs.region_id = r.id
它改进了
Showing rows 0 - 1 (2 total, Query took 4.8538 sec)
至
Showing rows 0 - 1 (2 total, Query took 0.2337 sec)
为了实现这一点,我必须对数据库设计进行一些更改。最初,属性 table 只存储 suburb_id,因为如果我们知道 suburb_id,我们可以得到它的 region_id 和 state_id。为避免加入 regions_suburbs 和区域 table,我现在存储 state_id、region_id 和 suburb_id 而不是仅存储一个 suburb_id。在此我们可以从查询中取出两个 LEFT JOIN,它通过利用两个额外的列存储极大地缩短了查询响应时间,这是值得的。
编辑: 在我选择的列上创建一些索引也大大缩短了响应时间。