首页 > 解决方案 > 预过滤查询

问题描述

我正在尝试优化一个需要很长时间的查询,我无法向数据库添加任何新索引,但我找到了一种将执行时间缩短一半的方法。解决方案是仅使用一些 WHERE 条件对查询进行预过滤,返回 id 并在这些 id 上运行其余条件。我想知道是否可以在没有往返的情况下做到这一点

原始查询的最小示例(不是实际查询,而是相同的想法):

SELECT 
    COUNT(*) as total 
FROM 
    my_db.bananas AS b, 
    my_db.bananas_specs as s
WHERE 
    b.created BETWEEN '2021-01-01 00:00:00' AND '2022-01-01 00:00:00'
    AND b.gone_bad = false
    AND b.id = s.id
    AND s.is_cool = true

我的新查询一起运行了一半的时间:

SELECT 
    GROUP_CONCAT(id SEPARATOR ', ') as ids
FROM
    my_db.bananas
WHERE
    created BETWEEN '2021-01-01 00:00:00' AND '2022-01-01 00:00:00'

SELECT 
    COUNT(*) as total
FROM 
    my_db.bananas AS b, 
    my_db.bananas_specs as s
WHERE 
    b.id in (...result of first query)
    AND b.gone_bad = false
    AND b.id = s.id
    AND s.is_cool = true

我知道 mysql 中有变量,并且它可以在单个查询中运行多个 select 语句,因为谷歌告诉我它是这样,但我无法让它工作,php 给我的错误消息不是很有帮助。本质上,如何在单个查询中将后两个查询作为单独的选择语句执行?

标签: mysqlmysql-5.6

解决方案


with 语句对于优化代码可读性问题非常有用 - 在加入第二个表之前选择基础数据可以改善,但不会显着。

只计算一列可以提高性能,而不是计算整个数据集

例如

with cte_bananas as (
SELECT id, created, gone_bad
  from my_db.bananas 
 where created BETWEEN '2021-01-01 00:00:00' AND '2022-01-01 00:00:00'
) -- end with cte_bananas
select count(1) as Total
  from cte_bananas b
  inner join my_db.bananas_specs as s
  on s.id = b.id
 where b.gone_bad = false
   and s.is_cool = true

由于您的查询是一个片段,因此这里可能需要进行一些实验。


推荐阅读