gpt4 book ai didi

sql - 存储日期范围的有效方法

转载 作者:行者123 更新时间:2023-12-04 07:56:03 25 4
gpt4 key购买 nike

我需要存储简单的数据 - 假设我有一些产品以代码作为主键、一些属性和有效性范围。所以数据可能是这样的:

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

这些范围不重叠,因此在每个日期我都会列出独特的产品及其属性。所以为了方便使用,我创建了这个函数:
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
)

这是我将如何使用它:
select
*
from <some table with product codes> as t
left join dbo.f_Products(@date) as p on
p.code = t.product_code

这一切都很好,但是我如何让优化器知道这些行是独一无二的,以便拥有更好的执行计划?

我做了一些谷歌搜索,发现了几篇关于 DDL 的非常好的文章,它们可以防止在表中存储重叠范围:
  • Self-maintaining, Contiguous Effective Dates in Temporal Tables
  • Storing intervals of time with no overlaps

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

    我想要的是某种方法,它给我的性能基本上与我在某个日期存储这些产品列表并使用 date = @date 选择它一样。 .

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

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

    最佳答案

    您可以创建一个 indexed view每个 code/date 包含一行在范围内。

    ProductDate (indexed view)
    code value date
    10905 13 2005-01-01
    10905 13 2005-01-02
    10905 13 ...
    10905 13 2016-12-31
    10905 11 2017-01-01
    10905 11 2017-01-02
    10905 11 ...
    10905 11 Today

    像这样:
    create schema digits
    go

    create table digits.Ones (digit tinyint not null primary key)
    insert into digits.Ones (digit) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)

    create table digits.Tens (digit tinyint not null primary key)
    insert into digits.Tens (digit) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)

    create table digits.Hundreds (digit tinyint not null primary key)
    insert into digits.Hundreds (digit) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)

    create table digits.Thousands (digit tinyint not null primary key)
    insert into digits.Thousands (digit) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)

    create table digits.TenThousands (digit tinyint not null primary key)
    insert into digits.TenThousands (digit) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)
    go

    create schema info
    go

    create table info.Products (code int not null, [value] int not null, begin_date date not null, end_date date null, primary key (code, begin_date))
    insert into info.Products (code, [value], begin_date, end_date) values
    (10905, 13, '2005-01-01', '2016-12-31'),
    (10905, 11, '2017-01-01', null)

    create table info.DateRange ([begin] date not null, [end] date not null, [singleton] bit not null default(1) check ([singleton] = 1))
    insert into info.DateRange ([begin], [end]) values ((select min(begin_date) from info.Products), getdate())
    go

    create view info.ProductDate with schemabinding
    as
    select
    p.code,
    p.value,
    dateadd(day, ones.digit + tens.digit*10 + huns.digit*100 + thos.digit*1000 + tthos.digit*10000, dr.[begin]) as [date]
    from
    info.DateRange as dr
    cross join
    digits.Ones as ones
    cross join
    digits.Tens as tens
    cross join
    digits.Hundreds as huns
    cross join
    digits.Thousands as thos
    cross join
    digits.TenThousands as tthos
    join
    info.Products as p on
    dateadd(day, ones.digit + tens.digit*10 + huns.digit*100 + thos.digit*1000 + tthos.digit*10000, dr.[begin]) between p.begin_date and isnull(p.end_date, datefromparts(9999, 12, 31))
    go

    create unique clustered index idx_ProductDate on info.ProductDate ([date], code)
    go

    select *
    from info.ProductDate with (noexpand)
    where
    date = '2014-01-01'

    drop view info.ProductDate
    drop table info.Products
    drop table info.DateRange
    drop table digits.Ones
    drop table digits.Tens
    drop table digits.Hundreds
    drop table digits.Thousands
    drop table digits.TenThousands
    drop schema digits
    drop schema info
    go

    关于sql - 存储日期范围的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40532854/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com