如何在两个日期之间将所有年份、月份显示为 int?
Posted
技术标签:
【中文标题】如何在两个日期之间将所有年份、月份显示为 int?【英文标题】:How to show all year, months as ints between two dates? 【发布时间】:2011-08-24 20:00:44 【问题描述】:在 SQL Server 2000 中,我需要在客户订单的临时表中以整数形式列出年、月。
如果月份不存在,我仍然需要列出年、月、0.00 美元。
在 SQL Server 2000 中执行此操作的最佳方法是什么?
01/01/2010
到 07/01/2010
会产生:
__Year__, __Month__ , __Billed__
2010,1,$5000.00
2010,2,$6000.00
2010,3,$8000.00
2010,4,$0.00
2010,5,$4000.00
2010,6,$4500.00
使用此代码:
select grp.* from
(
select year(orderdate) as yr,
month(orderdate) as mn,
sum(billed)
from Orders
group by year(orderdate), month(orderdate), billed
) grp
order grp.yr, grp.mn
是否有一个简单的解决方案可以不跳过没有计费的月份并增加 0.00 美元?
【问题讨论】:
【参考方案1】:对于 2005 年以上(对不起,我错过了 2000 年的要求):
DECLARE @Orders TABLE(OrderDate DATETIME, billed INT);
INSERT @Orders SELECT '20100104', 500
UNION SELECT '20100106', 700;
DECLARE
@year INT,
@end_month TINYINT;
SELECT
@year = 2010,
@end_month = 7;
WITH s(n) AS
(
SELECT TOP (@end_month) ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.objects ORDER BY [object_id]
),
m(s) AS
(
SELECT DATEADD(MONTH, n-1, DATEADD(YEAR, @year-1900, 0))
FROM s
)
SELECT
[Year] = @year,
[Month] = MONTH(m.s),
Billed = COALESCE(SUM(t.billed), 0)
FROM m
LEFT OUTER JOIN @Orders AS t
ON t.OrderDate >= m.s
AND t.OrderDate < DATEADD(MONTH, 1, m.s)
GROUP BY
MONTH(m.s)
ORDER BY [Year], [Month];
2000 年只是略有不同:
CREATE TABLE #Orders(OrderDate DATETIME, billed INT);
INSERT #Orders SELECT '20100104', 500
UNION SELECT '20100106', 700;
DECLARE
@year INT,
@end_month TINYINT;
SELECT
@year = 2010,
@end_month = 7;
SELECT
[year] = @year,
[month] = MONTH(m.s),
billed = COALESCE(SUM(t.billed), 0)
FROM
(
SELECT s = DATEADD(MONTH, n-1, DATEADD(YEAR, @year-1900, 0)) FROM
(
SELECT DISTINCT TOP 12 n = number
FROM master..spt_values
WHERE number BETWEEN 1 AND @end_month
ORDER BY number
)
AS s
) AS m
LEFT OUTER JOIN #Orders AS t
ON t.OrderDate >= m.s
AND t.OrderDate < DATEADD(MONTH, 1, m.s)
GROUP BY MONTH(m.s);
现在我承认我没有 2000 个实例可以方便地测试 - 这只是即兴发挥。
【讨论】:
CTE 在 2000 年无效,是吗? 糟糕,我看到了标签 2005 和 2008。 这就是用每个可能的版本标记你的问题的危险! @JNK 是的。无论如何,更新了一个可以在 2000 年运行的版本(我认为)。 @JNK 你能听到我的声音吗? “MWU-HA-HA-HAAAA”【参考方案2】:制作一个包含 0 到 100,000 或其他数字的数字表。
然后是这样的:
DECLARE @StartDate AS DATE
DECLARE @EndDate AS DATE
SELECT YEAR(DATEADD(m, Numbers.N, @StartDate))
,MONTH(DATEADD(m, Numbers.N, @StartDate))
,ISNULL(OrderSummary.Billed, 0) AS Billed
FROM Numbers
LEFT JOIN (
SELECT year(orderdate), month(orderdate), sum(billed)
FROM Orders
GROUP by year(orderdate), month(orderdate)
) AS OrderSummary (Yr, Mn, Billed)
ON YEAR(DATEADD(m, Numbers.N, @StartDate)) = OrderSummary.Yr
AND MONTH(DATEADD(m, Numbers.N, @StartDate)) = OrderSummary.Mn
WHERE Numbers.N < DATEDIFF(m, @StartDate, @EndDate)
在 2005 年使用公用表表达式会更容易一些。
【讨论】:
另外,关于数字,我可以计算开始日期和停止日期之间存在的月数,然后将其添加到临时“数字”表中。 当您最多只有十几个数字时,您不一定需要构建一个数字表(尽管它们很有用) - 如果您需要将其移植到不同的环境(不是所有这些可能已经有一个数字表)你可以使用内置的东西来生成数字。 @RetroCoder 已经以适当方式索引的数字表可以随便放置。您实际上也可能永久拥有一个日期表——这在数据仓库环境中当然是正确的。 我只是按月使用 datediff 来获取日期范围之间的总月数。你也有 2005 年的第二个版本吗? @RetroCoder 我会使用 CTE,比如 Aaron 在 2005 年的回答。以上是关于如何在两个日期之间将所有年份、月份显示为 int?的主要内容,如果未能解决你的问题,请参考以下文章