【问题标题】:SQL query with subqueries performing terribly带有子查询的 SQL 查询执行得非常糟糕
【发布时间】:2011-11-24 10:07:28
【问题描述】:

我有一个很长的查询,它应该给我一些关于发货的信息,它有效,但它的表现非常糟糕。加载大约需要 4500 毫秒。

SELECT
    DATE(paid_at) AS day,
    COUNT(*) as order_count,
    (
      SELECT COUNT(*) FROM line_items
      WHERE order_id IN (SELECT id from orders WHERE DATE(paid_at) = day)
    ) as product_count,
    (
      SELECT COUNT(*) FROM orders
      WHERE shipping_method = 'colissimo'
      AND DATE(paid_at) = day
      AND state IN ('paid','shipped','completed')
    ) as orders_co,
    (
      SELECT COUNT(*) FROM orders
      WHERE shipping_method = 'colissimo'
      AND DATE(paid_at) = day
      AND state IN ('paid','shipped','completed')
      AND paid_amount < 70
    ) as co_less_70,
    (
      SELECT COUNT(*) FROM orders
      WHERE shipping_method = 'colissimo'
      AND DATE(paid_at) = day
      AND state IN ('paid','shipped','completed')
      AND paid_amount >= 70
    ) as co_plus_70,
    (
      SELECT COUNT(*) FROM orders
      WHERE shipping_method = 'mondial_relais'
      AND DATE(paid_at) = day
      AND state IN ('paid','shipped','completed')
    ) as orders_mr,
    (
      SELECT COUNT(*) FROM orders
      WHERE shipping_method = 'mondial_relais'
      AND DATE(paid_at) = day
      AND state IN ('paid','shipped','completed')
      AND paid_amount < 70
    ) as mr_less_70,
    (
      SELECT COUNT(*) FROM orders
      WHERE shipping_method = 'mondial_relais'
      AND DATE(paid_at) = day
      AND state IN ('paid','shipped','completed')
      AND paid_amount >= 70
    ) as mr_plus_70
    FROM orders
    WHERE MONTH(paid_at) = 11
    AND YEAR(paid_at) = 2011
    AND state IN ('paid','shipped','completed')
    GROUP BY day;

知道我可能做错了什么或者我可以做得更好吗?我还有其他类似长度的查询不需要像这个一样多的时间来加载。我认为这会比每天单独查询(在我的编程中而不是 SQL 查询中)更快。

【问题讨论】:

  • 请发布查询的解释计划。

标签: mysql sql performance subquery


【解决方案1】:

这是因为您使用了不需要的子查询。

作为一般规则,如果您在主 SELECT 子句中有一个子查询,则该子查询将为主 SELECT 子句中的每一行查询其中的表一次 - 因此,如果您有 7 个子查询并且正在选择日期范围为 30 天,您将有效地运行 210 个单独的子查询(加上您的主查询)。

(在某些情况下,一些查询优化器可以将子查询解析为主查询,但作为一般规则,您不能依赖此。)

在这种情况下,您不需要任何 orders 子查询,因为您需要的所有 orders 数据都包含在主查询中 - 因此您可以将其重写为:

SELECT
    DATE(paid_at) AS day,
    COUNT(*) as order_count,
    (
      SELECT COUNT(*) FROM line_items
      WHERE order_id IN (SELECT id from orders WHERE DATE(paid_at) = day)
    ) as product_count,
    sum(case when shipping_method = 'colissimo' then 1 end) as orders_co,
    sum(case when shipping_method = 'colissimo' AND 
                  paid_amount < 70 then 1 end) as co_less_70,
    sum(case when shipping_method = 'colissimo' AND 
                  paid_amount >= 70 then 1 end) as co_plus_70,
    sum(case when shipping_method = 'mondial_relais' then 1 end) as orders_mr,
    sum(case when shipping_method = 'mondial_relais' AND 
                  paid_amount < 70 then 1 end) as mr_less_70,
    sum(case when shipping_method = 'mondial_relais' AND 
                  paid_amount >= 70 then 1 end) as mr_plus_70
    FROM orders
    WHERE MONTH(paid_at) = 11
    AND YEAR(paid_at) = 2011
    AND state IN ('paid','shipped','completed')
    GROUP BY day;

【讨论】:

    【解决方案2】:

    您的查询中的问题是一遍又一遍地扫描同一个表。 ORDER 表的所有扫描(在您的情况下选择)都可以转换为多个 SUM+CASE 或 COUNT+CASE,如SQL query with count and case statement

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 2014-11-24
      • 2018-02-01
      • 1970-01-01
      • 2018-05-31
      相关资源
      最近更新 更多