【问题标题】:Optimize SQL Query that calculates Subtotal, Discount and Total per Product优化计算每个产品的小计、折扣和总计的 SQL 查询
【发布时间】:2018-03-28 04:55:55
【问题描述】:

我有一个查询,显示每个产品的销售量、小计和总销售量的总和。问题是执行时间太长。我做错了吗?我需要您的帮助来优化查询。另外,我应该按 sd.Product_Id 还是 p.Product_Name 分组才可以?

SELECT p.Product_Name   `Product Name`,
       SUM(sd.Quantity) `Quantity Sold`,
       SUM(sd.Subtotal) `Subtotal`,
       SUM(sd.Discount_Value) `Total Discount`,
       SUM(sd.Total)    `Total`
FROM Sales_Detail sd
JOIN Product p ON sd.Product_Id = p.Id
WHERE DATE(sd.Purchase_Date) BETWEEN DATE('2017-08-21')
                                 and date('2017-08-25')
GROUP BY p.Product_Name;

【问题讨论】:

  • 如果DATE(sd.Purchase_Date) 正在转换日期时间字段,请停止这样做。将您的操作数之间的操作数也指定为日期时间类型。
  • 您应该将其发布为答案,而不仅仅是评论。我的原始查询是 200 多秒。现在缩短到 3 秒!谢谢!
  • 但是返回的原始行是1091。修改后的查询只返回973。我仍然需要检查哪些行被删除了
  • 不客气,祝你好运。
  • 见:Why should I provide an MCVE for what seems to me to be a very simple SQL query?。此外,关于查询性能的问题也需要给定查询的 EXPLAIN。

标签: mysql sql group-by query-optimization


【解决方案1】:

确保您在 Sales_Detail.Purchase_Date 列上有一个索引,然后重写您的查询以删除该列周围的函数,以便它可以利用索引:

SELECT p.Product_Name `Product Name`, 
  SUM(sd.Quantity) `Quantity Sold`, 
  SUM(sd.Subtotal) `Subtotal`, 
  SUM(sd.Discount_Value) `Total Discount`, 
  SUM(sd.Total) `Total`
FROM Sales_Detail sd
JOIN Product p ON sd.Product_Id = p.Id
WHERE sd.Purchase_Date >= '2017-08-21' 
  AND sd.Purchase_Date < '2017-08-26'
GROUP BY p.Product_Name;

【讨论】:

  • 感谢您的帮助!只是一个菜鸟问题,我是否应该担心 where 语句中没有 00:00:00 的日期?
  • 如果Purchase_DateDATE,您已经排除了整个第一天。
  • @HarambeAttackHelicopter - 日期时间文字可以包含或省略 00:00:00 - 没有区别。即使与DATEDATETIME 相比也是如此。
  • @Ike Walker 请问如果 Sales_Detail.Purchase_Date 没有被索引会发生什么?还有为什么 >= AND
  • 如果列没有被索引,那么 MySQL 会做一个表扫描,这很慢。我假设Purchase_DateDATETIMETIMESTAMP,所以我想包含2017-08-21 00:00:002017-08-25 23:59:59 之间的值,我个人觉得像我一样编写它更简单、更易读。
【解决方案2】:

sd 需要INDEX(Purchase_Date) 重写:

WHERE sd.Purchase_Date >= '2017-08-21'
      sd.Purchase_Date  < '2017-08-26'  -- next day

这适用于任何类型的日期/日期时间/等。

这可能会更好,因为它避免了计算闰年等:

WHERE sd.Purchase_Date >= '2017-08-21'
      sd.Purchase_Date  < '2017-08-21' + INTERVAL 5 DAY

另外,考虑将GROUP BY 更改为

GROUP BY sd.Product_Id

因为它应该是相同的。然后,有

INDEX(Purchase_Date, Product_Id)

这将是“覆盖”,并且通过处理带有索引的GROUP BY(而不是随后的“文件排序”)可能会更优化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多