繁体   English   中英

每个月的第一个SQL

[英]SQL first of every month

假设我想在SQL中编写表值函数,返回一个表,参数日期之间每个月的第一天,这样做的最简单方法是什么?

例如, fnFirstOfMonths('10/31/10', '2/17/11')将返回一个包含fnFirstOfMonths('10/31/10', '2/17/11') 11/1/10, 12/1/10, 1/1/112/1/11作为要素。

我的第一直觉就是使用while循环并重复插入几个月的前几天,直到我到达开始日期之前。 看起来应该有更优雅的方式来做到这一点。

感谢您的任何帮助,您可以提供。

这样的东西可以在不在函数内部的情况下工作:

DECLARE @LowerDate DATE 
SET @LowerDate = GETDATE()

DECLARE @UpperLimit DATE
SET @UpperLimit = '20111231'

;WITH Firsts AS
(
    SELECT
        DATEADD(DAY, -1 * DAY(@LowerDate) + 1, @LowerDate) AS 'FirstOfMonth'

    UNION ALL

    SELECT
        DATEADD(MONTH, 1, f.FirstOfMonth) AS 'FirstOfMonth'
    FROM
        Firsts f
    WHERE
        DATEADD(MONTH, 1, f.FirstOfMonth)  <= @UpperLimit
)   
SELECT * 
FROM Firsts

它使用一种称为CTE(公用表表达式)的东西 - 可在SQL Server 2005及更高版本和其他数据库系统中使用。

在这种情况下,我通过确定指定的@LowerDate日期的月份的第一个开始递归CTE,然后迭代添加一个月到前一个月的第一个月,直到达到上限。

或者,如果您想将其打包在存储函数中,您也可以这样做:

CREATE FUNCTION dbo.GetFirstOfMonth(@LowerLimit DATE, @UpperLimit DATE)
RETURNS TABLE 
AS 
   RETURN
      WITH Firsts AS
      (
          SELECT
             DATEADD(DAY, -1 * DAY(@LowerLimit) + 1, @LowerLimit) AS 'FirstOfMonth'
          UNION ALL
          SELECT
             DATEADD(MONTH, 1, f.FirstOfMonth) AS 'FirstOfMonth'
          FROM
             Firsts f
          WHERE
             DATEADD(MONTH, 1, f.FirstOfMonth)  <= @UpperLimit
       )    
       SELECT * FROM Firsts

然后像这样调用它:

SELECT * FROM dbo.GetFirstOfMonth('20100522', '20100831')

获得这样的输出:

FirstOfMonth
2010-05-01
2010-06-01
2010-07-01
2010-08-01

PS:通过使用DATE数据类型 - 它存在于SQL Server 2008及更新版本中 - 我修复了Richard评论的两个“错误”。 如果您使用的是SQL Server 2005,则必须使用DATETIME - 并处理您获得时间部分的事实。

create function dbo.fnFirstOfMonths(@d1 datetime, @d2 datetime)
returns table as return
select dateadd(m,datediff(m,0,@d1)+v.number,0) as FirstDay
from master..spt_values v
where v.type='P' and v.number between 0 and datediff(m, @d1, @d2)
  and dateadd(m,datediff(m,0,@d1)+v.number,0) between @d1 and @d2
GO

笔记

  • master..spt_values是SQL Server中通用序列号的源
  • dateadd(m,datediff(m是一种计算任何日期的第一天的技巧)
  • + v.number用于每次增加一个月
  • 0和datediff(m,@ d1,@ d2)这个条件给出了我们在@ d1和@ d2之间生成first-of-monthfirst-of-month日期所需的所有number ,包括两个月
  • 和dateadd(m,datediff(m,0,@ d1)+ v.number,0)@ d1和@ d2之间的最终过滤器,用于验证生成的first-of-month日期是否在@ d1和@ d2之间


与marc_s代码的性能比较

摘要

declare @t table (dt datetime)
declare @d datetime
declare @i int
set nocount on

set @d = GETDATE()
set @i = 0
while @i < 10000
begin
insert @t select * from dbo.getfirstofmonth('20090102', '20100506')
delete @t
set @i = @i + 1
end

print datediff(ms, @d, getdate())

set @d = GETDATE()
set @i = 0
while @i < 10000
begin
insert @t select * from dbo.fnfirstofmonths('20090102', '20100506')
delete @t
set @i = @i + 1
end
print datediff(ms, @d, getdate())

Performante

测试

 declare @t table (dt datetime) declare @d datetime declare @i int set nocount on set @d = GETDATE() set @i = 0 while @i < 10000 begin insert @t select * from dbo.getfirstofmonth('20090102', '20100506') delete @t set @i = @i + 1 end print datediff(ms, @d, getdate()) set @d = GETDATE() set @i = 0 while @i < 10000 begin insert @t select * from dbo.fnfirstofmonths('20090102', '20100506') delete @t set @i = @i + 1 end print datediff(ms, @d, getdate()) Performante 

它将在所涉及的月份之间循环(在示例中为4次):

set dateformat mdy;
declare @date1 smalldatetime,@date2 smalldatetime,@i int
set @date1= '10-31-2010'
set @date2= '02-17-2011'
set @i=1

while(@i<=DATEDIFF(mm,@date1,@date2))
begin
    select  convert(smalldatetime,CONVERT(varchar(6),DATEADD(mm,@i,@date1),112)+'01',112)
    set     @i=@i+1
end

我意识到这不是一个功能,但无论如何我都会把它扔进混音中。

select cal_date from calendar
where day_of_month = 1
and cal_date between '2011-01-01' and '2012-01-01'

这个日历表在PostgreSQL服务器上运行。 我今晚将它移植到SQL Server,并运行一些速度比较。 (为什么?因为这个东西很有趣,这就是原因。)

万一有人还在读这个...我无法想象任何上述功能比这更快:

declare @DatFirst date = '20101031', @DatLast date = '21110217';

declare @DatFirstOfFirstMonth date = dateadd(day,1-day(@DatFirst),@DatFirst);

select  DatFirstOfMonth = dateadd(month,n,@DatFirstOfFirstMonth)
from    (
        select  top (datediff(month,@DatFirstOfFirstMonth,@DatLast)+1)
                n=row_number() over (order by (select 1))-1
        from    (values (1),(1),(1),(1),(1),(1),(1),(1)) a (n)
        cross join (values (1),(1),(1),(1),(1),(1),(1),(1)) b (n)
        cross join (values (1),(1),(1),(1),(1),(1),(1),(1)) c (n)
        cross join (values (1),(1),(1),(1),(1),(1),(1),(1)) d (n)
        ) x

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM