在列中的日期值之后查找下一个日期 (SQL Server 2008)
Find next date after date value in column (SQL Server 2008)
我正在尝试使用 2 个表 Orders
和 Repairs
构建查询。目标是找到任何特定客户在订单日期之后的第一个维修日期,如果订单之间没有维修,则为 'null'。以下是每个的当前列:
CustomerID LocationID SaleDate
-------------------------------
1 1b 1/10/2019
1 1b 2/23/2019
1 1c 1/29/2019
2 2a 3/01/2019
2 2a 3/25/2019
CustomerID LocationID RepairDate
--------------------------------
1 1b 2/25/2019
1 1c 2/13/2019
1 1c 2/27/2019
2 2a 3/03/2019
2 2a 3/17/2019
这是预期的结果。有任何想法吗?出于某种原因,我尝试过的任何东西似乎都不起作用
CustomerID LocationID SaleDate RepairDate
-------------------------------------------
1 1b 1/10/2019 null
1 1b 2/23/2019 2/25/2019
1 1c 1/29/2019 2/13/2019
2 2a 3/1/2019 3/3/2019
2 2a 3/25/2019 null
使用outer apply
:
select o.*, r.*
from orders o outer apply
(select top (1) r.*
from repairs r
where r.customerId = o.customerId and
r.locationid = o.locationid and
r.RepairDate > o.SaleDate
order by r.RepairDate
) r;
注意:您指定想要客户的第一个维修日期。但是,数据表明您真正指的是 customer/location 组合。
编辑:
感谢您设置 SQL Fiddle。这就说明了问题。我认为这个调整可以满足您的需求:
-- 戈登·利诺夫的回答
select o.*, r.*
from (select o.*,
lead(saledate) over (partition by customerid order by saledate) as next_salesdate
from orders o
) o outer apply
(select top (1) r.*
from repairs r
where r.customerId = o.customerId and
r.locationid = o.locationid and
r.RepairDate > o.SaleDate and
(r.RepairDate < o.next_salesdate or o.next_salesdate is null)
order by r.RepairDate
) r;
Here 是 db<>fiddle.
这有点啰嗦,但这是我想到的最好的方法,可以防止相同的维修与之前的所有销售相匹配,我认为这是 Gordon Linoff 简单得多的答案的一个问题。我已将您的两个表联合到一个事件日志中,然后使用 row_number window 函数来识别影响同一客户和位置的事件序列。试一试here
WITH eventlog
AS (SELECT *,
Row_number()
OVER(
partition BY customerid, locationid
ORDER BY eventdate) AS seq
FROM (SELECT customerid,
locationid,
saledate AS eventdate,
's' AS type
FROM orders
UNION
SELECT customerid,
locationid,
repairdate AS repairdate,
'r' AS type
FROM repairs) a)
SELECT s.customerid,
s.locationid,
s.eventdate AS saledate,
r.eventdate AS repairdate
FROM eventlog s
LEFT JOIN eventlog r
ON s.seq + 1 = r.seq
AND s.customerid = r.customerid
AND s.locationid = r.locationid
AND r.type = 'r'
WHERE s.type = 's';
我正在尝试使用 2 个表 Orders
和 Repairs
构建查询。目标是找到任何特定客户在订单日期之后的第一个维修日期,如果订单之间没有维修,则为 'null'。以下是每个的当前列:
CustomerID LocationID SaleDate
-------------------------------
1 1b 1/10/2019
1 1b 2/23/2019
1 1c 1/29/2019
2 2a 3/01/2019
2 2a 3/25/2019
CustomerID LocationID RepairDate
--------------------------------
1 1b 2/25/2019
1 1c 2/13/2019
1 1c 2/27/2019
2 2a 3/03/2019
2 2a 3/17/2019
这是预期的结果。有任何想法吗?出于某种原因,我尝试过的任何东西似乎都不起作用
CustomerID LocationID SaleDate RepairDate
-------------------------------------------
1 1b 1/10/2019 null
1 1b 2/23/2019 2/25/2019
1 1c 1/29/2019 2/13/2019
2 2a 3/1/2019 3/3/2019
2 2a 3/25/2019 null
使用outer apply
:
select o.*, r.*
from orders o outer apply
(select top (1) r.*
from repairs r
where r.customerId = o.customerId and
r.locationid = o.locationid and
r.RepairDate > o.SaleDate
order by r.RepairDate
) r;
注意:您指定想要客户的第一个维修日期。但是,数据表明您真正指的是 customer/location 组合。
编辑:
感谢您设置 SQL Fiddle。这就说明了问题。我认为这个调整可以满足您的需求:
-- 戈登·利诺夫的回答
select o.*, r.*
from (select o.*,
lead(saledate) over (partition by customerid order by saledate) as next_salesdate
from orders o
) o outer apply
(select top (1) r.*
from repairs r
where r.customerId = o.customerId and
r.locationid = o.locationid and
r.RepairDate > o.SaleDate and
(r.RepairDate < o.next_salesdate or o.next_salesdate is null)
order by r.RepairDate
) r;
Here 是 db<>fiddle.
这有点啰嗦,但这是我想到的最好的方法,可以防止相同的维修与之前的所有销售相匹配,我认为这是 Gordon Linoff 简单得多的答案的一个问题。我已将您的两个表联合到一个事件日志中,然后使用 row_number window 函数来识别影响同一客户和位置的事件序列。试一试here
WITH eventlog
AS (SELECT *,
Row_number()
OVER(
partition BY customerid, locationid
ORDER BY eventdate) AS seq
FROM (SELECT customerid,
locationid,
saledate AS eventdate,
's' AS type
FROM orders
UNION
SELECT customerid,
locationid,
repairdate AS repairdate,
'r' AS type
FROM repairs) a)
SELECT s.customerid,
s.locationid,
s.eventdate AS saledate,
r.eventdate AS repairdate
FROM eventlog s
LEFT JOIN eventlog r
ON s.seq + 1 = r.seq
AND s.customerid = r.customerid
AND s.locationid = r.locationid
AND r.type = 'r'
WHERE s.type = 's';