【问题标题】:How can i create a Date table in SQL that will automatically fill itself如何在 SQL 中创建一个自动填充的日期表
【发布时间】:2021-01-11 22:16:50
【问题描述】:

我想在 SQL 中创建一个包含以下 4 列的表:id、年、月和日。但我想从 2012 年到 2020 年自动填充这些列。最好的方法是什么?我用谷歌搜索,但我真的找不到解决方案。例如,我希望表格显示以下信息。

ID  year  Month Day
1   2013   1     2
2   2013   1     3
3   2013   1     4
4   2013   1     5

【问题讨论】:

标签: sql sql-server date datatable dimensions


【解决方案1】:

使用 VBoka 建议,试试这个:

create table calendar(
    id int identity(1,1) not null primary key,
    ano int not null,
    mes int not null,
    dia int not null
)

declare @startdate date, @enddate date, @num_days int
set @startdate = '2012-01-01'
set @num_days = 3287 --number of days bewtween jan 2012 and dez 2020 https://www.timeanddate.com/date/durationresult.html
set @enddate = dateadd(d, @num_days, @startdate)

while @startdate <= @enddate
    begin
        insert into calendar (ano,mes,dia)
            select year(@startdate), month(@startdate), day(@startdate)

        set @startdate = dateadd(dd, 1, @startdate)
    end

【讨论】:

    【解决方案2】:

    使用 CTE 而没有日期时间函数:

    if object_id('Calendar_tb','U') is null
        create table Calendar_tb (
            id int identity(1,1) not null,
            [year] smallint, [month] tinyint, [day] tinyint
        )
    
    declare @tb table (yy smallint, mm tinyint, dd tinyint);
    with
    CTE_y as (
        select 2012 as nyear
        union all
        select nyear + 1 from CTE_y where nyear < 2020
    ),
    CTE_m as (
        select 1 as nmonth
        union all
        select nmonth + 1 from CTE_m where nmonth < 12
    ),
    CTE_d as (
        select 1 as nday
        union all
        select nday + 1 from cte_d where nday < 31
    )
    insert @tb
    select nyear, nmonth, nday from CTE_d, CTE_m, CTE_y
    
    delete from @tb
    where   (dd>30 and mm in (4, 6, 9, 11))
        or  (dd>29 and mm = 2)
        or  (dd>28 and mm = 2 and not
                -- leap year
                (yy%400=0 or (yy%4=0 and yy%100!=0)))
    
    truncate table Calendar_tb
    
    insert Calendar_tb
    select * from @tb order by 1, 2, 3
    
    select * from Calendar_tb
    

    【讨论】:

    • 考虑到递归 CTE 的表现有多糟糕,尤其是对于读取而言,您应该考虑练习避免它们......即使是小东西。
    【解决方案3】:

    不需要循环、游标或递归 CTE...就像以下一样简单。

        WITH 
            cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),   -- 10
            cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),                             -- 100
            cte_Calendar (dt) AS (
                SELECT TOP (DATEDIFF(DAY, '2012-01-01', '2020-12-31') + 1)
                    CONVERT(DATE, DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1, '2012-01-01'))
                FROM
                    cte_n2 a CROSS JOIN cte_n2 b                                                    -- 10,000
                )
        SELECT
            ID = DATEDIFF(DAY, '2012-01-01', c.dt) + 1,
            [Year] = YEAR(c.dt),
            [Month] = MONTH(c.dt),
            [Day] = DAY(c.dt)
        FROM
            cte_Calendar c;
    GO
    

    或者您可以使用它来填充永久表...

        IF OBJECT_ID('tempdb.dbo.YMD', 'U') IS NOT NULL 
        BEGIN DROP TABLE tempdb.dbo.YMD; END;
        GO
        
        CREATE TABLE tempdb.dbo.YMD (
                ID int NOT NULL IDENTITY(1,1)
                    CONSTRAINT pk_YMD PRIMARY KEY CLUSTERED (ID),
                [Year] int NOT NULL,
                [Month] tinyint NOT NULL,
                [Day] tinyint NOT NULL 
            );
        GO
        
        --------------------------------------------------------------------------------------------------------
        WITH 
            cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),   -- 10
            cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),                             -- 100
            cte_Calendar (dt) AS (
                SELECT TOP (DATEDIFF(DAY, '2012-01-01', '2020-12-31') + 1)
                    CONVERT(DATE, DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1, '2012-01-01'))
                FROM
                    cte_n2 a CROSS JOIN cte_n2 b                                                    -- 10,000
                )
        INSERT tempdb.dbo.YMD (Year, Month, Day)
        SELECT 
            [Year] = YEAR(c.dt),
            [Month] = MONTH(c.dt),
            [Day] = DAY(c.dt)
        FROM
            cte_Calendar c;
    GO
        
        ------------------------------------------------------------------------------------------------------
        SELECT
            YMD.ID,
            YMD.Year,
            YMD.Month,
            YMD.Day
        FROM
            tempdb.dbo.YMD;
    GO
    

    【讨论】:

      猜你喜欢
      • 2011-01-03
      • 1970-01-01
      • 2023-02-21
      • 2020-06-09
      • 1970-01-01
      • 2011-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多