SQL唯一ID上的累积计数

时间:2015-02-18 16:52:37

标签: sql sql-server

我有如下数据集(按时间列排序):

Time        ID1        ID2
2:00:00 AM  41  A56E34E0-FBE5-4C58-BDBD-87112E73A978
3:00:00 AM  34  B129798B-485E-41BB-8B9C-39A0E1841109
4:00:00 AM  41  A56E34E0-FBE5-4C58-BDBD-87112E73A978
4:00:00 AM  41  C1C14D08-C155-4857-93E2-3A748AC95C8D
4:00:00 AM  34  A4D389B1-C38F-446A-9336-6AA193D8F0E0
4:00:00 AM  17  C1C14D08-C155-4857-93E2-3A748AC95C8D

如果以前没有按时间和ID1分组的同一ID1出现,我想得到ID2的累计计数。因此,对于上述数据集,中间结果可能是:

Time        ID1        ID2                                     IsNewForID1
2:00:00 AM  41  A56E34E0-FBE5-4C58-BDBD-87112E73A978        1
3:00:00 AM  34  B129798B-485E-41BB-8B9C-39A0E1841109        1
4:00:00 AM  41  A56E34E0-FBE5-4C58-BDBD-87112E73A978        0
4:00:00 AM  41  C1C14D08-C155-4857-93E2-3A748AC95C8D        1
4:00:00 AM  34  A4D389B1-C38F-446A-9336-6AA193D8F0E0        1
4:00:00 AM  17  C1C14D08-C155-4857-93E2-3A748AC95C8D        1

按时间分组,ID1将是:

Time        ID1       Count
2:00:00 AM  41  1
3:00:00 AM  34  1
4:00:00 AM  41  1
4:00:00 AM  34  1
4:00:00 AM  17  1

如何在SQL中执行此操作?

2 个答案:

答案 0 :(得分:1)

如果您希望第一次出现id1id2对,并且为什么不使用group by?以下是标准SQL:

select min(time) as time, id1, id2, 1 as count
from dataset
group by id1, id2;

答案 1 :(得分:-1)

您可以使用Gordon Linoff's suggestion作为此类解决方案的起点:

SELECT
  d.Time,
  d.ID1,
  d.ID2,
  IsNewForID1 = CASE WHEN g.Time IS NULL THEN 0 ELSE 1 END
FROM
  YourDataset AS d
  LEFT JOIN (
    SELECT
      Time = MIN(Time),
      ID1,
      ID2,
    FROM
      YourDataset
    GROUP BY
      ID1,
      ID2
  ) AS g ON g.Time = d.Time AND g.ID1 = d.ID1 AND g.ID2 = d.ID2
;

也就是说,派生表包含第一个"新",每个ID2出现ID1,并将它连接回原始数据集以用作参考和标记每一行分别。

如果您使用的是SQL Server 2005或更高版本,则可以使用窗口MIN:

重写上述内容
SELECT
  Time,
  ID1,
  ID2,
  IsNewForID1 = CASE Time
    WHEN MIN(Time) OVER (PARTITION BY ID1, ID2) THEN 1
    ELSE 0
  END
FROM
  YourDataset
;

这个想法和以前一样,但是不需要连接或派生表,因为第一次出现是与细节一起获得的,IsNewForID1列是在同一范围内计算的。如果YourDataset实际上是一个查询,则此方法可能更受欢迎,因为第一个变体可能会评估YourDataset两次,而第二个变体可能会避免这种情况。

显然,要获得最终结果,您可以采用任一查询,然后按TimeID1进一步对其进行分组,以取得SUM(IsForNewID1)

SELECT
  Time,
  ID1,
  Count = SUM(IsNewForID1)
FROM
  (
    SELECT
      Time,
      ID1,
      ID2,
      IsNewForID1 = CASE Time
        WHEN MIN(Time) OVER (PARTITION BY ID1, ID2) THEN 1
        ELSE 0
      END
    FROM
      YourDataset
  ) AS s
;

但请注意,如果实际上只需要IsNewForID1来获取计数,您可以使用Gordon的想法以不同的方式跳过该中间步骤,如下所示:

SELECT
  Time,
  ID1,
  Count = COUNT(*)
FROM
  (
    SELECT
      Time = MIN(Time),
      ID1,
      ID2,
    FROM
      YourDataset
    GROUP BY
      ID1,
      ID2
  ) AS s
;

基本上,无论您是使用IsNewForID1还是使用Count = 0计算结果,结果都是相同的。但是,就行而言,可能存在差异。前一种方法可能会返回Time ID1 Count ---------- --- ----- 2:00:00 AM 41 1 3:00:00 AM 34 1 4:00:00 AM 41 0 4:00:00 AM 34 1 4:00:00 AM 17 1 行。例如,如果您的示例中的第4行不存在,它将返回以下内容:

4:00:00 AM, 41

最后一个方法只会省略计数为0的行,因此如果我们从示例数据中删除第四行,则{{1}}将没有结果。