[英]How can I contstruct this T-SQL query involving missing date ranges?
我將嘗試使我的問題的具體細節不在此問題之內,而僅專注於相關問題。
可以說我有一個Assets
表,其主鍵為AssetID
。
我還有一個名為ProcessedDates
表,該表具有主鍵PID
和其他列AssetID
, StartDate
, EndDate
。
我想為開始日期和結束日期之間的資產列表運行一個過程。
在運行此過程之前,我需要知道哪些資產和哪些日期范圍已被處理。
例如,ProcessedDates中有2個條目:
AssetID StartDate EndDate
--------------------------
Asset1 Day4 day7
Asset1 Day10 Day12
我想在第2天到第11天之間處理Asset1。 我不需要在已經完成的工作日上浪費時間,因此在此示例中,我將只從day2到day3和day8到day 9處理asset1。
因此,我需要的是一個查詢,該查詢返回日期范圍內的差距。 在這種情況下,結果集將為2行:
AssetID StartDate EndDate
--------------------------
Asset1 day2 day3
Asset1 day8 day9
在我的實際需求中,我有很多assetID。 ProcessedDates
表可能對每個資產有多個條目,或者根本沒有任何條目,並且每個資產不一定具有與任何其他資產相同的處理日期。
declare @StartDate date, @EndDate date (assume these are given)
--get distinct assets
select distinct AssetIDs from (some query) into #Assets
--get the already processed date ranges
select p.AssetID, p.StartDate, p.EndDate
from ProcessedDates p inner join #Assets a on p.AssetID = a.AssetID
where p.StartDate between @StartDate and @EndDate
or p.EndDate between @StartDate and @EndDate
從這里我不知道如何進行。 如何獲取兩者之間的所有間隔的AssetID
, StartDate
和EndDate
?
像這樣:
declare @StartDate date = '2015-01-01', @EndDate date = '2015-05-05'
declare @Assets table (AssetID varchar(50), StartDate date, EndDate date)
declare @AssetTypes table (AssetID varchar(50))
insert into @AssetTypes values
('Asset1'),
('Asset2')
insert into @Assets values
('Asset1', '2014-12-10', '2014-12-31'), -- Ignored
('Asset1', '2015-02-02', '2015-03-02'),
('Asset1', '2015-03-05', '2015-05-01'),
('Asset1', '2015-06-01', '2015-06-06') -- Ignored
;WITH Base AS (
SELECT AT.AssetID
, CASE WHEN A.AssetID IS NULL THEN 1 ELSE 0 END EmptyAsset
, A.StartDate
, A.EndDate
, ROW_NUMBER() OVER (PARTITION BY AT.AssetID ORDER BY StartDate) RN
FROM @AssetTypes AT
LEFT JOIN @Assets A ON A.AssetID = AT.AssetID
WHERE A.AssetID IS NULL -- case of totally missing asset
OR (StartDate <= @EndDate AND EndDate >= @StartDate)
)
-- first missing range, before the first row
SELECT AssetID, @StartDate StartDate, DATEADD(dd, -1, StartDate) EndDate
FROM Base
WHERE RN = 1 AND StartDate > @StartDate
UNION ALL
-- each row joined with the next one
SELECT B1.AssetID, DATEADD(dd, 1, B1.EndDate), ISNULL(DATEADD(dd, -1, B2.StartDate), @EndDate)
FROM Base B1
LEFT JOIN Base B2 ON B2.AssetID = B1.AssetID AND B2.RN = B1.RN + 1
WHERE B1.EmptyAsset = 0
AND (B2.AssetID IS NULL -- Last row case
OR DATEADD(dd, 1, B1.EndDate) < B2.StartDate) -- Other rows case
AND B1.EndDate < @EndDate -- If the range ends after @EndDate, nothing to do
UNION ALL
-- case of totally missing asset
SELECT AssetID, @StartDate, @EndDate
FROM Base
WHERE EmptyAsset = 1
主要思想是每一行都與下一行相連。 在EndDate + 1和StartDate-1之間會生成一個新范圍(如有必要)。最后一行( B2.AssetID IS NULL
和ISNULL(... @EndDate)
)有特殊處理。 第一個SELECT
在第一個范圍之前生成一行,最后一個選擇是針對資產不存在范圍的特殊情況。
正如我在評論中所寫,它很快變得很難看。
這是獲得所需結果的簡單版本。 我使用整數作為日期,並假設最小日期為0,最大日期為999。
--DDL
create table Assets (AssetID integer, StartDate integer, EndDate integer);
insert into Assets values
(1,4,7),
(1,10,12),
(1,15,17),
(2,5,7),
(2,9,10);
with temp as(
select a1.AssetId,
a1.enddate+1 as StartDate,
coalesce(min(a2.startdate) - 1,999) as EndDate
from Assets a1
left join Assets a2
on a1.assetid = a2.assetid
and a1.enddate < a2.startdate
group by a1.assetid,a1.enddate
union all
select a.assetid,0,min(startdate) -1
from Assets a
group by a.assetid
)
select AssetId,
case when StartDate<2 then 2 else StartDate end as StartDate,
case when EndDate>11 then 11 else EndDate end as EndDate
from temp
where StartDate<=11 and EndDate>=2
order by AssetId,StartDate
臨時表可以獲得缺少的范圍。 然后過濾Day2和Day11之間的匹配范圍,將獲得所需的結果。
AssetId StartDate EndDate
1 2 3
1 8 9
2 2 4
2 8 8
2 11 11
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.