【问题标题】:SQL: Select maximum value for each unique key?SQL:为每个唯一键选择最大值?
【发布时间】:2009-08-08 00:32:23
【问题描述】:

抱歉,我不知道该如何表达,而且我真的不太擅长 SQL。数据库引擎 i SQL Server Compact。我目前有这个查询:

SELECT *
FROM Samples
WHERE FunctionId NOT IN
(SELECT CalleeId FROM Callers)
ORDER BY ThreadId, HitCount DESC

这给了我:

ThreadId   Function  HitCount
       1        164      6945
       1       3817         1
       4       1328      7053

现在,我只想要 Thread 的每个唯一值的最大命中数的结果。换句话说,应该删除第二行。我不知道如何解决这个问题。

[编辑] 如果有帮助,这是同一查询的另一种形式:

SELECT *
FROM Samples s1
LEFT OUTER JOIN Callers c1
    ON s1.ThreadId = c1.ThreadId AND s1.FunctionId = c1.CalleeId
WHERE c1.ThreadId IS NULL
ORDER BY ThreadId

[编辑] 我最终进行了架构更改以避免这样做,因为建议的查询看起来相当昂贵。感谢大家的帮助。

【问题讨论】:

  • 是否有可能有两个 ThreadId 共享相同的 FunctionId,其中一个(ThreadId,FunctionId)在调用者中,而不是另一个?我问,因为上面的两个查询说的不是同一件事。

标签: sql sql-server database


【解决方案1】:

SQL Server compact 是否支持窗口函数?

备选方案 1--将包括所有并列的行。如果给定 Thread 的唯一行的 HitCount 都为 null,则不会包含一行:

SELECT Thread, Function, HitCount
FROM (SELECT Thread, Function, HitCount,
        MAX(HitCount) over (PARTITION BY Thread) as MaxHitCount
    FROM Samples
    WHERE FunctionId NOT IN
        (SELECT CalleeId FROM Callers)) t 
WHERE HitCount = MaxHitCount 
ORDER BY ThreadId, HitCount DESC

备选方案 2--将包括所有并列的行。如果给定线程没有具有非空 HitCount 的行,将返回该线程的所有行:

SELECT Thread, Function, HitCount
FROM (SELECT Thread, Function, HitCount,
        RANK() over (PARTITION BY Thread ORDER BY HitCount DESC) as R
    FROM Samples
    WHERE FunctionId NOT IN
        (SELECT CalleeId FROM Callers)) t
WHERE R = 1
ORDER BY ThreadId, HitCount DESC

备选方案 3--如果出现平局,将不确定地选择一行并丢弃其他行。如果给定线程的所有行的 HitCount 都为空,则将包含一行

SELECT Thread, Function, HitCount
FROM (SELECT Thread, Function, HitCount,
        ROW_NUMBER() over (PARTITION BY Thread ORDER BY HitCount DESC) as R
    FROM Samples
    WHERE FunctionId NOT IN
        (SELECT CalleeId FROM Callers)) t
WHERE R = 1
ORDER BY ThreadId, HitCount DESC

Alternative 4 & 5--如果窗口函数不可用,则使用较旧的构造,并说明比使用连接更简洁的含义。如果速度是优先级,则进行基准测试。两者都返回参与平局的所有行。当非空值不可用于 HitCount 时,备选方案 4 将 HitCount 为空。备选方案 5 不会返回 HitCount 为 null 的行。

SELECT *
FROM Samples s1
WHERE FunctionId NOT IN
    (SELECT CalleeId FROM Callers)
AND NOT EXISTS
    (SELECT *
    FROM Samples s2
    WHERE s1.FunctionId = s2.FunctionId
    AND s1.HitCount < s2.HitCount)
ORDER BY ThreadId, HitCount DESC

SELECT *
FROM Samples s1
WHERE FunctionId NOT IN
    (SELECT CalleeId FROM Callers)
AND HitCount = 
    (SELECT MAX(HitCount)
    FROM Samples s2
    WHERE s1.FunctionId = s2.FunctionId)
ORDER BY ThreadId, HitCount DESC

【讨论】:

    【解决方案2】:

    我会这样做:

    SELECT s1.*
    FROM Samples s1
    LEFT JOIN Samples s2 
      ON (s1.Thread = s2.Thread and s1.HitCount < s2.HitCount)
    WHERE s1.FunctionId NOT IN (SELECT CalleeId FROM Callers) 
      AND s2.Thread IS NULL
    ORDER BY s1.ThreadId, s1.HitCount DESC
    

    换句话说,没有其他行s2匹配相同Thread并且具有更大HitCount的行s1

    【讨论】:

    • 这里有一个令人讨厌的微妙之处——嵌套子句 (SELECT CalleeId FROM Callers) 也必须应用于连接的另一半。我稍微重构了初始查询:SELECT s1.* FROM Samples s1 LEFT OUTER JOIN Callers c1 ON s1.ThreadId = c1.ThreadId AND s1.FunctionId = c1.CalleeId WHERE c1.ThreadId IS NULL
    【解决方案3】:

    适用于 SQL Server 2005+:

    WITH maxHits AS(
      SELECT s.threadid,
             MAX(s.hitcount) 'maxhits'
        FROM SAMPLES s
        JOIN CALLERS c ON c.threadid = s.threadid AND c.calleeid != s.functionid
    GROUP BY s.threadid
    )
    SELECT t.*
      FROM SAMPLES t
      JOIN CALLERS c ON c.threadid = t.threadid AND c.calleeid != t.functionid
      JOIN maxHits mh ON mh.threadid = t.threadid AND mh.maxhits = t.hitcount
    

    在任何数据库上工作:

    SELECT t.*
      FROM SAMPLES t
      JOIN CALLERS c ON c.threadid = t.threadid AND c.calleeid != t.functionid
      JOIN (SELECT s.threadid,
                   MAX(s.hitcount) 'maxhits'
              FROM SAMPLES s
              JOIN CALLERS c ON c.threadid = s.threadid AND c.calleeid != s.functionid
          GROUP BY s.threadid) mh ON mh.threadid = t.threadid AND mh.maxhits = t.hitcount
    

    【讨论】:

    • 如果 (SELECT s.ThreadAID MAX(s.HitCount) FROM SAMPLES s GROUP BY s.ThreadId) 返回一个只属于 FunctionId 在 Callers.CallerId 中的行的最大值怎么办?这些行最终将被删除,并且该线程的 Max(HitCount) 从 FuctionId 不在 Callers.CallerId 的行中被排除在结果之外。
    • 好点。通过在 CTE 或内联视图中将 JOIN 添加到 CALLERS 轻松更正 - 请参阅更新。
    • @rexem。注意到另一个问题:OP 中没有任何内容让我相信 Callers 有一个 ThreadId 列。
    • 是的 - 查看备用查询(第二个查询)。
    猜你喜欢
    • 1970-01-01
    • 2018-03-06
    • 2012-01-04
    • 2020-05-16
    • 2016-11-01
    • 1970-01-01
    • 2020-03-16
    • 2016-12-16
    • 1970-01-01
    相关资源
    最近更新 更多