查询优化,(子查询)(sql-transact)

Posted

技术标签:

【中文标题】查询优化,(子查询)(sql-transact)【英文标题】:Query Optimization, (subquery) (sql-transact) 【发布时间】:2014-02-13 20:26:38 【问题描述】:

这是我必须优化的代码,我设法仅使用索引并将其更改为 SUBSTRING 语句的 LIKE 将其降低到起始成本的一半。我现在的问题是最后一行中的子查询和选择中的 SUM 行,我相信我必须通过创建新表或列来摆脱这些但无法完成。

SELECT 
C.LastName as Customer , e.LastName as SalesPerson, s.ProductID,
p.Name as ProductName, SUM( s.Quantity ) as quantity, 
SUM ( p.Price * s.Quantity ) as amount 
FROM dbo.Sales s, dbo.Customers c, dbo.Employees e, dbo.Products p 
WHERE 
 s.CustomerID = c.CustomerID and 
 s.ProductID = p.ProductID and 
 s.SalesPersonID = e.EmployeeID and
 p.Name like 'Paint%' 
GROUP BY C.LastName , e.LastName , s.ProductID, p.Name 
HAVING sum ( s.Quantity ) < 
(select AVG(s2.Quantity) from dbo.Sales s2 where s2.ProductID=s.ProductID ) 

欢迎任何帮助,在此先感谢。

【问题讨论】:

你用EXPLAIN查看优化方案了吗?不幸的是, HAVING sum() 可能会迫使您阅读很多行。数据重新设计是一种选择吗?是否可以确保此查询很少运行(例如使用 cron 作业)并缓存在文本文件或 sales_volume 表之类的地方? 你用的是什么数据库? @TomHaws 你的意思是执行?是的,我做到了,就像你说的那样,HAVING 和子查询给了我整个估计成本的 70%。允许重新设计。它是 SQL Server。 我越看这个,越像是一个真正复杂的查询,可能不需要最新的货币。如果需求非常频繁,我认为我的首选解决方案是提供定期缓存的结果。 【参考方案1】:

目前无法测试,但我认为这应该可行:

SELECT c.LastName as Customer , 
       e.LastName as SalesPerson, 
       s.ProductID,
       p.Name as ProductName, 
       SUM(s.Quantity) as quantity, 
       SUM(p.Price * s.Quantity) as amount 
  FROM dbo.Sales s, 
  JOIN dbo.Customers c
    ON c.CustomerID = s.CustomerID 
  JOIN dbo.Employees e
    ON e.EmployeeID = s.SalesPersonID
  JOIN dbo.Products p 
    ON p.ProductID = s.ProductID
   AND p.Name like 'Paint%'
  JOIN (SELECT ProductID,
               AVG(Quantity) as avg_Quantity
          FROM dbo.Sales) s2 
    ON s2.ProductID = s.ProductID
 GROUP BY c.LastName , e.LastName , s.ProductID, p.Name 
HAVING sum(s.Quantity) <  AVG(s2.avg_Quantity) 

【讨论】:

其实我觉得你可以把 AVG(s2.avg_Quantity) 换成 Min(s2.avg_Quantity);效果应该是一样的,但我猜 Min() 会比 AVG() 快(一小部分)。 (未经测试,只是大声思考,但我猜想实现 AVG() 需要更多逻辑)【参考方案2】:

如果使用 SQL Server,您可以使用窗口函数稍微简化一下:

WITH cte AS (   SELECT  C.LastName AS Customer
                      , e.LastName AS SalesPerson
                      , s.ProductID
                      , p.Name AS ProductName
                      , SUM(s.Quantity) AS quantity
                      , SUM(p.Price * s.Quantity) AS amount
                      , SUM(SUM(s.Quantity)) OVER (PARTITION BY s.ProductID)*1.0/SUM(COUNT(*)) OVER (PARTITION BY s.ProductID) AS Avg_Qty
                FROM     dbo.Sales s
                    JOIN dbo.Customers c ON s.CustomerID = c.CustomerID
                    JOIN dbo.Employees e ON s.SalesPersonID = e.EmployeeID
                    JOIN dbo.Products p ON s.ProductID = p.ProductID
                WHERE   s.Name LIKE 'Paint%'
                GROUP BY C.LastName
                      , e.LastName
                      , s.ProductID
                      , p.Name
            )
SELECT * 
FROM  cte
WHERE quantity < Avg_Qty

添加Name_Category 字段或类似字段以避免使用LIKE 会有所帮助。

【讨论】:

【参考方案3】:

这等于你的陈述,但要快得多:

SELECT 
C.LastName as Customer , e.LastName as SalesPerson, s.ProductID,
p.Name as ProductName, SUM( s.Quantity ) as quantity, 
SUM ( p.Price * s.Quantity ) as amount 
FROM dbo.Sales s
INNER JOIN dbo.Products p ON s.ProductID = p.ProductID /*AND p.Name like 'Paint%' --optionally place filter here if you like*/
INNER JOIN (SELECT s2.ProductID SalesProductID, AVG(s2.Quantity)AVG_Quantity FROM dbo.Sales s2 GROUP BY s2.ProductID)ProductSales ON ProductSales.SalesProductID = s.ProductID
INNER JOIN dbo.Customers c ON s.CustomerID = c.CustomerID
INNER JOIN dbo.Employees e ON s.SalesPersonID = e.EmployeeID
WHERE p.Name like 'Paint%' 
GROUP BY C.LastName , e.LastName , s.ProductID, p.Name 

【讨论】:

以上是关于查询优化,(子查询)(sql-transact)的主要内容,如果未能解决你的问题,请参考以下文章

MySQL---查询性能优化

mysql中,如何向测试人员介绍连接查询和子查询的优劣势?

oracle 优化时间查询

Oracle大量数据查询优化

012:子查询和增删改查

简单的记录一次简单的优化