【问题标题】:How to show all year, months as ints between two dates?如何在两个日期之间将所有年份、月份显示为 int?
【发布时间】: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 美元?

【问题讨论】:

    标签: sql-server tsql sql-server-2000


    【解决方案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 年的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-29
    相关资源
    最近更新 更多