[英]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/11
和2/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
first-of-month
的first-of-month
日期所需的所有number
,包括兩個月 first-of-month
日期是否在@ d1和@ d2之間 摘要
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.