我想选择拥有10件以上产品且年龄超过50件的员工。我也希望选择最后一件产品。我使用以下查询:
SELECT
PE.EmployeeID, E.Name, E.Age,
COUNT(*) as ProductCount,
(SELECT TOP(1) xP.Name
FROM ProductEmployee xPE
INNER JOIN Product xP ON xPE.ProductID = xP.ID
WHERE xPE.EmployeeID = PE.EmployeeID
AND xPE.Date = MAX(PE.Date)) as LastProductName
FROM
ProductEmployee PE
INNER JOIN
Employee E ON PE.EmployeeID = E.ID
WHERE
E.Age > 50
GROUP BY
PE.EmployeeID, E.Name, E.Age
HAVING
COUNT(*) > 10
以下是执行计划链接:https://www.dropbox.com/s/rlp3bx10ty3c1mf/ximExPlan.sqlplan?dl=0
然而,执行它需要太多时间。它有什么问题?是否可以提高查询效率?
我有一个限制 - 我不能使用CTE。我相信它无论如何也不会带来性能。
答案 0 :(得分:2)
在创建索引之前,我相信我们可以重新构建查询。
您的查询可以像这样重写
SELECT E.ID,
E.NAME,
E.Age,
CS.ProductCount,
CS.LastProductName
FROM Employee E
CROSS apply(SELECT TOP 1 P.NAME AS LastProductName,
ProductCount
FROM (SELECT *,
Count(1)OVER(partition BY EmployeeID) AS ProductCount -- to find product count for each employee
FROM ProductEmployee PE
WHERE PE.EmployeeID = E.Id) PE
JOIN Product P
ON PE.ProductID = P.ID
WHERE ProductCount > 10 -- to filter the employees who is having more than 10 products
ORDER BY date DESC) CS -- To find the latest sold product
WHERE age > 50
答案 1 :(得分:1)
这应该有效:
SELECT *
FROM Employee AS E
INNER JOIN (
SELECT PE.EmployeeID
FROM ProductEmployee AS PE
GROUP BY PE.EmployeeID
HAVING COUNT(*) > 10
) AS PE
ON PE.EmployeeID = E.ID
CROSS APPLY (
SELECT TOP (1) P.*
FROM Product AS P
INNER JOIN ProductEmployee AS PE2
ON PE2.ProductID = P.ID
WHERE PE2.EmployeeID = E.ID
ORDER BY PE2.Date DESC
) AS P
WHERE E.Age > 50;
正确的索引应该加快查询速度
您按Age
进行过滤,因此关注一个应该有所帮助:
CREATE INDEX ix_Person_Age_Name
ON Person (Age, Name);
应首先计算查找超过10条记录的员工的子查询,CROSS APPLY
应使用TOP
运算符更有效地恢复数据,而不是将其与MAX值进行比较。
@Prdp回答很好,但我想我会放弃一个替代方案。有时窗口函数不能很好地工作,用ol'good子查询替换它们是值得的。
此外,请勿使用datetime
,请使用datetime2
。这是微软的建议:
https://msdn.microsoft.com/en-us/library/ms187819.aspx
使用时间,日期, datetime2 和 datetimeoffset 数据 新工作的类型。这些类型与SQL标准一致。他们是 更便携。 时间, datetime2 和 datetimeoffset 提供 更精确的秒数。 datetimeoffset 提供时区支持 用于全球部署的应用程序。
顺便说一下,这是一个提示。尝试在表格后命名您的代理主键,以便它们变得更有意义,并且连接感觉更自然。即:
ID
替换为EmployeeID
ID
替换为ProductID
我发现这是一个很好的做法。
答案 2 :(得分:0)
with usersOver50with10productsOrMore (employeeID, productID, date, id, name, age, products ) as (
select employeeID, productID, date, id, name, age, count(productID) from productEmployee
join employee on productEmployee.employeeID = employee.id
where age >= 50
group by employeeID, productID, date, id, name, age
having count(productID) >= 10
)
select sfq.name, sfq.age, pro.name, sfq.products, max(date) from usersOver50with10productsOrMore as sfq
join product pro on sfq.productID = pro.id
group by sfq.name, sfq.age, pro.name, sfq.products
;
无需为整个表找到最后一个产品ID,只需填写10个或更多产品和50岁以上员工的最后一个产品。