带有子查询的 SQL 查询执行得很糟糕

发布于 2024-12-17 16:52:53 字数 1754 浏览 0 评论 0原文

我有一个很长的查询,它应该给我一些有关发货的信息,它有效,但它的性能非常糟糕。加载大约需要 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 查询中)要快。

I have this quite long query that should give me some information about shipments, and it works, but it's performing terribly bad. It takes about 4500ms to load.

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;

Any idea what I could be doing wrong or what I could be doing better? I have other queries of similar length that don't take as much time to load as this. I thought this would be faster than for example having an individual query for each day (in my programming instead of the SQL query).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夏日浅笑〃 2024-12-24 16:52:53

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

作为一般规则,如果主 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;

It is because you are using sub-queries where you don't need them.

As a general rule, where you have a sub-query within a main SELECT clause, that sub-query will query the tables within it once for each row in the main SELECT clause - so if you have 7 subqueries and are selecting a date range of 30 days, you will effectively be running 210 separate subqueries (plus your main query).

(Some query optimisers can resolve sub-queries into the main query under some circumstances, but as a general rule you can't rely on this.)

In this case, you don't need any of the orders sub-queries, because all the orders data you require is included in the main query - so you can rewrite this as:

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;
怪我太投入 2024-12-24 16:52:53

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

The problem in your query is that scans the same table over and over. All scans (selects in your case) of ORDER table can be transformed to multiple SUM+CASE or COUNT+CASE as in SQL query with count and case statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文