【问题标题】:Get a list of dates between two dates using a function使用函数获取两个日期之间的日期列表
【发布时间】:2010-11-25 14:27:12
【问题描述】:

我的问题类似于this MySQL 问题,但适用于 SQL Server:

是否有函数或查询可以返回两个日期之间的天数列表?例如,假设有一个名为 ExplodeDates 的函数:

SELECT ExplodeDates('2010-01-01', '2010-01-13');

这将返回一个包含值的单列表:

2010-01-01
2010-01-02
2010-01-03
2010-01-04
2010-01-05
2010-01-06
2010-01-07
2010-01-08
2010-01-09
2010-01-10
2010-01-11
2010-01-12
2010-01-13

我在想日历/数字表可能可以帮助我。


更新

我决定查看提供的三个代码答案,执行结果(占总批次的百分比)是:

越低越好

我接受了 Rob Farley 的回答,因为它是最快的,尽管数字表解决方案(KM 和 StingyJack 在他们的回答中都使用)是我最喜欢的。 Rob Farley 的速度要快三分之二。

更新 2

Alivia 的answer 更简洁。我已更改接受的答案。

【问题讨论】:

  • SQL 中的循环性能将成为 S U C K。尝试这些答案时请记住这一点。
  • 执行时间呢?总批次的百分比用于识别瓶颈,而不是吞吐量。您是在对实际的函数调用或其他所有内容进行基准测试吗?比较小批量和大批量的结果?
  • 使用 SET STATISTICS TIME ON 所有三个用 ('1/1/1998','12/31/2020') 调用的函数报告相同的 CPU 时间 = 0 毫秒,经过的时间 = 1 毫秒.当使用 ('1/1/1900','1921-11-27') 调用 Rob's 和 mine 时,StingyJacks 无法执行该日期范围,我得到 Rob's 为:CPU 时间 = 93 毫秒,经过时间 = 93 毫秒。我得到了我的:CPU时间= 0毫秒,经过时间= 1毫秒,我的看起来好多了。你使用什么测试方法@Dan Atkinson?如果您包括一次性数字表设置,这是一种非常有缺陷的方式,因为它不能反映实际的使用性能。
  • @KM 和@StingyJack。感谢你们教育我正确的基准测试方法。还有KM,感谢您不厌其烦地指出实际的基准测试结果。我将在我的数据库上运行一些并相应地更新问题。再次感谢!
  • 你为什么改变答案? Alivia 的回答需要提示以确保它包含足够的值,并且它不是所要求的函数。

标签: sql-server date


【解决方案1】:
DECLARE @StartDate DATE = '2017-09-13',         @EndDate DATE = '2017-09-16'

SELECT date  FROM (   SELECT DATE = DATEADD(DAY, rn - 1, @StartDate)   FROM    (
    SELECT TOP (DATEDIFF(DAY, @StartDate, DATEADD(DAY,1,@EndDate)))
      rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
    FROM sys.all_objects AS s1
    CROSS JOIN sys.all_objects AS s2
    ORDER BY s1.[object_id]   ) AS x ) AS y

结果:

2017-09-13

2017-09-14

2017-09-15

2017-09-16

