按DateTime字段分组

时间:2013-12-24 21:18:41

标签: sql tsql datetime sql-server-2008-r2 group-by

我遇到了一个有趣的问题,当然可以使用一些帮助。我有一个查询,可以提取一天中发生的“事件”。目标是按日对事件进行分组,并计算当天发生的事件数量。

我遇到的问题是日期时间字段对于每个事件都有不同的时间,这使得分组变得非常困难。请注意,“TheDate”必须有2个字段 - 一个将显示给用户(NVARCHAR转换),另一个用于进行日期范围搜索。此查询将存储在视图中。

查询虽然没有按照我的意愿执行,也没有考虑到这一点,看起来像这样:

SELECT TheDate, Username, CONVERT(NVARCHAR(10), TheDate, 120) 'Date',  
        (SUM(CASE WHEN itemID = 1 THEN 1 ELSE 0 END)) AS Item1,
        (SUM(CASE WHEN itemID = 2 THEN 1 ELSE 0 END)) AS Item2,
        (SUM(CASE WHEN itemID = 3 THEN 1 ELSE 0 END)) AS Item3,
        (SUM(CASE WHEN itemID = 4 THEN 1 ELSE 0 END)) AS Item4,
        (SUM(CASE WHEN itemID = 5 THEN 1 ELSE 0 END)) AS Item5
FROM         Table1 AS a INNER JOIN
                  Table2 AS tab2 ON tab2.Table1ID = a.Table1ID LEFT OUTER JOIN
                  Table3 AS tab3 ON tab3.Table2ID = tab2.Table2ID
WHERE someOtherID = 3 AND UserID = 40
GROUP BY CONVERT(NVARCHAR(10), TheDate, 120), TheDate, Username
ORDER BY CONVERT(NVARCHAR(10), TheDate, 120)

UNION 

    SELECT TheDate, Username, 'GrandTotal',  
        (SUM(CASE WHEN itemID = 1 THEN 1 ELSE 0 END)) AS Item1,
        (SUM(CASE WHEN itemID = 2 THEN 1 ELSE 0 END)) AS Item2,
        (SUM(CASE WHEN itemID = 3 THEN 1 ELSE 0 END)) AS Item3,
        (SUM(CASE WHEN itemID = 4 THEN 1 ELSE 0 END)) AS Item4,
        (SUM(CASE WHEN itemID = 5 THEN 1 ELSE 0 END)) AS Item5
FROM         Table1 AS a INNER JOIN
                  Table2 AS tab2 ON tab2.Table1ID = a.Table1ID LEFT OUTER JOIN
                  Table3 AS tab3 ON tab3.Table2ID = tab2.Table2ID
WHERE someOtherID = 3 AND UserID = 40
GROUP BY TheDate, Username

这种分组都搞砸了,原因很明显。我希望按日分组结果,而不是每天10-20行,因为日期时间字段TheDate上列出了小时/分钟/秒。

尝试以某种方式修复此问题,我尝试了类似以下内容:

SELECT * FROM(
SELECT UserName, DATEADD(DAY, DATEDIFF(day, 0, TheDate), 0) AS TheDate,
       CONVERT(NVARCHAR(10), TheDate, 120) 'Date',  
        (SUM(CASE WHEN itemID = 1 THEN 1 ELSE 0 END)) AS Item1,
        (SUM(CASE WHEN itemID = 2 THEN 1 ELSE 0 END)) AS Item2,
        (SUM(CASE WHEN itemID = 3 THEN 1 ELSE 0 END)) AS Item3,
        (SUM(CASE WHEN itemID = 4 THEN 1 ELSE 0 END)) AS Item4,
        (SUM(CASE WHEN itemID = 5 THEN 1 ELSE 0 END)) AS Item5
FROM         Table1 AS a INNER JOIN
                  Table2 AS tab2 ON tab2.Table1ID = a.Table1ID LEFT OUTER JOIN
                  Table3 AS tab3 ON tab3.Table2ID = tab2.Table2ID
WHERE someOtherID = 3 AND UserID = 40
GROUP BY CONVERT(NVARCHAR(10), TheDate, 120), TheDate, UserName
ORDER BY CONVERT(NVARCHAR(10), TheDate, 120)

UNION 

    SELECT Username, DATEADD(DAY, DATEDIFF(day, 0, TheDate), 0) AS TheDate, 
        'GrandTotal',  
        (SUM(CASE WHEN itemID = 1 THEN 1 ELSE 0 END)) AS Item1,
        (SUM(CASE WHEN itemID = 2 THEN 1 ELSE 0 END)) AS Item2,
        (SUM(CASE WHEN itemID = 3 THEN 1 ELSE 0 END)) AS Item3,
        (SUM(CASE WHEN itemID = 4 THEN 1 ELSE 0 END)) AS Item4,
        (SUM(CASE WHEN itemID = 5 THEN 1 ELSE 0 END)) AS Item5
FROM         Table1 AS a INNER JOIN
                  Table2 AS tab2 ON tab2.Table1ID = a.Table1ID LEFT OUTER JOIN
                  Table3 AS tab3 ON tab3.Table2ID = tab2.Table2ID
WHERE someOtherID = 3 AND UserID = 40
GROUP BY UserName, TheDate) AS X
GROUP BY X.TheDate, X.Date, X.UserName, X.Item1, X.Item2, X.Item3, X.Item4, X.Item5

