获取每组的前 1 行
Posted
技术标签:
【中文标题】获取每组的前 1 行【英文标题】:Get top 1 row of each group 【发布时间】:2011-10-14 01:43:13 【问题描述】:我有一张表,我想获取每个组的最新条目。这是表格:
DocumentStatusLogs
表
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
表格将按DocumentID
分组,并按DateCreated
降序排序。对于每个DocumentID
,我想获取最新状态。
我的首选输出:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
是否有任何聚合函数可以仅获取每个组的顶部?请参阅下面的伪代码GetOnlyTheTop
:
SELECT
DocumentID,
GetOnlyTheTop(Status),
GetOnlyTheTop(DateCreated)
FROM DocumentStatusLogs
GROUP BY DocumentID
ORDER BY DateCreated DESC
如果不存在这样的功能,有什么方法可以实现我想要的输出吗?
或者首先,这可能是由未规范化的数据库引起的吗?我在想,既然我要找的只是一行,那status
是否也应该位于父表中?
更多信息请查看父表:
当前Documents
表
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
父表是否应该这样,以便我可以轻松访问其状态?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
更新 我刚刚学会了如何使用“应用”,它可以更轻松地解决此类问题。
【问题讨论】:
要更详细地讨论和比较可能的解决方案,我建议阅读 dba.se 上的类似问题:Retrieving n rows per group。 我看了帖子试了一下。使用 按 StoreID 分组 产生错误。 相关:Select first row in each GROUP BY group? 这能回答你的问题吗? Select first row in each GROUP BY group? 【参考方案1】:;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) AS rn
FROM DocumentStatusLogs
)
SELECT *
FROM cte
WHERE rn = 1
如果您希望每天有 2 个条目,那么这将任意选择一个。要获得一天的两个条目,请改用 DENSE_RANK
至于是否归一化,就看你想不想了:
在 2 个地方保持状态 保留状态历史记录 ...就目前而言,您可以保留状态历史记录。如果您也想要父表中的最新状态(这是非规范化),您需要一个触发器来维护父表中的“状态”。或删除此状态历史记录表。
【讨论】:
还有...Partition By
是什么? With
对我来说也是新的 :( 反正我使用的是 mssql 2005。
@domanokz:Partition By 重置计数。因此,在这种情况下,它表示按 DocumentID 计数
嗯,我担心性能,我将查询数百万行。 SELECT * FROM (SELECT ...) 会影响性能吗?另外,ROW_NUMBER
每行是某种子查询吗?
@domanokz:不,这不是子查询。如果您有正确的索引,那么数百万应该不是问题。无论如何,只有两种基于集合的方式:this 和聚合(Ariel 的解决方案)。所以尝试他们两个......
@domanokz: 只需将 ORDER BY DateCreated DESC 更改为 ORDER BY ID DESC【参考方案2】:
SELECT * FROM
DocumentStatusLogs JOIN (
SELECT DocumentID, MAX(DateCreated) DateCreated
FROM DocumentStatusLogs
GROUP BY DocumentID
) max_date USING (DocumentID, DateCreated)
什么数据库服务器?此代码不适用于所有这些。
关于您问题的后半部分,我认为将状态列为一列似乎是合理的。您可以将DocumentStatusLogs
保留为日志,但仍将最新信息存储在主表中。
顺便说一句,如果您在 Documents 表中已有 DateCreated
列,则可以使用它加入 DocumentStatusLogs
(只要 DateCreated
在 DocumentStatusLogs
中是唯一的)。
编辑:MsSQL 不支持 USING,所以改成:
ON DocumentStatusLogs.DocumentID = max_date.DocumentID AND DocumentStatusLogs.DateCreated = max_date.DateCreated
【讨论】:
线索就在标题中:MSSQL。 SQL Server 没有 USING 但思路还行。 @gbn 愚蠢的版主通常会从标题中删除重要的关键字,就像他们在这里所做的那样。很难在搜索结果或 Google 中找到正确答案。 只是要指出,如果您在max(DateCreated)
上打成平手,这个“解决方案”仍然可以为您提供多条记录
去掉Using(在MS SQL中)并完成Join代码,就可以了。【参考方案3】:
我刚刚学会了如何使用cross apply
。以下是在这种情况下如何使用它:
select d.DocumentID, ds.Status, ds.DateCreated
from Documents as d
cross apply
(select top 1 Status, DateCreated
from DocumentStatusLogs
where DocumentID = d.DocumentId
order by DateCreated desc) as ds
【讨论】:
这实际上并没有什么区别,因为问题仍然得到解决。 我刚刚发布了针对所有提议的解决方案的时序测试结果,而您的则名列前茅。给你一个赞成票:-) +1 大幅提升速度。这比诸如 ROW_NUMBER() 之类的窗口函数要快得多。如果 SQL 能够识别 ROW_NUMBER() = 1 类似的查询并将它们优化为应用程序,那就太好了。注意:我使用 OUTER APPLY 是因为我需要结果,即使它们在申请中不存在。 @TamusJRoyce 您无法推断出这一点,因为一旦情况总是如此,它就会更快。这取决于。如此处所述sqlmag.com/database-development/optimizing-top-n-group-queries 如果您已经有一个单独的Documents
表,该表根据输出的需要为每组提供一行,则此方法效果很好。但是,如果您只使用一张表(在这种情况下为DocumentStatusLogs
),您首先必须对DocumentID
(或ROW_NUMBER()
、MAX(ID)
等)执行某种DISTINCT
操作。 ),失去所有获得的性能。【参考方案4】:
在你想避免使用 row_count() 的场景中,你也可以使用左连接:
select ds.DocumentID, ds.Status, ds.DateCreated
from DocumentStatusLogs ds
left join DocumentStatusLogs filter
ON ds.DocumentID = filter.DocumentID
-- Match any row that has another row that was created after it.
AND ds.DateCreated < filter.DateCreated
-- then filter out any rows that matched
where filter.DocumentID is null
对于示例模式,您还可以使用“不在子查询中”,它通常编译为与左连接相同的输出:
select ds.DocumentID, ds.Status, ds.DateCreated
from DocumentStatusLogs ds
WHERE ds.ID NOT IN (
SELECT filter.ID
FROM DocumentStatusLogs filter
WHERE ds.DocumentID = filter.DocumentID
AND ds.DateCreated < filter.DateCreated)
请注意,如果表没有至少一个单列唯一键/约束/索引(在本例中为主键“Id”),则子查询模式将不起作用。
这两个查询往往比 row_count() 查询更“昂贵”(由查询分析器衡量)。但是,您可能会遇到它们返回结果更快或启用其他优化的情况。
【讨论】:
【参考方案5】:我的代码从每个组中选择前 1 个
select a.* from #DocumentStatusLogs a where 创建日期(从 #DocumentStatusLogs b 中选择前 1 个创建日期 在哪里 a.documentid = b.documentid 按日期创建 desc 排序 )
【讨论】:
【参考方案6】:如果你担心性能,你也可以用 MAX() 来做到这一点:
SELECT *
FROM DocumentStatusLogs D
WHERE DateCreated = (SELECT MAX(DateCreated) FROM DocumentStatusLogs WHERE ID = D.ID)
ROW_NUMBER() 需要对 SELECT 语句中的所有行进行排序,而 MAX 不需要。应该会大大加快您的查询速度。
【讨论】:
不能通过正确的索引来解决 ROW_NUMBER() 的性能问题吗? (我觉得无论如何都应该这样做) 使用日期时间,您不能保证不会在同一日期和时间添加两个条目。精度不够高。 +1 为简单起见。 @TamusJRoyce 是对的。关于什么? 'select * from DocumentStatusLog D where ID = (select ID from DocumentsStatusLog where D.DocumentID = DocumentID order by DateCreated DESC limit 1);' SELECT * FROM EventScheduleTbl D WHERE DatesPicked = (SELECT top 1 min(DatesPicked) FROM EventScheduleTbl WHERE EventIDf = D.EventIDf and DatesPicked>= convert(date,getdate()) ) 在我的例子中,由于引入了子查询,这种方法比使用 ROW_NUMBER() 慢。您应该测试不同的方法,看看哪种方法最适合您的数据。【参考方案7】:在 SQLite 中检查,您可以将以下简单查询与 GROUP BY
一起使用SELECT MAX(DateCreated), *
FROM DocumentStatusLogs
GROUP BY DocumentID
这里 MAX 帮助从每个组中获取最大 DateCreated。
但似乎 mysql 没有将 *-columns 与 max DateCreated 的值相关联:(
【讨论】:
【参考方案8】:这是一个相当古老的线程,但我认为我会投入两分钱,因为接受的答案对我来说并不是特别好。我在一个大型数据集上尝试了 gbn 的解决方案,发现它非常慢(在 SQL Server 2012 中超过 500 万条记录超过 45 秒)。查看执行计划很明显,问题在于它需要一个 SORT 操作,这会大大减慢速度。
这是我从不需要 SORT 操作并执行非聚集索引搜索的实体框架中提取的替代方法。这将上述记录集的执行时间减少到
SELECT
[Limit1].[DocumentID] AS [DocumentID],
[Limit1].[Status] AS [Status],
[Limit1].[DateCreated] AS [DateCreated]
FROM (SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM [dbo].[DocumentStatusLogs] AS [Extent1]) AS [Distinct1]
OUTER APPLY (SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated]
FROM (SELECT
[Extent2].[ID] AS [ID],
[Extent2].[DocumentID] AS [DocumentID],
[Extent2].[Status] AS [Status],
[Extent2].[DateCreated] AS [DateCreated]
FROM [dbo].[DocumentStatusLogs] AS [Extent2]
WHERE ([Distinct1].[DocumentID] = [Extent2].[DocumentID])
) AS [Project2]
ORDER BY [Project2].[ID] DESC) AS [Limit1]
现在我假设原始问题中未完全指定的内容,但如果您的表格设计使得您的 ID 列是自动增量 ID,并且 DateCreated 设置为每次插入时的当前日期,那么即使没有运行我上面的查询,您实际上也可以通过 按 ID 排序而不是按 DateCreated 排序 对 gbn 的解决方案(大约一半的执行时间)获得相当大的性能提升,因为这将提供相同的排序顺序,这是一种更快的排序方式。
【讨论】:
【参考方案9】:我已经对这里的各种建议进行了一些计时,结果实际上取决于所涉及的表的大小,但最一致的解决方案是使用 CROSS APPLY 这些测试是针对 SQL Server 2008-R2 运行的,使用一个有 6,500 条记录的表,另一个(相同的模式)有 1.37 亿条记录。被查询的列是表上主键的一部分,表的宽度很小(大约30字节)。时间由 SQL Server 根据实际执行计划报告。
Query Time for 6500 (ms) Time for 137M(ms)
CROSS APPLY 17.9 17.9
SELECT WHERE col = (SELECT MAX(COL)…) 6.6 854.4
DENSE_RANK() OVER PARTITION 6.6 907.1
我认为真正令人惊奇的是,无论涉及多少行,CROSS APPLY 的时间都是如此一致。
【讨论】:
这完全取决于数据分布和可用索引。在dba.se 上进行了详细讨论。【参考方案10】:这是我能想到的最普通的 TSQL
SELECT * FROM DocumentStatusLogs D1 JOIN
(
SELECT
DocumentID,MAX(DateCreated) AS MaxDate
FROM
DocumentStatusLogs
GROUP BY
DocumentID
) D2
ON
D2.DocumentID=D1.DocumentID
AND
D2.MaxDate=D1.DateCreated
【讨论】:
不幸的是 MaxDate 不是唯一的。可以同时输入两个日期。因此,这可能会导致每组重复。但是,您可以使用标识列或 GUID。身份列将为您提供最新输入的内容(使用默认身份计算,1...x 步骤 1)。 嗯,我有点同意,但作者要求最新条目 - 除非您包含自动增量标识列,否则意味着同时添加的两个项目同样是“最新的”跨度> 最新记录为一条记录。所以是的。您需要考虑自增标识列。【参考方案11】:从上面验证克林特的真棒和正确的答案:
下面两个查询之间的性能很有趣。 52% 是第一名。 48% 是第二个。使用 DISTINCT 而不是 ORDER BY,性能提高了 4%。但 ORDER BY 的优势在于可以按多列排序。
IF (OBJECT_ID('tempdb..#DocumentStatusLogs') IS NOT NULL) BEGIN DROP TABLE #DocumentStatusLogs END
CREATE TABLE #DocumentStatusLogs (
[ID] int NOT NULL,
[DocumentID] int NOT NULL,
[Status] varchar(20),
[DateCreated] datetime
)
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (2, 1, 'S1', '7/29/2011 1:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (3, 1, 'S2', '7/30/2011 2:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 1, 'S1', '8/02/2011 3:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (1, 2, 'S1', '7/28/2011 4:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (4, 2, 'S2', '7/30/2011 5:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (5, 2, 'S3', '8/01/2011 6:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 3, 'S1', '8/02/2011 7:00:00')
选项 1:
SELECT
[Extent1].[ID],
[Extent1].[DocumentID],
[Extent1].[Status],
[Extent1].[DateCreated]
FROM #DocumentStatusLogs AS [Extent1]
OUTER APPLY (
SELECT TOP 1
[Extent2].[ID],
[Extent2].[DocumentID],
[Extent2].[Status],
[Extent2].[DateCreated]
FROM #DocumentStatusLogs AS [Extent2]
WHERE [Extent1].[DocumentID] = [Extent2].[DocumentID]
ORDER BY [Extent2].[DateCreated] DESC, [Extent2].[ID] DESC
) AS [Project2]
WHERE ([Project2].[ID] IS NULL OR [Project2].[ID] = [Extent1].[ID])
选项 2:
SELECT
[Limit1].[DocumentID] AS [ID],
[Limit1].[DocumentID] AS [DocumentID],
[Limit1].[Status] AS [Status],
[Limit1].[DateCreated] AS [DateCreated]
FROM (
SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM #DocumentStatusLogs AS [Extent1]
) AS [Distinct1]
OUTER APPLY (
SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated]
FROM (
SELECT
[Extent2].[ID] AS [ID],
[Extent2].[DocumentID] AS [DocumentID],
[Extent2].[Status] AS [Status],
[Extent2].[DateCreated] AS [DateCreated]
FROM #DocumentStatusLogs AS [Extent2]
WHERE [Distinct1].[DocumentID] = [Extent2].[DocumentID]
) AS [Project2]
ORDER BY [Project2].[ID] DESC
) AS [Limit1]
M$的Management Studio:高亮并运行第一个块后,同时高亮选项1和选项2,右键->【显示预估执行计划】。然后运行整个事情以查看结果。
选项 1 结果:
ID DocumentID Status DateCreated
6 1 S1 8/2/11 3:00
5 2 S3 8/1/11 6:00
6 3 S1 8/2/11 7:00
选项 2 结果:
ID DocumentID Status DateCreated
6 1 S1 8/2/11 3:00
5 2 S3 8/1/11 6:00
6 3 S1 8/2/11 7:00
注意:
当我希望联接是一对(许多中的一个)时,我倾向于使用 APPLY。
如果我希望联接是一对多或多对多,我使用联接。
我避免使用 ROW_NUMBER() 进行 CTE,除非我需要执行一些高级操作并且可以接受窗口性能损失。
我还避免在 WHERE 或 ON 子句中使用 EXISTS / IN 子查询,因为我经历过这会导致一些糟糕的执行计划。但里程不同。随时随地查看执行计划和性能分析!
【讨论】:
【参考方案12】:试试这个:
SELECT [DocumentID]
,[tmpRez].value('/x[2]', 'varchar(20)') AS [Status]
,[tmpRez].value('/x[3]', 'datetime') AS [DateCreated]
FROM (
SELECT [DocumentID]
,cast('<x>' + max(cast([ID] AS VARCHAR(10)) + '</x><x>' + [Status] + '</x><x>' + cast([DateCreated] AS VARCHAR(20))) + '</x>' AS XML) AS [tmpRez]
FROM DocumentStatusLogs
GROUP BY DocumentID
) AS [tmpQry]
【讨论】:
您应该始终描述您的 SQL 语句将如何工作并解决 OP 的查询。【参考方案13】:SELECT o.*
FROM `DocumentStatusLogs` o
LEFT JOIN `DocumentStatusLogs` b
ON o.DocumentID = b.DocumentID AND o.DateCreated < b.DateCreated
WHERE b.DocumentID is NULL ;
如果您只想按 DateCreated 返回最近的文档顺序,它将仅返回按 DocumentID 排列的前 1 个文档
【讨论】:
这只会返回表格中的所有内容。【参考方案14】:这是关于该主题的最容易找到的问题之一,因此我想给出一个现代的答案(供我参考和帮助其他人)。通过使用first_value
和over
,您可以简化上述查询:
Select distinct DocumentID
, first_value(status) over (partition by DocumentID order by DateCreated Desc) as Status
, first_value(DateCreated) over (partition by DocumentID order by DateCreated Desc) as DateCreated
From DocumentStatusLogs
这应该适用于 Sql Server 2008 及更高版本。当使用over
子句时,First_value
可以被认为是完成Select Top 1
的一种方式。 Over
允许在选择列表中进行分组,因此不是编写嵌套子查询(就像许多现有答案一样),而是以更易读的方式进行。希望这会有所帮助。
【讨论】:
这在 SQL Server 2008 R2 中不起作用。我认为 first_value 是在 2012 年推出的! 非常快!我使用的是 @dpp 提供的 Cross Apply 解决方案,但这个解决方案要快得多。 对于大量列(Status、DateCreated 等),这是对每一列进行单独的分区/排序,还是优化为一个?【参考方案15】:我知道这是一个旧线程,但 TOP 1 WITH TIES
解决方案非常好,可能有助于阅读解决方案。
select top 1 with ties
DocumentID
,Status
,DateCreated
from DocumentStatusLogs
order by row_number() over (partition by DocumentID order by DateCreated desc)
select top 1 with ties
子句告诉 SQL Server 您要返回每个组的第一行。但是 SQL Server 是如何知道如何对数据进行分组的呢?这就是order by row_number() over (partition by DocumentID order by DateCreated desc
的用武之地。partition by
之后的列/列定义了 SQL Server 如何对数据进行分组。在每个组中,行将根据order by
列进行排序。排序后,查询中将返回每组中的第一行。
有关 TOP 子句的更多信息,请参阅here。
【讨论】:
这是imo最优雅的解决方案 同意 - 这最好地复制了在其他版本的 SQL 和其他语言 imo 中非常容易做的事情 希望我能多次投票。我已经回到这个答案大约 7.000 次了。可能有一天,我会花时间了解这一点,这样我就不必回来了。但这不是这一天。 嗯,'With Ties' 可能会导致返回的行数超过表达式中指定的值 (TOP 1)。如果 OP 只想要 1,那么你需要删除这个短语,对吗? @TKBruin 这就是为什么需要按 row_number() 排序的原因。这允许检索每个分区的顶部记录。【参考方案16】:此方案可用于获取每个分区的 TOP N 最近行(在示例中,WHERE 语句中的 N 为 1,分区为 doc_id):
SELECT T.doc_id, T.status, T.date_created FROM
(
SELECT a.*, ROW_NUMBER() OVER (PARTITION BY doc_id ORDER BY date_created DESC) AS rnk FROM doc a
) T
WHERE T.rnk = 1;
【讨论】:
【参考方案17】:这里有 3 种不同的方法来解决手头的问题,以及为每个查询建立索引的最佳选择(请自己尝试索引并查看逻辑读取、经过的时间、执行计划。我提供了来自我对此类查询的经验,但未针对此特定问题执行)。
方法 1:使用 ROW_NUMBER()。如果行存储索引无法提高性能,您可以尝试非聚集/聚集列存储索引,对于具有聚合和分组的查询以及始终在不同列中排序的表,列存储索引通常是最佳选择。
;WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
FROM DocumentStatusLogs
)
SELECT ID
,DocumentID
,Status
,DateCreated
FROM CTE
WHERE RN = 1;
方法 2:使用 FIRST_VALUE。如果行存储索引无法提高性能,您可以尝试非聚集/聚集列存储索引,对于具有聚合和分组的查询以及始终在不同列中排序的表,列存储索引通常是最佳选择。
SELECT DISTINCT
ID = FIRST_VALUE(ID) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
,DocumentID
,Status = FIRST_VALUE(Status) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
,DateCreated = FIRST_VALUE(DateCreated) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
FROM DocumentStatusLogs;
方法 3:使用 CROSS APPLY。在 DocumentStatusLogs 表上创建覆盖查询中使用的列的行存储索引应该足以覆盖查询,而不需要列存储索引。
SELECT DISTINCT
ID = CA.ID
,DocumentID = D.DocumentID
,Status = CA.Status
,DateCreated = CA.DateCreated
FROM DocumentStatusLogs D
CROSS APPLY (
SELECT TOP 1 I.*
FROM DocumentStatusLogs I
WHERE I.DocumentID = D.DocumentID
ORDER BY I.DateCreated DESC
) CA;
【讨论】:
感谢您提出的不同解决方案。我经历了第二个,今天救了我! 我有一个 100M 行的表,我需要在其中获取每个组的第一条和最后一条记录。前两种方法需要几分钟才能执行。方法 3 用时不到一秒。【参考方案18】:SELECT documentid,
status,
datecreated
FROM documentstatuslogs dlogs
WHERE status = (SELECT status
FROM documentstatuslogs
WHERE documentid = dlogs.documentid
ORDER BY datecreated DESC
LIMIT 1)
【讨论】:
【参考方案19】:CROSS APPLY
是我用于解决方案的方法,因为它适用于我,也适用于我的客户需求。从我读到的内容来看,如果他们的数据库大幅增长,应该会提供最佳的整体性能。
【讨论】:
【参考方案20】:我相信这可以像这样完成。这可能需要一些调整,但您可以从组中选择最大值。
这些答案太过分了..
SELECT
d.DocumentID,
MAX(d.Status),
MAX(d1.DateCreated)
FROM DocumentStatusLogs d, DocumentStatusLogs d1
USING(DocumentID)
GROUP BY d.DocumentID
ORDER BY DateCreated DESC
【讨论】:
这是 t-sql 吗?Using
不支持那样...
mysql 8 应该支持@PedroC88
是的,我提到它是因为 OP 指定了 sql-server
@PedroC88 问题似乎已更改,因此不再引用 sql-server。所以这是一个好的答案。
标签上有以上是关于获取每组的前 1 行的主要内容,如果未能解决你的问题,请参考以下文章