【问题标题】:How can I generate a temporary table filled with dates in SQL Server 2000?如何在 SQL Server 2000 中生成一个填充有日期的临时表?
【发布时间】:2011-09-01 19:14:38
【问题描述】:

我需要创建一个包含日期范围的临时表,以及几个包含占位符值 (0) 的列以供将来使用。我需要的日期是 $startDate 和 $endDate 之间的每个月的第一天,其中这些变量可能相隔数年。

我原来的sql语句是这样的:

select dbo.FirstOfMonth(InsertDate) as Month, 0 as Trials, 0 as Sales
into #dates
from customer
group by dbo.FirstOfMonth(InsertDate)

“FirstOfMonth”是我创建的一个用户定义的函数,它几乎按照它所说的那样,返回所提供日期的月份的第一天,时间正好是午夜。

这几乎完全符合我的需要,直到我发现我的日期偶尔会有几个月的空白,因为没有记录插入日期。由于我的结果必须仍然缺少月份,因此我需要一种不同的方法。

我已将以下声明添加到存储过程中,以预测它们对我需要的日期范围的需求...

declare $startDate set $startDate = select min(InsertDate) from customer
declare $endDate set $endDate = select max(InsertDate) from customer

...但我不知道从这里做什么。

我知道这个问题是similar to this question,但坦率地说,这个答案超出了我的想象(我不经常使用 SQL,而且当我使用 SQL Server 时,它往往是在旧版本的 SQL Server 上)并且有一个一些让我失望的细微差别。

【问题讨论】:

标签: sql tsql stored-procedures sql-server-2000


【解决方案1】:
SELECT  P.Id
      , DATEADD ( DD, -P.Id, P.Date ) AS Date
    FROM  (SELECT TOP 1000 ROW_NUMBER () OVER (ORDER BY (SELECT NULL)) AS Id, CAST(GETDATE () AS DATE) AS Date FROM master.dbo.spt_values) AS P

此查询返回最近 1000 天左右的表日历。它可以放在临时表或其他表中。

