【问题标题】:merging two tables, while applying aggregates on the duplicates (max,min and sum)合并两个表,同时对重复项应用聚合(最大、最小和总和)
【发布时间】:2014-06-05 02:10:48
【问题描述】:

我有一个包含几百万条记录的表(我们称之为日志)。在我有 Id、Count、FirstHit、LastHit 的字段中。

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

对于任何给定的 Id,此表只有一条记录

每天我都会进入另一个表(我们称之为提要),其中包含大约 50 万条记录,其中包含许多其他字段:

  • 身份证
  • 时间戳 - 输入日期和时间。

这个表可以有很多相同id的记录

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

需要注意的是,feed 中的很多 id 已经在 log 中了。

简单的方法是创建一个临时表并将两者的并集插入其中

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(firsthit)、max(lasthit) 和 sum(Count)

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

这给了我最终的结果。然后我可以从日志中删除所有内容并用 temp 替换它,或者为公共记录制作更新并插入新记录。但是,我认为两者都非常低效。

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

【问题讨论】:

  • + 1 表示好问题和好演示。

标签: sql sql-server database


【解决方案1】:

如果您的 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);

【讨论】:

    【解决方案2】:

    这里的关键字是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);
    

    【讨论】:

    • 这很有帮助。我不知道 merge 关键字。 IT 确实作为批处理运行,并且提要仅包含前一天(或至少自上次合并以来)的数据。合并提要中的所有内容后都将被删除。
    • 那么请查看更新后的答案。每次运行都使用批处理作业中的合并语句:)
    【解决方案3】:

    我无法测试它,但我认为这应该可以,但不确定它会如何执行:

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-28
      • 2019-08-03
      • 1970-01-01
      • 2018-06-03
      • 2021-01-04
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多