存储日期范围的有效方式

Rom*_*kar 8 sql sql-server date-range intervals sql-server-2016

我需要存储简单的数据 - 假设我有一些产品将代码作为主键,一些属性和有效范围.所以数据可能如下所示:

Products
code    value   begin_date  end_date
10905   13      2005-01-01  2016-12-31
10905   11      2017-01-01  null
Run Code Online (Sandbox Code Playgroud)

这些范围不重叠,所以在每个日期我都有一系列独特的产品及其属性.所以为了简化它的使用,我创建了这个函数:

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
)
Run Code Online (Sandbox Code Playgroud)

这就是我要用它的方式:

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

这一切都很好,但我怎么能让优化器知道那些行是唯一的,有更好的执行计划?

我做了一些谷歌搜索,发现了一些非常好的DDL文章,可以防止在表格中存储重叠范围:

但即使我尝试这些约束,我也看到优化器无法理解结果记录集将返回唯一代码.

我想要的是某种方法,它给了我基本相同的性能,就像我在某个日期存储这些产品列表并选择它一样date = @date.

我知道一些RDMBS(如PostgreSQL)具有特殊的数据类型(Range Types).但是SQL Server没有这样的东西.

我错过了什么,或者在SQL Server中没有办法正确地做到这一点?

Shn*_*ugo 0

一个没有间隙的解决方案可能是这样的:

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
Run Code Online (Sandbox Code Playgroud)

重要提示:确保有一个(唯一的)索引start_date

更新:针对不同的产品

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
Run Code Online (Sandbox Code Playgroud)