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%',...

  1. 对于每个 table,在相关字段中搜索 'Newtown%'。
  2. 将步骤 1 中的每个 SELECT 加入回 properties(假设这是该数据集的焦点);得到只是属性的主键。
  3. UNION DISTINCT 来自步骤 2 的所有 SELECT。
  4. 现在加入任何需要的 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,它通过利用两个额外的列存储极大地缩短了查询响应时间,这是值得的。

编辑: 在我选择的列上创建一些索引也大大缩短了响应时间。