合并两个表,同时在重复项上应用聚合(最大值,最小值和总和)

时间:2014-06-05 02:10:49

标签: sql sql-server database

我有一张桌子(让我们称之为日志),有几百万条记录。我有Id,Count,FirstHit,LastHit。

  • Id - 记录ID
  • 计数 - 报告此ID的次数
  • FirstHit - 报告此Id的最早时间戳
  • LastHit - 报告此ID的最新时间戳

此表只有任何给定Id的记录

每天我都会进入另一张桌子(让我们称之为“饲料”),其中包含大约五十万条记录,其中包括这些字段:

  • Id
  • 时间戳 - 输入日期和时间。

此表可以包含许多相同ID的记录

我想要做的是以下列方式更新日志。 计数 - 日志计数值,加上在Feed中找到的该ID的记录的count() FirstHit - 日志中最早的当前值或该id的feed中的最小值 LastHit - 日志中当前值的最新值或该ID的Feed中的最大值。

应该注意的是,Feed中的许多ID都已记录在日志中。

有用的简单方法是创建一个临时表,并在其中插入两者的并集,如

Select Id, Min(Timestamp) As FirstHit, MAX(Timestamp) as LastHit, Count(*) as Count FROM feed GROUP BY Id
UNION ALL
Select Id, FirstHit,LastHit,Count FROM log;

从那个临时表中我做了一个聚合Min(第一个),最大(最后一个)和总和(计数)的选择

Select Id, Min(FirstHit),Max(LastHit),Sum(Count) FROM @temp GROUP BY Id;

这给了我最终的结果。然后,我可以从日志中删除所有内容,并将其替换为带有temp的所有内容,或者为常见记录创建更新并插入新记录。但是,我认为两者效率都很低。

有没有更有效的方法来做到这一点。也许在日志表中进行更新?

3 个答案:

答案 0 :(得分:2)

如果您的SQL Server版本是2008或更高版本,那么您可以尝试:

MERGE INTO log l
USING (SELECT Id, MIN(Timestamp) AS FirstHit, MAX(Timestamp) AS LastHit, Count(*) as Count FROM feed GROUP BY Id) f
    ON l.Id = f.Id
WHEN MATCHED THEN
    UPDATE SET
        FirstHit = CASE WHEN l.FirstHit < f.FirstHit THEN l.FirstHit ELSE f.FirstHit END,
        LastHit = CASE WHEN l.LastHit > f.LastHit THEN l.LastHit ELSE f.LastHit END,
        Count = l.Count + f.Count
WHEN NOT MATCHED THEN
    INSERT (Id, FirstHit, LastHit, Count)
    VALUES (f.Id, f.FirstHit, f.LastHit, f.Count);

答案 1 :(得分:1)

此处的关键字为EVERYDAY。您应该有一个(批处理)作业,在每天结束时运行该过程。这个想法只处理来自昨天的记录,这比处理整个Feed表更好。

更新信息:

Feed表仅包含上次运行日期的匹配。使用MERGE更新Log表格更容易:

注意:我们可以说FirstHit永远不会更新。仅限LastHitCount。从@dened回答改进。

MERGE INTO log l
USING (SELECT Id, MIN(Timestamp) AS FirstHit, MAX(Timestamp) AS LastHit, Count(*) as TodayHit FROM feed GROUP BY Id) f
    ON l.Id = f.Id
WHEN MATCHED THEN
    UPDATE SET
        LastHit = f.LastHit,
        Count = l.Count + f.TodayHit
WHEN NOT MATCHED THEN
    INSERT (Id, FirstHit, LastHit, Count)
    VALUES (f.Id, f.FirstHit, f.LastHit, f.TodayHit);

答案 2 :(得分:0)

我无法测试它,但我认为这应该可行,但不确定它的表现如何:

select
  ifnull(log.Id,feedsum.Id) as Id
, case when log.FirstHit is null then feedsum.FirstHit
       when feedsum.FirstHit is null then log.FirstHit
       when log.FirstHit<feedsum.FirstHit then log.FirstHit
       else feedsum.FirstHit as FirstHit
, case when log.LastHit is null then feedsum.LastHit
       when feedsum.LastHit is null then log.LastHit
       when log.LastHit>feedsum.LastHit then log.LastHit
       else feedsum.LastHit as LastHit       
from
  log
  full outer join (
      Select Id, Min(Timestamp) As FirstHit, MAX(Timestamp) as LastHit, Count(*) as Count FROM feed GROUP BY Id
    ) feedsum using (Id)