【问题标题】:How to get all dates between current month and the two last months如何获取当前月份和最后两个月之间的所有日期
【发布时间】:2019-06-05 11:12:00
【问题描述】:

我正在尝试获取当前月份和最后两个月之间存在的所有日期。

例如:今天 10-01-2019 使用 sql 脚本,我将获得 2018-10-01 和 2019-01-31 之间的所有日期。

with cte as
  (
  select getdate() as   n
  union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) < month(DATEADD(month, -3, getdate())) --and month(DATEADD(month, 0, getdate()))
   union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) > month(DATEADD(month, 0, getdate()))
  )
  select * from cte

我明白了

错误消息 530,级别 16,状态 1,行 1 声明终止。在语句完成之前,最大递归 100 已用完。

【问题讨论】:

标签: sql sql-server tsql


【解决方案1】:

您正在达到最大递归限制。作为选项增加它:

with cte as
  (
  select getdate() as   n
  union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) < month(DATEADD(month, -3, getdate())) --and month(DATEADD(month, 0, getdate()))
   union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) > month(DATEADD(month, 0, getdate()))
  )
select * from cte
OPTION (MAXRECURSION 1000)

【讨论】:

  • 这仍然达到了递归限制……递归并不是这里最好的方法。这需要一个计数表或日历表。
【解决方案2】:

您可以为此目的使用临时表。使用循环,只需将您需要的日期添加到临时表中。检查以下查询:

create table #temp (thedate date)
declare @i int = 1
declare @tmpDate datetime = dateadd(month,-2,getdate())

while @tmpDate<=getdate()
begin
  insert into #temp
  values (@tmpDate)

  set @tmpDate = dateadd(day,1,@tmpDate)
end

select * from #temp

编辑:根据 OP 的评论,新查询:

create table #temp (thedate date)
declare @i int = 1
declare @endDate datetime = '2019-01-31'
declare @tmpDate datetime = '2018-10-01'

while @tmpDate<=@endDate
begin
  insert into #temp
  values (@tmpDate)

  set @tmpDate = dateadd(day,1,@tmpDate)
end

select * from #temp

【讨论】:

  • 在这里循环不是最有效的方法。
  • @SeanLange 你说得对,它的性能不是最好的,但是经过两个月的处理,它非常快速和有用。
  • 使用计数表会简单得多。 :)
  • 我从 2018-11-10 到 2019-01-10 获取值,但要求是从 2018-10-01(月初)和 2019-01-31(结束)获取日期当月)。
  • @NewbieSQL 然后将结束日期设置为 2019-01-31 而不是使用 getdate() 并将 tmpdate 的第一个值设置为 2018-10-01。
【解决方案3】:

这取决于您的 SQL Server 版本。

with cte as
  (
     select cast(getdate() as date) as   n
     union all
     select  dateadd(DAY,-1,n) from cte where dateadd(DAY,-1,n) > (select eomonth(cast(dateadd(month,-4,getdate()) as date)))
  )
  select * 
  from cte
  order by n desc
  option (maxrecursion 200)

【讨论】:

    【解决方案4】:
    with cte as
      (
      select dateadd(month,1,dateadd(day, -1* day(getdate()) , cast(getdate() as date) )   ) n
      union all
      select  dateadd(day,-1,n) from cte where month(n)  + year(n) * 12 >= month(getdate())  + year(getdate()) * 12 -3
      ),
     final as (select * from cte except  select top 1 * from cte order by n)
    select * from final order by n
    OPTION (MAXRECURSION 1000)
    

    或仅使用 dateadd 并避免使用 except

    with cte as
      (
      select dateadd(day,-1,dateadd(month,1,dateadd(day, 1 - day(getdate()) , cast(getdate() as date)))) n
      union all
      select  dateadd(day,-1,n) from cte where n > dateadd(month,-3,dateadd(day , 1 - day(getdate()),cast(getdate() as date))) 
      )
    select * from cte order by n
    OPTION (MAXRECURSION 1000)
    

    【讨论】:

      【解决方案5】:

      递归不是解决这个问题的好方法。使用递归 cte 来增加计数器的性能与游标相同。 http://www.sqlservercentral.com/articles/T-SQL/74118/

      更好的方法是基于这个集合。对于这项任务,一个计数表是理想的。 Here 是一篇关于该主题的精彩文章。

      我在系统中保留了一个计数表作为视图。零读取速度快如闪电。

      create View [dbo].[cteTally] as
      
      WITH
          E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
          E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
          E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
          cteTally(N) AS 
          (
              SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
          )
      select N from cteTally
      GO
      

      然后对于像这个问题它是超级简单的使用。这将产生相同的结果,没有循环。

      declare @endDate datetime = '2019-01-31'
          , @tmpDate datetime = '2018-10-01'
      
      select dateadd(day, t.N - 1, @tmpDate)
      from cteTally t
      where t.N - 1 <= DATEDIFF(day, @tmpDate, @endDate)
      

      --编辑--

      如果您需要它是动态的,您可以使用一点日期数学。这将获取从 3 个月前开始到当月月底的数据,无论您何时运行它。如果您以前没有见过这种东西,日期逻辑可能有点难以破译。 Lynn Pettis 有一篇关于这个主题的精彩文章。 http://www.sqlservercentral.com/blogs/lynnpettis/2009/03/25/some-common-date-routines/

      select dateadd(day, t.N - 1, dateadd(month, -3, dateadd(month, datediff(month, 0, getdate()), 0)))
      from cteTally t
      where t.N - 1 < datediff(day,dateadd(month, -3, dateadd(month, datediff(month, 0, getdate()), 0)), dateadd(month, datediff(month, 0, getdate()) + 1, 0))
      

      【讨论】:

      • 唯一不使用 rCTE 或(更糟)WHILE 循环的答案(在评论时)。到目前为止,这是正确的答案。
      • 很好的答案,有一个很好的计数表示例。感谢您与我们分享这一点。
      • 我认为他的日期范围不应该是硬编码的,这是他给出的一个例子,当前月份和之前的 3 个月。每个月的第一天都要去改变它会浪费他的时间。这是一种糟糕的方法。
      • @Cato 这也是一个例子。调整它以适应当前月份和前 3 个月是微不足道的。相同的概念只需要使开始日期和结束日期动态化,这并不难。我将在一秒钟内更新此内容以进行演示。
      • 实际上,这部分在他的代码中并没有完全发挥作用,所以这根本不是微不足道的。你实际上并没有给他一个符合他要求的重写。
      【解决方案6】:

      如果您使用的是 SQL 2012+

      SELECT 
          dateadd(dd, number, (dateadd(dd, 1, dateadd(MM, -4, eomonth(getdate()))))) as TheDate
      FROM 
          master..spt_values m1
      WHERE 
          type = 'P' 
      AND dateadd(dd, number, (dateadd(dd, 1, dateadd(MM, -4, eomonth(getdate()))))  ) <= eomonth(getdate())
      

      对于早期版本的 SQL:

      SELECT 
          cast(dateadd(dd, number, dateadd(MM, -3, dateadd(dd, -day(getdate())+1, getdate()))) as date)
      FROM 
          master..spt_values m1
      WHERE 
          type = 'P' 
      AND dateadd(dd, number, dateadd(MM, -3, dateadd(dd, -day(getdate())+1, getdate()))) <= dateadd(MM, 1, dateadd(dd, -day(getdate()) , getdate()))
      

      【讨论】:

        猜你喜欢
        • 2015-05-20
        • 1970-01-01
        • 1970-01-01
        • 2015-11-29
        • 2013-10-17
        • 2016-10-10
        • 2021-07-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多