【讨论】:

    【解决方案2】:

    如果你遇到像我这样过程和函数被禁止,并且你的sql用户没有插入权限,因此不允许插入,也“不允许设置/声明像@c 这样的临时变量”,但是您想生成特定时期内的日期列表,比如说当年进行一些聚合,使用这个

    select * from 
    (select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date from
     (select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
     (select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
     (select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
     (select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
     (select 0 t4 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
    where gen_date between '2017-01-01' and '2017-12-31'
    

    【讨论】:

      【解决方案3】:

      此查询适用于 Microsoft SQL Server。

      select distinct format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) as aDate
             from (
                   SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n as v
                   FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
                          (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
                          (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
                        (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
             ) a
             where format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) < cast('2010-01-13' as datetime)
             order by aDate asc;
      

      现在让我们看看它是如何工作的。

      内部查询仅返回一个从 0 到 9999 的整数列表。它将为我们提供 10,000 个值的范围来计算日期。您可以通过添加十万和十万行等来获得更多日期。

      SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n as v
               FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
                      (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
                      (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
                    (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
         ) a;
      

      这部分将字符串转换为日期,并从内部查询中添加一个数字。

      cast('2010-01-01' as datetime) + ( a.v / 10 )
      

      然后我们将结果转换为您想要的格式。这也是列名!

      format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' )
      

      接下来,我们只提取不同的值,并为列名指定一个别名 aDate。

      distinct format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) as aDate
      

      我们使用 where 子句仅过滤所需范围内的日期。请注意,我们在这里使用列名,因为 SQL Server 不接受 where 子句中的列别名 aDate。

      where format( cast('2010-01-01' as datetime) + ( a.v / 10 ), 'yyyy-MM-dd' ) < cast('2010-01-13' as datetime)
      

      最后,我们对结果进行排序。

         order by aDate asc;
      

      【讨论】:

        【解决方案4】:

        如果您想打印从特定年份到当前日期的年份。只是更改了接受的答案。

        WITH mycte AS
            (
              SELECT YEAR(CONVERT(DATE, '2006-01-01',102)) DateValue
              UNION ALL
              SELECT  DateValue + 1
              FROM    mycte   
              WHERE   DateValue + 1 < = YEAR(GETDATE())
            )
            SELECT  DateValue
            FROM    mycte
        
        OPTION (MAXRECURSION 0)
        

        【讨论】:

          【解决方案5】:
          DECLARE @MinDate DATETIME = '2012-09-23 00:02:00.000',
              @MaxDate DATETIME = '2012-09-25 00:00:00.000';
          
          SELECT  TOP (DATEDIFF(DAY, @MinDate, @MaxDate) + 1) Dates = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @MinDate)
          FROM sys.all_objects a CROSS JOIN sys.all_objects b;
          

          【讨论】:

            【解决方案6】:

            -- ### 一打六打。假设 MsSql 的另一种方法

            Declare @MonthStart    datetime   = convert(DateTime,'07/01/2016')
            Declare @MonthEnd      datetime   = convert(DateTime,'07/31/2016')
            Declare @DayCount_int       Int   = 0 
            Declare @WhileCount_int     Int   = 0
            
            set @DayCount_int = DATEDIFF(DAY, @MonthStart, @MonthEnd)
            select @WhileCount_int
            WHILE @WhileCount_int < @DayCount_int + 1
            BEGIN
               print convert(Varchar(24),DateAdd(day,@WhileCount_int,@MonthStart),101)
               SET @WhileCount_int = @WhileCount_int + 1;
            END;
            

            【讨论】:

              【解决方案7】:
              Declare @date1 date = '2016-01-01'
                            ,@date2 date = '2016-03-31'
                            ,@date_index date
              Declare @calender table (D date)
              SET @date_index = @date1
              WHILE @date_index<=@date2
              BEGIN
              INSERT INTO @calender
              SELECT @date_index
              
              SET @date_index = dateadd(day,1,@date_index)
              
              IF @date_index>@date2
              Break
              ELSE
              Continue
              END
              

              【讨论】:

                【解决方案8】:

                聚会有点晚了,但我非常喜欢这个解决方案。

                CREATE FUNCTION ExplodeDates(@startDate DateTime, @endDate DateTime)
                RETURNS table as
                return (
                    SELECT  TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
                            DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @startDate) AS DATE
                    FROM    sys.all_objects a
                            CROSS JOIN sys.all_objects b
                            )
                

                【讨论】:

                  【解决方案9】:

                  答案是avialbe here How to list all dates between two dates

                  Create Procedure SelectDates(@fromDate Date, @toDate Date)
                  AS
                  BEGIN
                      SELECT DATEADD(DAY,number,@fromDate) [Date]
                      FROM master..spt_values
                      WHERE type = 'P'
                      AND DATEADD(DAY,number,@fromDate) < @toDate
                  
                  END
                  

                  【讨论】:

                  • 这不是一个好的答案,原因有几个。 1:主表并不总是可用的。 2:表只要你的数据库中的项目数。如果这小于实际答案,则此 proc 的返回列表将不正确。 3:答案或多或少是使用系统表的数字表。
                  【解决方案10】:
                  SELECT  dateadd(dd,DAYS,'2013-09-07 00:00:00') DATES
                  INTO        #TEMP1
                  FROM
                  (SELECT TOP 365 colorder - 1 AS DAYS from master..syscolumns 
                      WHERE id = -519536829 order by colorder) a
                  
                  WHERE datediff(dd,dateadd(dd,DAYS,'2013-09-07 00:00:00'),'2013-09-13 00:00:00' ) >= 0 
                      AND  dateadd(dd,DAYS,'2013-09-07 00:00:00') <=  '2013-09-13 00:00:00'  
                      SELECT * FROM #TEMP1
                  

                  【讨论】:

                  • 您能否在示例中提供解释?
                  【解决方案11】:
                  WITH TEMP (DIA, SIGUIENTE_DIA ) AS
                             (SELECT 
                                 1, 
                                 CAST(@FECHAINI AS DATE)
                              FROM 
                                 DUAL
                             UNION ALL
                              SELECT 
                                 DIA, 
                                 DATEADD(DAY, DIA, SIGUIENTE_DIA)
                              FROM 
                                 TEMP
                              WHERE
                                 DIA < DATEDIFF(DAY,  @FECHAINI, @FECHAFIN)   
                                 AND DATEADD(DAY, 1, SIGUIENTE_DIA) <=  CAST(@FECHAFIN AS DATE)
                             )
                             SELECT 
                                SIGUIENTE_DIA AS CALENDARIO 
                             FROM
                                TEMP
                             ORDER BY   
                                SIGUIENTE_DIA
                  

                  详细信息在 DUAL 表上,但如果您将此表交换为虚拟表,则可以。

                  【讨论】:

                    【解决方案12】:

                    这正是您想要的,根据 Will 的早期帖子进行了修改。不需要辅助表或循环。

                    WITH date_range (calc_date) AS (
                        SELECT DATEADD(DAY, DATEDIFF(DAY, 0, '2010-01-13') - DATEDIFF(DAY, '2010-01-01', '2010-01-13'), 0)
                            UNION ALL SELECT DATEADD(DAY, 1, calc_date)
                                FROM date_range
                                WHERE DATEADD(DAY, 1, calc_date) <= '2010-01-13')
                    SELECT calc_date
                    FROM date_range;
                    

                    【讨论】:

                    • 我在更复杂的日期集上收到以下错误:The statement terminated. The maximum recursion 100 has been exhausted before statement completion. 所以,我应该指出其他希望在大范围内使用此答案的人,您需要添加一个 maxrecursion 值 - @ 987654323@.
                    【解决方案13】:

                    您只需更改下面提供的代码中的硬编码值

                    DECLARE @firstDate datetime
                        DECLARE @secondDate datetime
                        DECLARE @totalDays  INT
                        SELECT @firstDate = getDate() - 30
                        SELECT @secondDate = getDate()
                    
                        DECLARE @index INT
                        SELECT @index = 0
                        SELECT @totalDays = datediff(day, @firstDate, @secondDate)
                    
                        CREATE TABLE #temp
                        (
                             ID INT NOT NULL IDENTITY(1,1)
                            ,CommonDate DATETIME NULL
                        )
                    
                        WHILE @index < @totalDays
                            BEGIN
                    
                                INSERT INTO #temp (CommonDate) VALUES  (DATEADD(Day, @index, @firstDate))   
                                SELECT @index = @index + 1
                            END
                    
                        SELECT CONVERT(VARCHAR(10), CommonDate, 102) as [Date Between] FROM #temp
                    
                        DROP TABLE #temp
                    

                    【讨论】:

                      【解决方案14】:

                      这几行是sql server中这个问题的简单答案。

                      WITH mycte AS
                      (
                        SELECT CAST('2011-01-01' AS DATETIME) DateValue
                        UNION ALL
                        SELECT  DateValue + 1
                        FROM    mycte   
                        WHERE   DateValue + 1 < '2021-12-31'
                      )
                      
                      SELECT  DateValue
                      FROM    mycte
                      OPTION (MAXRECURSION 0)
                      

                      【讨论】:

                      • 我想知道为什么 '2011-01-01' 不适合我,而 '20110101' 适合。
                      • 这太棒了!读者:根据您的要求设置 where 子句以包含或排除最终日期。赞成票。
                      • OP 从最正确的答案变成了 Alivia 的,因为它更“简洁”。非常糟糕的举动,因为它也更加占用 CPU,与普通 WHILE 循环一样慢,比事务性 WHILE 循环慢,并且使用的逻辑 I/O 是普通 WHILE 循环的 8 倍。我强烈建议避免使用这种方法(递增递归 CTE 或 rCTE)。 Rob Farely 的答案是 iTVF(内联表值函数),它比 rCTE 方法更易于使用。
                      【解决方案15】:

                      试试这样的:

                      CREATE FUNCTION dbo.ExplodeDates(@startdate datetime, @enddate datetime)
                      returns table as
                      return (
                      with 
                       N0 as (SELECT 1 as n UNION ALL SELECT 1)
                      ,N1 as (SELECT 1 as n FROM N0 t1, N0 t2)
                      ,N2 as (SELECT 1 as n FROM N1 t1, N1 t2)
                      ,N3 as (SELECT 1 as n FROM N2 t1, N2 t2)
                      ,N4 as (SELECT 1 as n FROM N3 t1, N3 t2)
                      ,N5 as (SELECT 1 as n FROM N4 t1, N4 t2)
                      ,N6 as (SELECT 1 as n FROM N5 t1, N5 t2)
                      ,nums as (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as num FROM N6)
                      SELECT DATEADD(day,num-1,@startdate) as thedate
                      FROM nums
                      WHERE num <= DATEDIFF(day,@startdate,@enddate) + 1
                      );
                      

                      然后你使用:

                      SELECT *
                      FROM dbo.ExplodeDates('20090401','20090531') as d;
                      

                      已编辑(接受后):

                      请注意...如果您已经有一个足够大的 nums 表,那么您应该使用:

                      CREATE FUNCTION dbo.ExplodeDates(@startdate datetime, @enddate datetime)
                      returns table as
                      return (
                      SELECT DATEADD(day,num-1,@startdate) as thedate
                      FROM nums
                      WHERE num <= DATEDIFF(day,@startdate,@enddate) + 1
                      );
                      

                      您可以使用以下方法创建这样的表:

                      CREATE TABLE dbo.nums (num int PRIMARY KEY);
                      INSERT dbo.nums values (1);
                      GO
                      INSERT dbo.nums SELECT num + (SELECT COUNT(*) FROM nums) FROM nums
                      GO 20
                      

                      这些行将创建一个包含 1M 行的数字表...并且比逐个插入要快得多。

                      您不应使用涉及 BEGIN 和 END 的函数来创建 ExplodeDates 函数,因为查询优化器根本无法简化查询。

                      【讨论】:

                      • 感谢您抽出宝贵时间改进您的回答。我也不知道使用 BEGIN 和 END 会阻止查询优化器完成它的工作。谢谢!
                      • 如果我能多次投票赞成,我会 - 它的表现是惊人的。我针对一个简单版本对其进行了测试,其中 nums 是一个数字表,数字上有一个聚集索引。在日期差为 2 天的情况下,CTE 比聚集索引高出 2 倍以上(28% 对 72%),但如果日期差为 37 年,则 CTE 版本为 3% 对表的 97%!我希望我知道它为什么这么快......
                      • 这是因为它不需要做任何 I/O。
                      【解决方案16】:

                      绝对是一个数字表,但如果您确实需要性能,您可能想要使用 Mark Redman 的 CLR proc/assembly 理念。

                      如何创建日期表(以及创建数字表的超快速方法)

                      /*Gets a list of integers into a temp table (Jeff Moden's idea from SqlServerCentral.com)*/
                       SELECT TOP 10950 /*30 years of days*/
                              IDENTITY(INT,1,1) as N
                         INTO #Numbers
                         FROM Master.dbo.SysColumns sc1,
                              Master.dbo.SysColumns sc2
                      
                      
                      /*Create the dates table*/
                      CREATE TABLE [TableOfDates](
                          [fld_date] [datetime] NOT NULL,
                       CONSTRAINT [PK_TableOfDates] PRIMARY KEY CLUSTERED 
                      (
                          [fld_date] ASC
                      )WITH FILLFACTOR = 99 ON [PRIMARY]
                      ) ON [PRIMARY]
                      
                      /*fill the table with dates*/
                      DECLARE @daysFromFirstDateInTheTable int
                      DECLARE @firstDateInTheTable DATETIME
                      
                      SET @firstDateInTheTable = '01/01/1998'
                      SET @daysFromFirstDateInTheTable = (SELECT (DATEDIFF(dd, @firstDateInTheTable ,GETDATE()) + 1))
                      
                      INSERT INTO
                            TableOfDates
                      SELECT 
                            DATEADD(dd,nums.n - @daysFromFirstDateInTheTable, CAST(FLOOR(CAST(GETDATE() as FLOAT)) as DateTime)) as FLD_Date
                      FROM #Numbers nums
                      

                      现在您有了日期表,您可以使用 KM 之类的函数(不是 PROC)来获取日期表。

                      CREATE FUNCTION dbo.ListDates
                      (
                           @StartDate    DATETIME  
                          ,@EndDate      DATETIME
                      )
                      RETURNS
                      @DateList table
                      (
                          Date datetime
                      )
                      AS
                      BEGIN
                      
                      /*add some validation logic of your own to make sure that the inputs are sound.Adjust the rest as needed*/
                      
                        INSERT INTO
                          @DateList
                        SELECT FLD_Date FROM TableOfDates (NOLOCK) WHERE FLD_Date >= @StartDate AND FLD_Date <= @EndDate
                        RETURN
                      END
                      

                      【讨论】:

                      • 为什么你需要一个日期表,你只是用你的数字表来计算它们??
                      • 因为动态计算它们会导致性能下降,尤其是当它们被内联使用并针对语句访问的每一行进行评估时。
                      • Msg 137, Level 15, State 2, Line 23 必须声明标量变量“@”。 this (SELECT (DATEDIFF(dd, @ firstDateInTheTable ,GETDATE ()) + 1)) 应该是 (SELECT (DATEDIFF(dd, @firstDateInTheTable ,GETDATE()) + 1))_
                      【解决方案17】:

                      也许如果你想走更简单的路,应该这样做。

                      WITH date_range (calc_date) AS (
                          SELECT DATEADD(DAY, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP) - 6, 0)
                              UNION ALL SELECT DATEADD(DAY, 1, calc_date)
                                  FROM date_range
                                  WHERE DATEADD(DAY, 1, calc_date) < CURRENT_TIMESTAMP)
                      SELECT calc_date
                      FROM date_range;
                      

                      但是临时表也是一个很好的方法。也许您还应该考虑一个填充的日历表。

                      【讨论】:

                      • 您只需要使用此代码创建一个存储过程,并可能将 CURRENT_TIMESTAMP 值替换为您的或类似的值。
                      【解决方案18】:

                      所有这些日期是否已经在数据库中,或者您只想知道这两个日期之间的天数?如果是第一次,您可以使用 BETWEEN= 来查找之间的日期

                      示例:

                      SELECT column_name(s)
                      FROM table_name
                      WHERE column_name
                      BETWEEN value1 AND value2
                      

                      SELECT column_name(s)
                      FROM table_name
                      WHERE column_name
                      value1 >= column_name
                      AND column_name =< value2
                      

                      【讨论】:

                        【解决方案19】:

                        在你使用我的功能之前,你需要建立一个“helper”表,每个数据库只需要这样做一次:

                        CREATE TABLE Numbers
                        (Number int  NOT NULL,
                            CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
                        ) ON [PRIMARY]
                        DECLARE @x int
                        SET @x=0
                        WHILE @x<8000
                        BEGIN
                            SET @x=@x+1
                            INSERT INTO Numbers VALUES (@x)
                        END
                        

                        函数如下:

                        CREATE FUNCTION dbo.ListDates
                        (
                             @StartDate    char(10)  
                            ,@EndDate      char(10)
                        )
                        RETURNS
                        @DateList table
                        (
                            Date datetime
                        )
                        AS
                        BEGIN
                        
                        
                        IF ISDATE(@StartDate)!=1 OR ISDATE(@EndDate)!=1
                        BEGIN
                            RETURN
                        END
                        
                        INSERT INTO @DateList
                                (Date)
                            SELECT
                                CONVERT(datetime,@StartDate)+n.Number-1
                                FROM Numbers  n
                                WHERE Number<=DATEDIFF(day,@StartDate,CONVERT(datetime,@EndDate)+1)
                        
                        
                        RETURN
                        
                        END --Function
                        

                        使用这个:

                        select * from dbo.ListDates('2010-01-01', '2010-01-13')
                        

                        输出:

                        Date
                        -----------------------
                        2010-01-01 00:00:00.000
                        2010-01-02 00:00:00.000
                        2010-01-03 00:00:00.000
                        2010-01-04 00:00:00.000
                        2010-01-05 00:00:00.000
                        2010-01-06 00:00:00.000
                        2010-01-07 00:00:00.000
                        2010-01-08 00:00:00.000
                        2010-01-09 00:00:00.000
                        2010-01-10 00:00:00.000
                        2010-01-11 00:00:00.000
                        2010-01-12 00:00:00.000
                        2010-01-13 00:00:00.000
                        
                        (13 row(s) affected)
                        

                        【讨论】:

                        • 哪里有“WHILE”,哪里就有循环!
                        • @StingyJack,你疯了吗,我的函数中没有循环。我使用一个循环来设置 Numbers 表,这样人们就可以很容易地看到它的作用。我可以在那里轻松地使用 CTE(比如从这里:sommarskog.se/arrays-in-sql-2005.html#tblnum),但它让一些人感到困惑。对于一次性设置一张桌子来说,这不是问题。
                        • 他指的是您在 Numbers 表中输入值的位置。
                        • @KM.. 你的代码中肯定有一个循环。在你抨击我之前帮我一个忙......测试使用你和我的方法创建 10000 个数字的数字表需要多长时间。你会看到我从 Moden 那里借来的那个会比 looped op 好几个数量级。在那之后,测试每个函数需要多长时间。我敢打赌,我提供的解决方案会比你得到的解决方案更好。下次你想 DV 某人时,你最好确保你的理由是有根据的。
                        • 我喜欢数字表的想法!它们用途广泛,也可用于其他用途。
                        【解决方案20】:

                        我是 oracle 人,但我相信 MS SQL Server 支持 connect by 子句:

                        select  sysdate + level
                        from    dual
                        connect by level <= 10 ;
                        

                        输出是:

                        SYSDATE+LEVEL
                        05-SEP-09
                        06-SEP-09
                        07-SEP-09
                        08-SEP-09
                        09-SEP-09
                        10-SEP-09
                        11-SEP-09
                        12-SEP-09
                        13-SEP-09
                        14-SEP-09
                        

                        Dual 只是 oracle 附带的一个“虚拟”表(它包含 1 行和单词“虚拟”作为单列的值)。

                        【讨论】:

                        • SQL Server 没有内置表“dual”,您需要创建自己的表,就像我在示例代码中所做的那样。我认为 SQL Server 中的“sysdate”是 GETDATE(),而“connect by”不是有效的语法。
                        • 因此,您也可以在 SQL Server 中从无处选择。 SELECT GETDATE() 是 SQL Server 中的有效代码行,而不是 Oracle 中的有效代码行,即使您将 GETDATE() 函数替换为其 SYSDATE 同源函数。
                        • 你说得对,Brian,在 Oracle 中我们会这样做。 Oracle 和 PL/SQL 中有很多有趣的特性不包含在 TSQL 和 SQL Server 中。这是Sybase的错! ;-) SQL Server 主要基于 Sysbase TSQL 语言。
                        • SELECT GETDATE() 不会只生成一行。在 Oracle 中使用对偶,你会得到一个集合。
                        • Little offtop:实际上列名是“DUMMY”,值是“X”。 ;-)
                        【解决方案21】:

                        一些想法:

                        如果您需要列表日期来循环它们,您可以有一个开始日期和日期计数参数,并在创建日期并使用它的同时执行一个 while 循环?

                        使用 C# CLR 存储过程并用 C# 编写代码

                        在代码中的数据库之外执行此操作

                        【讨论】:

                        • CLR 存储 proc 是如果性能至关重要的话要走的路。
                        • @StingyJack,没办法。数字表会更有效,请参阅我的答案以获取有关如何操作的示例。
                        • 性能并不重要,因为这在最坏的情况下只会每小时调用一次,平均每天调用一次,然后它会被缓存。不过,我不想使用 CLR 来执行此操作。
                        • @KM - 有可能在这里引发一场激烈的战争,您应该知道 SQL 不是为处理过程操作而设计的,并且执行起来很差。如果您需要做类似的事情,最好由应用程序代码处理。
                        • @StingyJack,我的函数程序如何?除了验证检查之外,它是一个简单的查询,可以将偏移量添加到固定日期以获得可变化的行数。在商品详细信息中进行的计算并没有太大区别,例如根据数量和单价计算总价,有或没有货币。
                        猜你喜欢
                        • 2010-10-05
                        • 2018-09-17
                        • 1970-01-01
                        • 1970-01-01
                        • 2018-01-11
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多