查询优化,(子查询)(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)的主要内容,如果未能解决你的问题,请参考以下文章