Reputation: 12703
I have an table with all our orders. The orders are linked to a person. And every person is attached to a company. Now I need a list of all companies have never ordered before a certain date. The query works fine, but it's very slow.
This is my T-SQL query:
SELECT
DISTINCT p1.company_id
FROM
order o
JOIN person p1 ON (o.person_id = p1.id AND p1.company_id IS NOT NULL)
WHERE
o.orderDate > '2017-01-01'
AND
o.orderDate < '2017-09-01'
AND NOT EXISTS (SELECT
p2.company_id
FROM
order o2
JOIN person p2 ON (o2.person_id = p2.id AND p2.company_id = p1.company_id)
WHERE
o2.orderDate < '2017-01-01')
I've already changed it from a NOT IN to a NOT EXISTS. Since this was what most people here recommended. Didn't help much. A better index improved the situation a bit, but the query is still slow. I assume it's because for every order it has to do the sub-query.
This is the execution plan:
https://www.brentozar.com/pastetheplan/?id=SyAlAU3db
For simplicity reasons I removed a few WHERE clauses in my example above)
The query runs on Azure SQL (and SQL Server Express 12 for development)
Anyone has a better idea on how to solve this?
Upvotes: 2
Views: 92
Reputation: 1981
May be this will help you:
WITH cte AS
(
SELECT o.person_id, MIN(o.orderDate) minOrderDate
FROM order o
GROUP BY o.person_id
)
SELECT DISTINCT p1.company_id
FROM person p1
JOIN cte ON cte.person_id = p1.id
WHERE p1.company_id IS NOT NULL AND cte.minOrderDate > '2017-01-01' AND cte.minOrderDate < '2017-09-01';
Upvotes: 0
Reputation: 458
What's about this one. Hope I understood the task correctly.
(
SELECT p1.company_id
FROM order o
JOIN person p1
ON o.person_id = p1.id
WHERE p1.company_id IS NOT NULL
AND o.orderDate > '2017-01-01'
AND o.orderDate < '2017-09-01'
)
EXCEPT
(
SELECT p2.company_id
FROM order o2
JOIN person p2
ON o2.person_id = p2.id
WHERE p2.company_id IS NOT NULL
AND o2.orderDate < '2017-01-01'
)
Upvotes: 1
Reputation: 379
I think this would do it (oops missed the have not ordered)
;With FirstOrders
as
(
Select p1.company_id
, MIN(o.orderDate) as FirstCompanyOrder
From Orders o
Join Person P1 on o.person_id = p1.id
Group by P1.Company_id
Having MIN(o.OrderDate) < '2017-01-01'
)
Select distinct o.company_id
From Orders o
Left join FirstOrders FO on o.Company_id = FO.ComapnyId
where FO.company_id is null
Upvotes: 0
Reputation: 6205
If you have execution plan to share, it will help on performance analyse.
I made some change on query as below, you may try if that improve it
SELECT p1.company_id
FROM order o
INNER JOIN person p1
ON (o.person_id = p1.id AND p1.company_id IS NOT NULL)
GROUP BY p1.company_id
HAVING SUM(CASE WHEN (o.orderDate > '2017-01-01' AND o.orderDate < '2017-09-01') THEN 1 ELSE 0 END) > 0
AND
SUM(CASE WHEN orderDate < '2017-01-01' THEN 1 ELSE 0 END) = 0
Upvotes: 1