簡體   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