在SQL中使用日期聚合多个表

时间:2017-01-20 10:18:30

标签: sql sql-server aggregate

我有一个联系人列表,以及三个包含不同信息的表格,其中包含有关联系人的日期相关数据。

我想做的是显示每个表的每月计数:

ID Date       Hist Trck Evt
1  2016-01-01    0    0   0
1  2016-02-01    1    0   1
1  2016-03-01    2    0   1
1  2016-04-01    0    5   1

我通过对PersonId和Date进行分组来单独获得每种类型的结果:

SELECT PersonId AS CID, 
       HistoryDate,
       COUNT(id) Records 
FROM @History  
GROUP BY PersonId, 
         HistoryDate

这会得到3个数据表的总数,但不按人员ID分组:

SELECT DateMonth, pvt.[EH], pvt.[ET], pvt.[EV] 
FROM
(
    SELECT Id AS CID,  HistoryDate AS DateMonth, 'EH' AS RecordType, COUNT(id) Records FROM @History   GROUP BY Id,  HistoryDate
    UNION ALL
    SELECT Id AS CID,  TrackingDate AS DateMonth, 'ET' AS RecordType, COUNT(id) Records FROM @Tracking GROUP BY Id,  TrackingDate
    UNION ALL
    SELECT Id AS CID, EventDate  AS DateMonth, 'EV' AS RecordType, COUNT(id) Records FROM @Events      GROUP BY Id, EventDate
) as x
PIVOT( COUNT(CID) FOR RecordType IN ( [EH], [ET], [EV] ) ) as pvt

DateMonth               EH          ET          EV
----------------------- ----------- ----------- -----------
2016-02-01 00:00:00.000 1           1           1
2016-03-01 00:00:00.000 2           0           1
2016-04-01 00:00:00.000 0           5           1
2016-05-01 00:00:00.000 1           2           0
2016-06-01 00:00:00.000 2           2           0
2016-07-01 00:00:00.000 2           0           0
2016-08-01 00:00:00.000 2           0           0

我似乎无法向枢轴添加第二列,因此它有IdDateMonth,然后是三个透视字段。

我正在努力将这些内容与联系人数据合并到一个记录集中。

我实际得到的是每个联系人和日期的大量记录,而不是一个。

我怀疑这是显而易见的事情,但是随着时间的推移,这些要求已经发生了变化(实际上这些表格很复杂而且非常庞大)所以我可能只是让我的思绪陷入了死胡同。

