Effiziente Art der memoryung von datesbereichen

Ich muss einfache data speichern – Angenommen, ich habe einige Produkte mit Codes als Primärschlüssel, einige properties und Gültigkeitsbereiche. So können data so aussehen:

Products code value begin_date end_date 10905 13 2005-01-01 2016-12-31 10905 11 2016-01-01 null 

Diese Bereiche sind nicht überlappend, so dass bei jedem date habe ich eine list von einzigartigen Produkten und ihre properties. Also, um die Verwendung von ihm zu erleichtern Ich habe die function erstellt:

 create function dbo.f_Products ( @date date ) returns table as return ( select from dbo.Products as p where @date >= p.begin_date and @date <= p.end_date ) 

So werde ich es benutzen:

 select * from <some table with product codes> as t left join dbo.f_Products(@date) as p on p.code = t.product_code 

Das ist alles in Ordnung, aber wie kann ich Optimierer wissen lassen, dass diese Zeilen einzigartig sind, um einen besseren Ausführungsplan zu haben?

Ich habe einige googeln, und fand ein paar wirklich nette Artikel für DDL, die das memoryn von überlappenden Bereichen in der Tabelle verhindert:

  • Selbsthaltende, zusammenhängende wirksame data in timetabellen
  • memoryn von timeintervallen ohne Überschneidungen

Aber auch wenn ich diese Einschränkung ausprobiere, sehe ich, dass der Optimierer nicht verstehen kann, dass das resultierende Recordset einzigartige Codes zurückgibt.

Was ich gerne hätte, ist ein gewisser Ansatz, der mir im Grunde die gleiche performance gibt, als ob ich diese date = @date zu einem bestimmten date aufbewahrt und mit date = @date date ausgewählt date = @date .

Ich weiß, dass einige RDMBS (wie PostgreSQL) hierfür spezielle datatypen haben ( Range Types ). Aber SQL server hat so etwas nicht.

Bin ich etwas fehlt oder gibt es keine Möglichkeit, dies richtig in SQL server zu tun?

Eine Lösung ohne Lücken könnte dies sein:

 DECLARE @tbl TABLE(ID INT IDENTITY,[start_date] DATE); INSERT INTO @tbl VALUES({d'2016-10-01'}),({d'2016-09-01'}),({d'2016-08-01'}),({d'2016-07-01'}),({d'2016-06-01'}); SELECT * FROM @tbl; DECLARE @DateFilter DATE={d'2016-08-13'}; SELECT TOP 1 * FROM @tbl WHERE [start_date]<=@DateFilter ORDER BY [start_date] DESC 

Wichtig: Sei sicher, dass es einen (eindeutigen) Index auf start_date

UPDATE: für verschiedene Produkte

 DECLARE @tbl TABLE(ID INT IDENTITY,ProductID INT,[start_date] DATE); INSERT INTO @tbl VALUES --product 1 (1,{d'2016-10-01'}),(1,{d'2016-09-01'}),(1,{d'2016-08-01'}),(1,{d'2016-07-01'}),(1,{d'2016-06-01'}) --product 1 ,(2,{d'2016-10-17'}),(2,{d'2016-09-16'}),(2,{d'2016-08-15'}),(2,{d'2016-07-10'}),(2,{d'2016-06-11'}); DECLARE @DateFilter DATE={d'2016-08-13'}; WITH PartitionedCount AS ( SELECT ROW_NUMBER() OVER(PARTITION BY ProductID ORDER BY [start_date] DESC) AS Nr ,* FROM @tbl WHERE [start_date]<=@DateFilter ) SELECT * FROM PartitionedCount WHERE Nr=1