根据另一列选择具有最高 ID 的行

Posted

技术标签:

【中文标题】根据另一列选择具有最高 ID 的行【英文标题】:Selecting row with highest ID based on another column 【发布时间】:2013-11-09 23:51:59 【问题描述】:

在 SQL Server 2008 R2 中,假设我有这样的表格布局...

+----------+---------+-------------+
| UniqueID | GroupID | Title       |
+----------+---------+-------------+
|    1     |    1    | TEST 1      |
|    2     |    1    | TEST 2      |
|    3     |    3    | TEST 3      |
|    4     |    3    | TEST 4      |
|    5     |    5    | TEST 5      |
|    6     |    6    | TEST 6      |
|    7     |    6    | TEST 7      |
|    8     |    6    | TEST 8      |
+----------+---------+-------------+

是否可以为每个 GroupID 选择具有最高 UniqueID 编号的每一行。所以根据上表 - 如果我运行查询,我会期望这个......

+----------+---------+-------------+
| UniqueID | GroupID | Title       |
+----------+---------+-------------+
|    2     |    1    | TEST 2      |
|    4     |    3    | TEST 4      |
|    5     |    5    | TEST 5      |
|    8     |    6    | TEST 8      |
+----------+---------+-------------+

一直在咀嚼这个,但似乎无法破解它。

非常感谢,

【问题讨论】:

更新为包含 DB 类型 - SQL Server 2008 R2 【参考方案1】:

由于您没有提到任何 RDBMS,因此下面的这条语句几乎适用于所有 RDBMS。子查询的目的是为每个GROUPID 获得最大的uniqueID。为了能够获取其他列,将子查询的结果连接到原始表上。

SELECT  a.*
FROM    tableName a
        INNER JOIN
        (
            SELECT  GroupID, MAX(uniqueID) uniqueID
            FROM    tableName
            GROUP   By GroupID
        ) b ON a.GroupID = b.GroupID
                AND a.uniqueID = b.uniqueID

如果您的 RDBMS 支持 Qnalytic 功能,您可以使用ROW_NUMBER()

SELECT uniqueid,  groupid,  title
FROM   
(
SELECT uniqueid,  groupid,  title, 
        ROW_NUMBER() OVER (PARTITION BY groupid 
                           ORDER BY uniqueid DESC)  rn 
FROM   tableName
) x
WHERE  x.rn = 1 
TSQL Ranking Functions

ROW_NUMBER() 生成可以过滤掉的序列号。在这种情况下,序列号在groupid 上生成,并按uniqueid 降序排序。最大的uniqueid 将在rn 中具有1 的值。

【讨论】:

谢谢...+1 解释了行号部分的工作原理。我想知道,但这是有道理的。【参考方案2】:
SELECT * 
FROM   (SELECT uniqueid,  groupid,  title, 
               Row_number() 
                 OVER ( partition BY groupid ORDER BY uniqueid DESC) AS rn 
        FROM   table) a 
WHERE  a.rn = 1 

【讨论】:

非常感谢您在超快的时间内提供所有答案 - 非常感谢。谁能告诉我哪个答案被认为是最有效的? @Simon:你可以对它们进行基准测试。智能查询优化器/计划器将为所有给定的解决方案创建相同的计划。 (在所有其他情况下,我的解决方案可能是最快的)【参考方案3】:

使用 SQL-Server 作为 rdbms,您可以使用像 ROW_NUMBER 这样的排名函数:

WITH CTE AS
(
    SELECT UniqueID, GroupID, Title,
       RN = ROW_NUMBER() OVER (PARTITON BY GroupID
                               ORDER BY UniqueID DESC)
    FROM dbo.TableName
)
SELECT UniqueID, GroupID, Title
FROM CTE
WHERE RN = 1

这会为每个 GroupID 返回一个记录,即使有多个行的最高 UniqueID 也是如此(名称并非如此)。如果要返回所有行,请使用 DENSE_RANK 而不是 ROW_NUMBER

您可以在此处查看所有功能及其工作方式:http://technet.microsoft.com/en-us/library/ms189798.aspx

【讨论】:

使用 WITH 会比嵌套 SELECT 快吗? 两者的性能应该相同。公用表表达式的主要优点(当不使用它进行递归查询时)是封装,而不是必须在您希望使用它的每个地方声明子查询,您可以定义一次,但有多个引用给它。 我已将此标记为答案,因为出于上述原因,封装正是我需要将此查询纳入更大的图景 - 尽管 Royi Namirs 的答案在上下文中也很有效。非常感谢帮助的人 - 感谢大家的回答。 @TimSchmelter 他认为他可以在文档中的任何地方使用它。 (他认为这是一个参考)嗯 - 它不是。并且只能在 CTE decleration 之后立即使用(如您所知)。你应该跟他说清楚。我个人不会使用 CTE 。在同一查询中进行多引用时,通常使用 CTE。 (不是你想要的时候),当然在递归 cte 中。 @RoyiNamir:您可以连接多个 CTE 并在最终查询中引用另一个 CTE。 MSDN:“使用 CTE 具有提高可读性和易于维护复杂查询的优点。查询可以分为单独的、简单的逻辑构建块。这些简单的块可以用于构建更复杂的临时 CTE,直到最终结果集生成。”【参考方案4】:
SELECT *
FROM the_table tt
WHERE NOT EXISTS (
    SELECT *
    FROM the_table nx
    WHERE nx.GroupID = tt.GroupID
    AND nx.UniqueID > tt.UniqueID
    )
    ;
应该在任何 DBMS 中工作(不需要窗口函数或 CTE) 可能比带有聚合的子查询更快

【讨论】:

【参考方案5】:

保持简单:

select * from test2
where UniqueID in (select max(UniqueID) from test2 group by GroupID)


Considering:

create table test2
(
UniqueID numeric,
GroupID numeric,
Title varchar(100)
)

insert into test2 values(1,1,'TEST 1')
insert into test2 values(2,1,'TEST 2')
insert into test2 values(3,3,'TEST 3')
insert into test2 values(4,3,'TEST 4')
insert into test2 values(5,5,'TEST 5')
insert into test2 values(6,6,'TEST 6')
insert into test2 values(7,6,'TEST 7')
insert into test2 values(8,6,'TEST 8')

【讨论】:

以上是关于根据另一列选择具有最高 ID 的行的主要内容,如果未能解决你的问题,请参考以下文章

SQL:根据另一列的值在列上保留一个具有最大值的行

从表中选择行,其中具有相同 id 的另一个表中的行在另一列中具有特定值

如何选择与 PostgreSQL 中另一列的最高值的唯一列值对对应的行?

在根据最大列值理解行的同时查找具有最高值的行的列名

如何查询仅出现特定列中具有最高值的行的行?

根据另一列的先前值填充一列