簡體   English   中英

如何構造涉及缺少日期范圍的T-SQL查詢?

[英]How can I contstruct this T-SQL query involving missing date ranges?

我將嘗試使我的問題的具體細節不在此問題之內,而僅專注於相關問題。

可以說我有一個Assets表,其主鍵為AssetID
我還有一個名為ProcessedDates表,該表具有主鍵PID和其他列AssetIDStartDateEndDate
我想為開始日期和結束日期之間的資產列表運行一個過程。
在運行此過程之前,我需要知道哪些資產和哪些日期范圍已被處理。

例如,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

從這里我不知道如何進行。 如何獲取兩者之間的所有間隔的AssetIDStartDateEndDate

像這樣:

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 NULLISNULL(... @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

這是SqlFiddle演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM