简体   繁体   中英

Selecting records from slowly changing table with a set of dates

I have a slowly changing table,a new row is created each time any of the source fields are changed. Some metadata is added to show when that version was valid. This is a simplified example(dates are dd/mm/yyyy format) that doesn't show the fields which have changed.

Startdate Enddate Currentrecord unique id serial_number
15/12/2020 31/12/2020 0 1 2345
15/12/2020 8/3/2021 0 2 1234
19/9/2020 15/2/2021 0 3 2345
15/12/2020 8/3/2021 0 4 3456
9/3/2021 10/3/2021 0 5 3456
16/2/2021 10/3/2021 0 6 2345
9/3/2021 26/3/2021 0 7 1234
27/3/2021 2/5/2021 0 8 1234
11/3/2021 17/5/2021 0 9 3456
3/3/2021 27/4/2021 0 10 4567
20/1/2021 7/4/2021 0 11 5678
3/5/2021 30/6/2021 1 12 1234
25/5/2021 31/5/2021 0 13 2345
8/4/2021 22/5/2021 0 14 5678
1/6/2021 26/6/2021 0 15 2345
18/5/2021 3/6/2021 0 16 3456
27/6/2021 2/8/2021 0 17 2345
28/4/2021 28/6/2021 0 18 4567
23/5/2021 6/9/2021 0 19 5678
4/6/2021 28/6/2021 0 20 3456
29/6/2021 25/7/2021 0 21 3456
3/8/2021 31/12/9999 1 22 2345
26/7/2021 31/12/9999 1 23 3456
15/10/2021 31/12/9999 1 24 4567
7/9/2021 1/11/2021 0 25 5678
22/9/2021 10/11/2021 0 26 6789
2/11/2021 16/11/2021 0 27 5678
17/11/2021 21/11/2021 0 28 5678
15/7/2021 31/12/9999 1 29 7891
22/11/2021 31/12/9999 1 30 5678
26/11/2021 31/12/9999 1 31 6789
15/6/2021 31/12/9999 1 32 8912

There is only one record for each serial_number for any given point in time (ie the dates ranges will not overlap for identical serial_numbers) but there might be gaps between episodes for a some serial_numbers (representing something leaving and returning after a gap in service).

I want to supply an arbitrary list of datetimes, say midnight on 01/01/2021, 15/03/2021, 27/05/2021. 23/10/2021. I want to return a set of records, containing every record which was in effect on each of the dates, with each row labelled with the date it was selected by. So the above example should return this.

date unique id serial_number
1/1/2021 2 1234
1/1/2021 3 2345
1/1/2021 4 3456
15/3/2021 7 1234
15/3/2021 9 3456
15/3/2021 10 4567
15/3/2021 11 5678
27/5/2021 12 1234
27/5/2021 13 2345
27/5/2021 16 3456
27/5/2021 18 4567
27/5/2021 19 5678
23/10/2021 22 2345
23/10/2021 23 3456
23/10/2021 24 4567
23/10/2021 25 5678
23/10/2021 26 6789
23/10/2021 29 7891
23/10/2021 32 8912

I can see how to do this with a cursor, stepping through each date putting them into a variable and using something like

select @date, [unique id], serial_number
from example
where @date between start_date and end_date

to get the rows.

I can't work out a pattern that would do it in a set based approach. My preferred SQL version is TSQL. Sorry as this is almost certainly a repeat, but I can't find a form of words that hits a worked example.

You can use a temporary table to accomplish this.

CREATE TABLE #RequestedDates([Date] DATE)

You insert your dates you want into a temporary table.

INSERT INTO #RequestedDates([Date])
VALUES ('2021-01-01'), ('2021-03-15') /*Other dates*/

And then you join with the temporary table and use the between clause to get the valid results.

SELECT rd.[Date]
  , t.UniqueId
  , t.SerialNumber
FROM MyTable t
INNER JOIN #RequestedDates rd on rd.[Date] BETWEEN t.StartDate AND t.EndDate
ORDER BY rd.[Date]
  , t.UniqueId
  , t.SerialNumber

You can join to VALUES with the dates you need.
Then join the datetimes on the range.

SELECT 
  datetimes.dt as [date] 
, t.[unique id] 
, t.serial_number
FROM example t
JOIN (VALUES
 (cast('2021-01-01 00:00:00' as datetime)), 
 ('2021-03-15 00:00:00'), 
 ('2021-05-27 00:00:00'), 
 ('2021-10-23 00:00:00')
) datetimes(dt) 
  ON datetimes.dt >= t.start_date
 AND datetimes.dt <= t.end_date
ORDER BY datetimes.dt, t.[unique id], t.serial_number 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM