更好的 tsql 查询来确定谁是新客户(或 NOT EXISTS 的替代方案)
Better tsql query to figure out who's a new customer (or alternative to NOT EXISTS)
我有一个 table 包含我们所有的订单。订单链接到一个人。每个人都隶属于一家公司。现在我需要一份在特定日期之前从未订购过的所有公司的清单。查询工作正常,但速度很慢。
这是我的 T-SQL 查询:
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')
我已经将它从 NOT IN 更改为 NOT EXISTS。因为这是这里大多数人推荐的。没有太大帮助。更好的索引稍微改善了这种情况,但查询仍然很慢。我认为这是因为对于每个订单它都必须执行子查询。
这是执行计划:
https://www.brentozar.com/pastetheplan/?id=SyAlAU3db
为简单起见,我在上面的示例中删除了一些 WHERE 子句)
查询在 Azure SQL(和 SQL Server Express 12 用于开发)上运行
有人对如何解决这个问题有更好的想法吗?
我想这样就可以了(哎呀错过了还没有订购)
;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
如果有执行计划可以分享,对性能分析会有帮助。
我对查询进行了如下修改,如果改进了可以试试
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
这个怎么样。希望我正确理解了任务。
(
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'
)
也许这会对你有所帮助:
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';
我有一个 table 包含我们所有的订单。订单链接到一个人。每个人都隶属于一家公司。现在我需要一份在特定日期之前从未订购过的所有公司的清单。查询工作正常,但速度很慢。
这是我的 T-SQL 查询:
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')
我已经将它从 NOT IN 更改为 NOT EXISTS。因为这是这里大多数人推荐的。没有太大帮助。更好的索引稍微改善了这种情况,但查询仍然很慢。我认为这是因为对于每个订单它都必须执行子查询。
这是执行计划:
https://www.brentozar.com/pastetheplan/?id=SyAlAU3db
为简单起见,我在上面的示例中删除了一些 WHERE 子句)
查询在 Azure SQL(和 SQL Server Express 12 用于开发)上运行
有人对如何解决这个问题有更好的想法吗?
我想这样就可以了(哎呀错过了还没有订购)
;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
如果有执行计划可以分享,对性能分析会有帮助。
我对查询进行了如下修改,如果改进了可以试试
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
这个怎么样。希望我正确理解了任务。
(
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'
)
也许这会对你有所帮助:
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';