这是一些示例架构。 (您也可以运行它here

DECLARE @Contacts TABLE (
    Id int not null,
    Email nvarchar(50)
);

DECLARE @History TABLE (
    Id int not null,
    PersonId int,
    HistoryDate datetime
);

DECLARE @Tracking TABLE (
    Id int not null,
    PersonId int,
    TrackingDate datetime
);

DECLARE @Events TABLE (
    Id int not null,
    PersonId int,
    EventDate datetime
);

以下是一位联系人的一些示例数据:

INSERT INTO @Contacts VALUES (1, 'someone@gmail.com');

INSERT INTO @History VALUES (1, 1, '2016-02-01');
INSERT INTO @History VALUES (2, 1, '2016-03-01');
INSERT INTO @History VALUES (3, 1, '2016-03-01');
INSERT INTO @History VALUES (4, 1, '2016-05-01');
INSERT INTO @History VALUES (5, 1, '2016-06-01');
INSERT INTO @History VALUES (6, 1, '2016-06-01');
INSERT INTO @History VALUES (7, 1, '2016-07-01');
INSERT INTO @History VALUES (8, 1, '2016-07-01');
INSERT INTO @History VALUES (9, 1, '2016-08-01');
INSERT INTO @History VALUES (10,1, '2016-08-01');

INSERT INTO @Tracking VALUES (1,  1, '2016-02-01');
INSERT INTO @Tracking VALUES (2,  1, '2016-04-01');
INSERT INTO @Tracking VALUES (3,  1, '2016-04-01');
INSERT INTO @Tracking VALUES (4,  1, '2016-04-01');
INSERT INTO @Tracking VALUES (5,  1, '2016-04-01');
INSERT INTO @Tracking VALUES (6,  1, '2016-04-01');
INSERT INTO @Tracking VALUES (7,  1, '2016-05-01');
INSERT INTO @Tracking VALUES (8,  1, '2016-05-01');
INSERT INTO @Tracking VALUES (9,  1, '2016-06-01');
INSERT INTO @Tracking VALUES (19, 1, '2016-06-01');

INSERT INTO @Events VALUES (1, 1, '2016-02-01');
INSERT INTO @Events VALUES (2, 1, '2016-03-01');
INSERT INTO @Events VALUES (3, 1, '2016-04-01');

3 个答案:

答案 0 :(得分:1)

您的查询修补程序

  1. 由于您在PIVOT子句(CID)中使用了COUNT(CID),因此被用作PIVOT聚合列之一,您无法选择它。
  2. 由于您使用中间聚合,因此它应该是SUM而不是COUNT。
  3. PersonIdId
  4. 不匹配
    • 我已将COUNT(CID)替换为SUM(Recrods)并将CID添加到SELECT子句中。
    • 我已将PersonId替换为Id
    • 您可能希望用coalesce (...,0)
    • 替换eh / et / ev
    SELECT CID,DateMonth, pvt.[EH], pvt.[ET], pvt.[EV] 
    FROM
    (
        SELECT PersonId AS CID,  HistoryDate AS DateMonth, 'EH' AS RecordType, COUNT(id) Records FROM @History   GROUP BY PersonId,  HistoryDate
        UNION ALL
        SELECT PersonId AS CID,  TrackingDate AS DateMonth, 'ET' AS RecordType, COUNT(id) Records FROM @Tracking GROUP BY PersonId,  TrackingDate
        UNION ALL
        SELECT PersonId AS CID, EventDate  AS DateMonth, 'EV' AS RecordType, COUNT(id) Records FROM @Events      GROUP BY PersonId, EventDate
    ) as x
    PIVOT( SUM(Records) FOR RecordType IN ( [EH], [ET], [EV] ) ) as pvt
    

    更清洁的解决方案

    1. 无需中间组
    2. 不需要UNION ALL部件的单独别名
    3. select  cid,datemonth,h,t,v
      
      from    (           select PersonId, historydate  ,'h' from @history  
              union all   select PersonId, trackingdate ,'t' from @tracking 
              union all   select PersonId, eventdate    ,'v' from @events   
              ) as t (cid,datemonth,recordtype) 
                  pivot (count(recordtype) for recordtype in (h,t,v)) as p
      
      +-----+-------------------------+----+----+----+
      | cid | datemonth               | h  | t  | v  |
      +-----+-------------------------+----+----+----+
      | 1   | 2016-02-01 00:00:00.000 | 1  | 1  | 1  |
      +-----+-------------------------+----+----+----+
      | 1   | 2016-03-01 00:00:00.000 | 2  | 0  | 1  |
      +-----+-------------------------+----+----+----+
      | 1   | 2016-04-01 00:00:00.000 | 0  | 5  | 1  |
      +-----+-------------------------+----+----+----+
      | 1   | 2016-05-01 00:00:00.000 | 1  | 2  | 0  |
      +-----+-------------------------+----+----+----+
      | 1   | 2016-06-01 00:00:00.000 | 2  | 2  | 0  |
      +-----+-------------------------+----+----+----+
      | 1   | 2016-07-01 00:00:00.000 | 2  | 0  | 0  |
      +-----+-------------------------+----+----+----+
      | 1   | 2016-08-01 00:00:00.000 | 2  | 0  | 0  |
      +-----+-------------------------+----+----+----+
      

答案 1 :(得分:0)

在postsgresql中你可以使用

GROUP BY date_trunc('month', date_column) 

答案 2 :(得分:0)

可能你可以做得更好但是有效。

WITH eventss as (
SELECT COUNT(ID) as cnt, PERSONID, YEAR(eventdate) as y, MONTH(eventdate) as m
FROM @Events
GROUP BY PERSONID, YEAR(eventdate), MONTH(eventdate)
),
 tracking as (
SELECT COUNT(ID) as cnt, PERSONID, YEAR(trackingdate) as y, MONTH(trackingdate) as m
FROM @Tracking
GROUP BY PERSONID, YEAR(trackingdate), MONTH(trackingdate)
),
 history as (
SELECT COUNT(ID) as cnt, PERSONID, YEAR(historydate) as y, MONTH(historydate) as m
FROM @History
GROUP BY PERSONID, YEAR(historydate), MONTH(historydate)
),
ttime as (  SELECT YEAR(historydate) as y,MONTH(historydate) as m FROM @History UNION
            SELECT YEAR(trackingdate) as y,MONTH(trackingdate) as m FROM @Tracking UNION
            SELECT YEAR(eventdate) as y,MONTH(eventdate) as m FROM @Events 
)
SELECT c.id, DATEFROMPARTS(tt.y,tt.m,1) as date, ISNULL(h.cnt,0) as Hist, ISNULL(tr.cnt,0) as Trck, ISNULL(e.cnt,0) as Evt
FROM @Contacts AS c
CROSS JOIN ttime AS tt
LEFT JOIN eventss AS e ON c.Id=e.PERSONID AND tt.m=e.m AND tt.y=e.y
LEFT JOIN tracking AS tr ON c.Id=tr.PERSONID AND tt.m=tr.m AND tt.y=tr.y
LEFT JOIN history AS h ON c.Id=h.PERSONID AND tt.m=h.m AND tt.y=h.y