我的目标是从日期时间中移除时间。可见,在结果中,它起作用了。日期时间结果现在看起来都像

2013-12-24 00:00:00.000

不幸的是,从技术上讲,它没有做任何事情。我每天仍然有10-20行,就像我看到时间一样。

我希望我的结果看起来像这样(记住,这是进入一个视图,所以我不会明显选择Username和TheDate供用户查看。这些仅用于where子句目的):

Date        Item2   Item2   Item3   Item4   Item5
2013-05-31  1       30      0       0       129
2013-06-03  0       90      0       15      78
2013-06-04  0       50      0       1       124
2013-06-05  0       100     0       17      58
2013-06-06  0       24      0       0       105
2013-06-07  0       50      0       1       83
2013-06-10  0       45      2       42      64
2013-06-11  3       43      0       14      90
2013-06-12  2       44      0       36      88
2013-06-13  1       34      0       15      92
GRAND TOTAL 7       510     2       141     911

有没有人建议解决此问题的最佳方法,并允许我按天分组每组行,而不是因为日期时间不同而每天多行?

谢谢!

2 个答案:

答案 0 :(得分:3)

您正在使用SQL Server 2008,因此请使用cast(xxx as date) - 一种更简单的语法。并将它们放在selectgroup by子句中:

SELECT * FROM(
SELECT UserName, cast(TheDate as date) AS TheDate,
       CONVERT(NVARCHAR(10), TheDate, 120) 'Date',  
        (SUM(CASE WHEN itemID = 1 THEN 1 ELSE 0 END)) AS Item1,
        (SUM(CASE WHEN itemID = 2 THEN 1 ELSE 0 END)) AS Item2,
        (SUM(CASE WHEN itemID = 3 THEN 1 ELSE 0 END)) AS Item3,
        (SUM(CASE WHEN itemID = 4 THEN 1 ELSE 0 END)) AS Item4,
        (SUM(CASE WHEN itemID = 5 THEN 1 ELSE 0 END)) AS Item5
FROM         Table1 AS a INNER JOIN
                  Table2 AS tab2 ON tab2.Table1ID = a.Table1ID LEFT OUTER JOIN
                  Table3 AS tab3 ON tab3.Table2ID = tab2.Table2ID
WHERE someOtherID = 3 AND UserID = 40
GROUP BY CONVERT(NVARCHAR(10), TheDate, 120), cast(TheDate as date), UserName
ORDER BY CONVERT(NVARCHAR(10), TheDate, 120)

UNION 

    SELECT Username, cast(TheDate as date) AS TheDate, 
        'GrandTotal',  
        (SUM(CASE WHEN itemID = 1 THEN 1 ELSE 0 END)) AS Item1,
        (SUM(CASE WHEN itemID = 2 THEN 1 ELSE 0 END)) AS Item2,
        (SUM(CASE WHEN itemID = 3 THEN 1 ELSE 0 END)) AS Item3,
        (SUM(CASE WHEN itemID = 4 THEN 1 ELSE 0 END)) AS Item4,
        (SUM(CASE WHEN itemID = 5 THEN 1 ELSE 0 END)) AS Item5
FROM         Table1 AS a INNER JOIN
                  Table2 AS tab2 ON tab2.Table1ID = a.Table1ID LEFT OUTER JOIN
                  Table3 AS tab3 ON tab3.Table2ID = tab2.Table2ID
WHERE someOtherID = 3 AND UserID = 40
GROUP BY UserName, cast(TheDate as date)) AS X
GROUP BY X.TheDate, X.Date, X.UserName, X.Item1, X.Item2, X.Item3, X.Item4, X.Item5

您的查询存在的问题是您拥有构造:

select <expression> as TheDate
. . .
group by TheDate

其中TheDate也是表格中的一列。 group by使用表中的列,而不是计算值。实际上,SQL Server允许在group by子句中使用列别名。

答案 1 :(得分:2)

如果您想按日期分组,可以将DATETIME列转换为DATE类型(在SQL Server 2008或更高版本中)。这将删除时间部分。

SELECT CAST(TheDate AS DATE) as TheDate, ...

GROUP BY CAST(TheDate AS DATE), Username