SQL 按月拆分期间
SQL Split Period by Months
下午好,
我有一个看似简单的问题,但事实证明并不那么简单。我有 2 个约会对象。 BeginPeriod (2010-06-10) 和 EndPeriod (2011-06-11)。
我想看看是否可以将这些日期分解为各自的月度分解。对于上面的例子,类似于
- 2010/06/10 - 2010/06/30
- 2010/07/01 - 2010/07/31
- 2010/08/01 - 2010/08/31
- .......
- 2011/06/01 - 2011/06/10
我不讲究方法。 CTE 很好,但不是首选。正如他们所说,乞丐不能挑剔。
一切顺利,
乔治
它是 CTE。
DECLARE @BeginPeriod DATETIME = '2010-06-10',
@EndPeriod DATETIME = '2011-06-11'
;WITH cte AS
(
SELECT DATEADD(month, DATEDIFF(month, 0, @BeginPeriod), 0) AS StartOfMonth,
DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, @BeginPeriod) + 1, 0)) AS EndOfMonth
UNION ALL
SELECT DATEADD(month, 1, StartOfMonth) AS StartOfMonth,
DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, DATEADD(month, 1, StartOfMonth)) + 1, 0)) AS EndOfMonth
FROM cte
WHERE DATEADD(month, 1, StartOfMonth) <= @EndPeriod
)
SELECT
(CASE WHEN StartOfMonth < @BeginPeriod THEN @BeginPeriod ELSE StartOfMonth END) StartOfMonth,
(CASE WHEN EndOfMonth > @EndPeriod THEN @EndPeriod ELSE EndOfMonth END) EndOfMonth
FROM cte
最后一个 EndOfMonth
是您用作 @EndPeriod
的值,如果您想要前一天
,请将其设置为 DATEADD(day, -1, @EndPeriod)
你可以用这个来trim时间。
SELECT
CONVERT(VARCHAR(10), (CASE WHEN StartOfMonth < @BeginPeriod THEN @BeginPeriod ELSE StartOfMonth END), 120) StartOfMonth,
CONVERT(VARCHAR(10), (CASE WHEN EndOfMonth > @EndPeriod THEN @EndPeriod ELSE EndOfMonth END), 120) EndOfMonth
FROM cte
"Numbers" CTE
的另一个选项
declare @df datetime, @dt datetime
set @df = '20100610'
set @dt = '20110611'
;WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY e1.n) FROM e1 CROSS JOIN e1 AS b) -- 10*10
select
case when e2.n = 1
then @df
else dateadd(day, -day(@df) + 1, dateadd(month, e2.n - 1, @df)) end,
case when e2.n = datediff(month, @df, @dt) + 1
then dateadd(month, e2.n -1 , @df)
else EOMONTH( dateadd(month, e2.n -1 , @df) ) end
from e2
where e2.n
您可以使用其他选项代替 Numbers CTE,例如此处所述
http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1
我的数据库中经常有 Numbers table 用于此类任务。我认为主要是因为我在 CTE 被添加到 MS 之前就开始使用这种技术 SQL 但它也减少了输入。
我知道这是一个老问题,但我最近遇到了同样的问题,并写了一个没有 CTE 的版本。
这是经过测试的code
DECLARE @FromDate DATETIME
, @ToDate DATETIME
, @UpdateBeginDate BIT
, @UpdateEndDate BIT
SET @FromDate = '2019-01-15'
SET @ToDate = '2021-08-17'
SET @UpdateBeginDate = 1
SET @UpdateEndDate = 1
DECLARE @Results TABLE (
MonthStart DATETIME
, MonthEnd DATETIME
, TheMonth VARCHAR(2)
, TheYear VARCHAR(4)
)
-- Months in that period
INSERT INTO @Results
SELECT TOP (DATEDIFF(MONTH, @FromDate, @ToDate)+1) -- calculate how many rows needed
DATEADD(MONTH, number, @FromDate)
, DATEADD(MONTH, number, @FromDate)
, MONTH(DATEADD(MONTH, number, @FromDate))
, YEAR(DATEADD(MONTH, number, @FromDate))
FROM [master].dbo.spt_values
WHERE [type] = N'P'
ORDER BY number
/*
by using [master].dbo.spt_values, the total series number are 2048,
for month cases, there will be 2048/12 ≈ 170 year range
*/
-- Update first date of month and last date of month for each row
UPDATE @Results
SET MonthStart = DATEADD(MONTH, DATEDIFF(MONTH, 0, MonthStart), 0)
, MonthEnd = DATEADD(MONTH, ((YEAR(MonthEnd) - 1900) * 12) + MONTH(MonthEnd), -1)
/*
MonthStart
Caculate the month difference from 1900-01-01 (0 represent 1900-01-01) to the current row's MonthStart record (DATEDIFF part)
Then add the month difference from 1900-01-01 (DATEADD part)
MonthEnd
Caculate the year difference from 1900 to the current row's MonthEnd record then multiply 12 and plus the months of the record (The middle part)
Then add the month difference from 1899-12-31 (DATEADD part)
NOTE: -1 means 1899-12-31
The MonthEnd sentence is equivalent to this, if you keeped the record of TheYear and TheMonth
MonthEnd = DATEADD(MONTH, ((TheYear - 1900) * 12) + TheMonth, -1)
*/
IF @UpdateBeginDate = 1
BEGIN
UPDATE @Results
SET MonthStart = @FromDate
WHERE MonthStart = (SELECT MIN(MonthStart) FROM @Results)
END
IF @UpdateEndDate = 1
BEGIN
UPDATE @Results
SET MonthEnd = @ToDate
WHERE MonthEnd = (SELECT MAX(MonthEnd) FROM @Results)
END
select * from @Results
我在 MSSQL 2005 和 MSSQL 2017 中测试了这段代码,我认为其他版本也可能运行良好。
希望这会帮助其他想要没有 CTE 的解决方案的人。
下午好, 我有一个看似简单的问题,但事实证明并不那么简单。我有 2 个约会对象。 BeginPeriod (2010-06-10) 和 EndPeriod (2011-06-11)。
我想看看是否可以将这些日期分解为各自的月度分解。对于上面的例子,类似于
- 2010/06/10 - 2010/06/30
- 2010/07/01 - 2010/07/31
- 2010/08/01 - 2010/08/31
- .......
- 2011/06/01 - 2011/06/10
我不讲究方法。 CTE 很好,但不是首选。正如他们所说,乞丐不能挑剔。
一切顺利, 乔治
它是 CTE。
DECLARE @BeginPeriod DATETIME = '2010-06-10',
@EndPeriod DATETIME = '2011-06-11'
;WITH cte AS
(
SELECT DATEADD(month, DATEDIFF(month, 0, @BeginPeriod), 0) AS StartOfMonth,
DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, @BeginPeriod) + 1, 0)) AS EndOfMonth
UNION ALL
SELECT DATEADD(month, 1, StartOfMonth) AS StartOfMonth,
DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, DATEADD(month, 1, StartOfMonth)) + 1, 0)) AS EndOfMonth
FROM cte
WHERE DATEADD(month, 1, StartOfMonth) <= @EndPeriod
)
SELECT
(CASE WHEN StartOfMonth < @BeginPeriod THEN @BeginPeriod ELSE StartOfMonth END) StartOfMonth,
(CASE WHEN EndOfMonth > @EndPeriod THEN @EndPeriod ELSE EndOfMonth END) EndOfMonth
FROM cte
最后一个 EndOfMonth
是您用作 @EndPeriod
的值,如果您想要前一天
DATEADD(day, -1, @EndPeriod)
你可以用这个来trim时间。
SELECT
CONVERT(VARCHAR(10), (CASE WHEN StartOfMonth < @BeginPeriod THEN @BeginPeriod ELSE StartOfMonth END), 120) StartOfMonth,
CONVERT(VARCHAR(10), (CASE WHEN EndOfMonth > @EndPeriod THEN @EndPeriod ELSE EndOfMonth END), 120) EndOfMonth
FROM cte
"Numbers" CTE
的另一个选项declare @df datetime, @dt datetime set @df = '20100610' set @dt = '20110611' ;WITH e1(n) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ), -- 10 e2(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY e1.n) FROM e1 CROSS JOIN e1 AS b) -- 10*10 select case when e2.n = 1 then @df else dateadd(day, -day(@df) + 1, dateadd(month, e2.n - 1, @df)) end, case when e2.n = datediff(month, @df, @dt) + 1 then dateadd(month, e2.n -1 , @df) else EOMONTH( dateadd(month, e2.n -1 , @df) ) end from e2 where e2.n
您可以使用其他选项代替 Numbers CTE,例如此处所述 http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1 我的数据库中经常有 Numbers table 用于此类任务。我认为主要是因为我在 CTE 被添加到 MS 之前就开始使用这种技术 SQL 但它也减少了输入。
我知道这是一个老问题,但我最近遇到了同样的问题,并写了一个没有 CTE 的版本。
这是经过测试的code
DECLARE @FromDate DATETIME
, @ToDate DATETIME
, @UpdateBeginDate BIT
, @UpdateEndDate BIT
SET @FromDate = '2019-01-15'
SET @ToDate = '2021-08-17'
SET @UpdateBeginDate = 1
SET @UpdateEndDate = 1
DECLARE @Results TABLE (
MonthStart DATETIME
, MonthEnd DATETIME
, TheMonth VARCHAR(2)
, TheYear VARCHAR(4)
)
-- Months in that period
INSERT INTO @Results
SELECT TOP (DATEDIFF(MONTH, @FromDate, @ToDate)+1) -- calculate how many rows needed
DATEADD(MONTH, number, @FromDate)
, DATEADD(MONTH, number, @FromDate)
, MONTH(DATEADD(MONTH, number, @FromDate))
, YEAR(DATEADD(MONTH, number, @FromDate))
FROM [master].dbo.spt_values
WHERE [type] = N'P'
ORDER BY number
/*
by using [master].dbo.spt_values, the total series number are 2048,
for month cases, there will be 2048/12 ≈ 170 year range
*/
-- Update first date of month and last date of month for each row
UPDATE @Results
SET MonthStart = DATEADD(MONTH, DATEDIFF(MONTH, 0, MonthStart), 0)
, MonthEnd = DATEADD(MONTH, ((YEAR(MonthEnd) - 1900) * 12) + MONTH(MonthEnd), -1)
/*
MonthStart
Caculate the month difference from 1900-01-01 (0 represent 1900-01-01) to the current row's MonthStart record (DATEDIFF part)
Then add the month difference from 1900-01-01 (DATEADD part)
MonthEnd
Caculate the year difference from 1900 to the current row's MonthEnd record then multiply 12 and plus the months of the record (The middle part)
Then add the month difference from 1899-12-31 (DATEADD part)
NOTE: -1 means 1899-12-31
The MonthEnd sentence is equivalent to this, if you keeped the record of TheYear and TheMonth
MonthEnd = DATEADD(MONTH, ((TheYear - 1900) * 12) + TheMonth, -1)
*/
IF @UpdateBeginDate = 1
BEGIN
UPDATE @Results
SET MonthStart = @FromDate
WHERE MonthStart = (SELECT MIN(MonthStart) FROM @Results)
END
IF @UpdateEndDate = 1
BEGIN
UPDATE @Results
SET MonthEnd = @ToDate
WHERE MonthEnd = (SELECT MAX(MonthEnd) FROM @Results)
END
select * from @Results
我在 MSSQL 2005 和 MSSQL 2017 中测试了这段代码,我认为其他版本也可能运行良好。
希望这会帮助其他想要没有 CTE 的解决方案的人。