Include monthly counts including months where data doesn't exist

后端 未结 1 1599
日久生厌
日久生厌 2020-12-22 06:44

This seems like it should be easy, but I cannot seem to figure it out. I have a table that has an Artifact name and Modification_Date among other t

1条回答
  •  醉梦人生
    2020-12-22 07:17

    Don't convert dates to strings to strip time or day, use date arithmetic. Converting to strings is less efficient and also requires a scan of the entire table.

    If you're going to convert to a fixed length string, don't use NVARCHAR, use CHAR. What Unicode characters are you going to need to support in a numeric date? Umlauts? Pound signs? Hieroglyphics?

    Here is an example that uses a catalog view to generate 6 rows, then subtracts months away from the current date to group by the previous 6 months (and an index on Modification_Date should be used, unlike your current approach). This is not entirely intuitive the first time you see it, but you can see my series on generating sets without loops (part 1 | part 2 | part 3).

    ;WITH x(m) AS 
    (
      SELECT TOP 6 DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) 
        - (ROW_NUMBER() OVER (ORDER BY [object_id])), 0) 
      FROM sys.all_objects
      ORDER BY [object_id]
    )
    SELECT [Month] = x.m, Quantity = COALESCE(COUNT(t.Artifact), 0)
    FROM x
    LEFT OUTER JOIN dbo.tablename AS t
    ON t.Modification_Date >= x.m
    AND t.Modification_Date < DATEADD(MONTH, 1, x.m)
    GROUP BY x.m
    ORDER BY x.m DESC;
    

    Note that this will not include the current month. If you want to shift to include October -> March instead of September -> February, just change this line:

    + 1 - (ROW_NUMBER() OVER (ORDER BY [object_id])), 0) 
    

    And if formatting as YYYY-MM is absolutely essential, you can do this:

    ;WITH y(m) AS 
    (
      SELECT TOP 6 DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) 
        - (ROW_NUMBER() OVER (ORDER BY [object_id])), 0) 
      FROM sys.all_objects
      ORDER BY [object_id]
    ),
    x([Month], Quantity)
    AS
    (
      SELECT [Month] = y.m, Quantity = COALESCE(COUNT(t.Artifact), 0)
      FROM y
      LEFT OUTER JOIN dbo.tablename AS t
      ON t.Modification_Date >= y.m
      AND t.Modification_Date < DATEADD(MONTH, 1, y.m)
      GROUP BY y.m
    )
    SELECT [Month] = CONVERT(CHAR(7), [Month], 120), Quantity
    FROM x
    ORDER BY [Month] DESC;
    

    0 讨论(0)
提交回复
热议问题