如何在两个日期之间将所有年份、月份显示为 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/201007/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?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2008 仅在月份和年份之间选择数据

获取两个日期之间的所有月份

如何将 jQuery 日期选择器转换为仅选择月份和年份?

如何使用月份和年份格式以精确格式显示日期[重复]

如何仅在月份、日期和年份中更改我的日期选择器显示

求JS两个日期之间的月份数