【讨论】:

    【解决方案2】:

    创建一个包含一年中每个月的日期的表变量:

    declare @months table (reportMonth date, PRIMARY KEY (reportMonth));
    declare @start date = '2018', @month int = 0; -- base 0 month
    while (@offset < 12)
    begin
        insert into @reportMonths select dateAdd(month, @offset, @start);
        select @offset = @offset + 1; 
    end
    

    【讨论】:

      【解决方案3】:

      指定日期范围或特定日期列表的有用组合的起点:

      SELECT *
      FROM 
          (SELECT CONVERT(DateTime,'2017-1-1')+number AS [Date]
           FROM master..spt_values WHERE type='P' AND number<370) AS DatesList
      WHERE DatesList.Date IN ('2017-1-1','2017-4-14','2017-4-17','2017-12-25','2017-12-26')
      

      你可以从master..spt_values WHERE type='P' 中得到 0 到 2047,所以如果你需要的话,这是五年半的日期!

      【讨论】:

        【解决方案4】:
        declare @start datetime
        set @start = '2016-09-01'
        declare @end datetime
        set @end = '2016-09-30'
        
        create table #Date
        (
            table_id int identity(1,1) NOT NULL,
            counterDate datetime NULL
        );
        
        insert into #Date select top (datediff(D,@start,@end)) NULL from SOME_TABLE
        update #Date set counterDate = dateadd(D,table_id - 1, @start)
        

        上面的代码应该用开始和结束之间的所有日期填充表格。然后,您只需加入此表即可获取所需的所有日期。如果您只需要每个月的某一天,您可以改为添加一个月。

        【讨论】:

          【解决方案5】:
          select top (datediff(D,@start,@end)) dateadd(D,id-1,@start)
          from BIG_TABLE_WITH_NO_JUMPS_IN_ID
          

          【讨论】:

            【解决方案6】:

            这当然不适用于 SQL-Server 2000,但在您不想创建永久表的现代数据库中。您可以使用表变量而不是创建表,这样您就可以离开连接数据试试这个。将 DAY 更改为 HOUR 等以更改增量类型。

            declare @CalendarMonths table (date DATETIME,  PRIMARY KEY (date)
            )
            
            DECLARE
              @basedate DATETIME,
              @offset   INT
            SELECT
              @basedate = '01 Jan 2014',
              @offset = 1
              INSERT INTO @CalendarMonths SELECT @basedate
            
            WHILE ( DATEADD(DAY, @offset, @basedate) < CURRENT_TIMESTAMP)
            BEGIN
              INSERT INTO @CalendarMonths SELECT DATEADD(HOUR, @offset, date) FROM @CalendarMonths where DATEADD(DAY, @offset, date) < CURRENT_TIMESTAMP
              SELECT @offset = @offset + @offset
            END
            

            【讨论】:

              【解决方案7】:

              对于 SQL Server 2000,这个stackoverflow post 看起来很有希望用于临时生成根据开始和结束日期计算的日期。它不完全相同,但非常相似。如果需要,This post 对截断日期有非常深入的回答。

              如果有人偶然发现这个问题并且在 PostgreSQL 而不是 SQL Server 2000 中工作,那么您可以在此处进行操作...

              PostgreSQL 有一个漂亮的series generating function。对于您的示例,您可以使用这一系列的所有日期而不是生成整个日历表,然后从那里进行分组和匹配。

              SELECT current_date + s.a AS dates FROM generate_series(0,14,7) AS s(a);
                 dates
              ------------
               2004-02-05
               2004-02-12
               2004-02-19
              (3 rows)
              
              SELECT * FROM generate_series('2008-03-01 00:00'::timestamp,
                                            '2008-03-04 12:00', '10 hours');
                 generate_series   
              ---------------------
               2008-03-01 00:00:00
               2008-03-01 10:00:00
               2008-03-01 20:00:00
               2008-03-02 06:00:00
               2008-03-02 16:00:00
               2008-03-03 02:00:00
               2008-03-03 12:00:00
               2008-03-03 22:00:00
               2008-03-04 08:00:00
              (9 rows)
              

              我还将研究 PostgreSQL 中的 date_trunc,使用 'month' 作为截断器字段,以重构您的原始查询以轻松匹配日历系列的 date_trunc 版本。

              【讨论】:

              • 这没有回答这个问题,它是针对 SQL Server 2000 的。
              • 已更新以引用另一个 stackoverflow 帖子,该帖子确实为 SQL Server 提供了答案,并阐明了针对 PostgreSQL 的答案部分
              【解决方案8】:

              我需要类似的东西,但所有天而不是所有月。

              使用 MatBailie 中的代码作为起点,以下是用于创建所有日期从 2000-01-01 到 2099-12-31 的永久表的 SQL:

              CREATE TABLE _Dates (
                d DATE,
                PRIMARY KEY (d)
              )
              DECLARE @dIncr DATE = '2000-01-01'
              DECLARE @dEnd DATE = '2100-01-01'
              
              WHILE ( @dIncr < @dEnd )
              BEGIN
                INSERT INTO _Dates (d) VALUES( @dIncr )
                SELECT @dIncr = DATEADD(DAY, 1, @dIncr )
              END
              

              【讨论】:

                【解决方案9】:

                这将快速填充一个包含 170 年日期的表。

                CREATE TABLE CalendarMonths (
                  date DATETIME,
                  PRIMARY KEY (date)
                )
                
                DECLARE
                  @basedate DATETIME,
                  @offset   INT
                SELECT
                  @basedate = '01 Jan 2000',
                  @offset = 1
                
                WHILE (@offset < 2048)
                BEGIN
                  INSERT INTO CalendarMonths SELECT DATEADD(MONTH, @offset, date) FROM CalendarMonths
                  SELECT @offset = @offset + @offset
                END
                

                然后您可以通过向左加入该表来使用它,用于您需要的日期范围。

                【讨论】:

                • 我必须在 CalendarMonths 表中添加基础以使其工作:INSERT INTO @CalendarMonths SELECT @basedate 我还将表更改为表变量,以便它也是临时的并且也在内存中。跨度>
                • @edmicman - 为什么是临时的?如果您多次使用它,肯定值得持久化/缓存吗?
                • 在我的具体情况下,我想获取输入日期范围内的月/年列表。也许静态表会很好,但我并没有经常遇到这种需要,以至于只是动态创建它是一个问题。
                • @PeterTirrell - 表变量不存储在内存中,它作为临时表存储在磁盘上。
                • 只需在要工作的循环之前添加'insert into CalendarMonths select @basedate'(创建第一行)。
                【解决方案10】:

                在下面测试,它可以工作,虽然有点复杂。

                我为测试的日期指定了任意值。

                DECLARE @SD smalldatetime,
                        @ED smalldatetime,
                        @FD smalldatetime,
                        @LD smalldatetime,
                        @Mct int,
                        @currct int = 0
                
                SET @SD = '1/15/2011'
                SET @ED = '2/02/2012'
                
                
                SET @FD = (DATEADD(dd, -1*(Datepart(dd, @SD)-1), @sd))
                SET @LD = (DATEADD(dd, -1*(Datepart(dd, @ED)-1), @ED))
                
                SET @Mct = DATEDIFF(mm, @FD, @LD)
                
                CREATE TABLE #MyTempTable (FoM smalldatetime, Trials int, Sales money)
                
                WHILE @currct <= @Mct
                BEGIN
                    INSERT INTO #MyTempTable (FoM, Trials, Sales)
                    VALUES
                    (DATEADD(MM, @currct, @FD), 0, 0)
                    SET @currct = @currct + 1
                END
                
                
                SELECT * FROM #MyTempTable
                
                DROP TABLE #MyTempTable
                

                【讨论】:

                  【解决方案11】:

                  我可能会使用日历表。在您的数据库中创建一个永久表并用所有日期填充它。即使您涵盖了 100 年的范围,该表中仍然只有约 36,525 行。

                  CREATE TABLE dbo.Calendar (
                      calendar_date    DATETIME    NOT NULL,
                      is_weekend       BIT         NOT NULL,
                      is_holiday       BIT         NOT NULL,
                      CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (calendar_date)
                  )
                  

                  创建表后,只需在循环中填充一次,以便它始终存在并可供您使用。

                  您的查询可能是这样的:

                  SELECT
                      C.calendar_date,
                      0 AS trials,
                      0 AS sales
                  FROM
                      dbo.Calendar C
                  WHERE
                      C.calendar_date BETWEEN @start_date AND @end_date AND
                      DAY(C.calendar_date) = 1
                  

                  您可以根据需要加入客户表,如果需要,可以在 FirstOfMonth(InsertDate) = C.calendar_date 上进行外部加入。

                  如果需要,您还可以为 day_of_month 添加一列,这样可以避免调用 DAY() 函数的开销,但这很简单,因此无论哪种方式都可能无关紧要。

                  【讨论】:

                    猜你喜欢
                    • 2012-02-21
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-01-11
                    • 2019-07-20
                    • 2023-01-05
                    相关资源
                    最近更新 更多