Sql server 2008 如何在SQL中获取每个组的最后一条记录

Sql server 2008 如何在SQL中获取每个组的最后一条记录,sql-server-2008,tsql,sql-server-2005,greatest-n-per-group,Sql Server 2008,Tsql,Sql Server 2005,Greatest N Per Group,我面临一个相当有趣的问题。我有一个具有以下结构的表: CREATE TABLE [dbo].[Event] ( Id int IDENTITY(1,1) NOT NULL, ApplicationId nvarchar(32) NOT NULL, Name nvarchar(128) NOT NULL, Description nvarchar(256) NULL, Date nvarchar(16) NOT NULL, Time nvarchar

我面临一个相当有趣的问题。我有一个具有以下结构的表:

CREATE TABLE [dbo].[Event]
(
    Id int IDENTITY(1,1) NOT NULL,
    ApplicationId nvarchar(32) NOT NULL,
    Name nvarchar(128) NOT NULL,
    Description nvarchar(256) NULL,
    Date nvarchar(16) NOT NULL,
    Time nvarchar(16) NOT NULL,
    EventType nvarchar(16) NOT NULL,
    CONSTRAINT Event_PK PRIMARY KEY CLUSTERED ( Id ) WITH (
        PAD_INDEX = OFF, 
        STATISTICS_NORECOMPUTE = OFF, 
        IGNORE_DUP_KEY = OFF, 
        ALLOW_ROW_LOCKS = ON, 
        ALLOW_PAGE_LOCKS  = ON
    )
)
所以问题是我必须在网格中显示这些数据。有两个要求。第一个是显示所有事件,而不管是哪个应用程序抛出的事件。这很简单-select语句将很容易完成这项工作

第二个要求是能够按应用程序对事件进行分组。换句话说,显示所有事件的方式是,如果ApplicationId重复多次,则只获取每个应用程序的最后一个条目。此时事件Id的主键在此查询/视图中不再需要

您可能还注意到事件日期和时间是字符串格式的。这是可以的,因为它们遵循标准的日期时间格式:mm/dd/yyyy和hh:mm:ss。我可以按如下方式提取:

Convert( DateTime, (Date + ' ' +  Time)) AS 'TimeStamp'
我的问题是,如果我在其余列上使用聚合函数,我不知道它们的行为如何:

SELECT
    ApplicationId,
    MAX(Name),
    MAX(Description),
    MAX( CONVERT(DateTime, (Date + ' ' + Time))) AS 'TimeStamp',
    MAX( EventType )
FROM
    Event
GROUP BY
    ApplicationId
我之所以犹豫不决,是因为像MAX这样的函数将从记录子集返回给定列的最大值。没有必要拉最后一条记录

关于如何在每个应用程序的基础上只选择最后一条记录,您有什么想法吗?

您可以使用和


因为这里没有where子句,记录的子集就是所有记录。但我认为你把max放错了栏目。此查询将为您提供所需的内容

Select max(applicationid), name, description, CONVERT(DateTime, (Date + ' ' + Time)) 
from event
group by name, description, CONVERT(DateTime, (Date + ' ' + Time)) 

您可以使用subqery或CTE表来执行此操作:

;WITH CTE_LatestEvents as (
SELECT
    ApplicationId,    
    MAX( CONVERT(DateTime, (Date + ' ' + Time))) AS 'LatestTimeStamp',
FROM
    Event
GROUP BY
    ApplicationId
)
SELECT
    ApplicationId,
    Name,
    Description,
    CONVERT(DateTime, (Date + ' ' + Time))) AS 'TimeStamp',
    EventType
FROM
    Event e
    Join CTE_LatestEvents le 
        on e.applicationid = le.applicationid
        and CONVERT(DateTime, (e.Date + ' ' + e.Time))) = le.LatestTimeStamp

可以将子查询与group by一起使用-group by参数不需要位于select中。这假定Id是自动递增的,因此最大的Id是最近的Id

SELECT
    ApplicationId,
    Name,
    Description,
    CONVERT(DateTime, (Date + ' ' + Time)) AS 'TimeStamp',
    EventType
FROM
    Event e
WHERE
    Id in (select max(Id) from Event GROUP BY ApplicationId)

我认为这对许多愿意获取最后插入的记录的人来说是可行的,它应该是分组的:

选择*从选择*从表名按id排序描述为x组按字段名排序

它将在以下方面发挥作用:

表结构 ID名称状态 1朱奈德:是的 2号加瓦德 3法哈德:是的 朱奈德4号 5 Kashif是的

以上查询结果 ID名称状态 朱奈德4号 2号加瓦德 3法哈德:是的 4卡西夫是的


它只会生成按名称分组的最后一条记录。

自SQL Server 2012以来,您只需

SELECT 
    [Month]
    , [First] = FIRST_VALUE(SUM([Clicks])) OVER (ORDER BY [Month])
    , [Last]  = FIRST_VALUE(SUM([Clicks])) OVER (ORDER BY [Month] DESC)
FROM 
    [dbo].[Table]
GROUP BY [Month]
ORDER BY [Month]

6年后,SQL Server的另一个答案是:

select t1.[Id], t2.[Value]  
from [dbo].[Table] t1  
  outer apply (  
    select top 1 [Value]  
      from [dbo].[Table] t2  
        where t2.[Month]=t1.[Month]  
      order by [dbo].[Date] desc  
  )  
虽然我更喜欢Postgresql解决方案,因为它具有独特的on功能,打字更方便,效率更高:

select distinct on (id),val  
from tbl  
order by id,val  

起初,我习惯使用带有行号的CTE,但SQL server认证课程中的一个示例向我展示了一个更好的示例,通过不断获得更好的执行计划来判断:

SELECT
  ApplicationId,
  Name,
  Description,
  CONVERT(DateTime, (Date + ' ' + Time)) AS 'TimeStamp',
  EventType
FROM
  Event AS E
WHERE
  NOT EXISTS(SELECT * FROM Event AS Newer WHERE Newer.ApplicationId = E.ApplicationId AND Newer.Id > E.Id)
GROUP BY
  ApplicationId

我假设更大的Id意味着更大的日期+时间,否则我将使用convert to datetime,这是不可取的。此查询将查找最年轻的记录-较年轻的记录不存在。如果索引设置正确,则将使用索引查找。带有排名功能的替代方案通常使用表扫描,因为它对所有记录进行排名。

我也有同样的问题。现在,我不想让CTE的事情变得过于复杂。这里有一个简单的例子。我编写了一个子查询,其中包含一个由maxDateEnter创建的组。例如,如果它是int,您可能希望按ID执行,这比日期/时间更准确。在任何情况下,一旦您有了这个子查询,您只需将它内部连接到主查询,作为记录的过滤器。就这么简单

表a是我的用户表。表b是子查询,表c是我要筛选的表

SELECT DISTINCT a.FirstName,a.LastName,a.ImagePath, c.MessageText
        FROM [AuthUsers] a 
            INNER JOIN (SELECT MessageFromId,MAX(DateEntered) AS LastEntered FROM ChatRoomConversation GROUP BY MessageFrom) AS b
                ON a.Id=b.MessageFromId
            INNER JOIN ChatRoomConversation c
                ON b.LastEntered=c.DateEntered

在Oracle中使用窗口功能,例如按…分区的行数,AFAIK SQL server具有类似的功能。您不能只按日期和时间排序而不转换为日期时间值,因为mm/dd/yyyy格式不能作为字符串正确排序。谢谢@Anthony Faull。这是有效的,但我不明白怎么做。@damien好球。我已经更新了ORDER BY子句,将美国日期月日年转换为可排序的日期。尽管这是一个非常晚的注释,并且不再有助于@bleepzter,但它可能有助于其他人理解它的工作原理:通过设备将数据和事件划分为子集,其中每个子集具有相同的appliationid。每个子集按日期和时间排序。然后每一行得到一个行号。这就用部分来描述了。下面的select获取with语句的结果,并输出行号为1的所有条目。FIRST_值超过-非常令人印象深刻!今天学到了新东西!!非常感谢。SQL版本ref也是一个加号!
SELECT
  ApplicationId,
  Name,
  Description,
  CONVERT(DateTime, (Date + ' ' + Time)) AS 'TimeStamp',
  EventType
FROM
  Event AS E
WHERE
  NOT EXISTS(SELECT * FROM Event AS Newer WHERE Newer.ApplicationId = E.ApplicationId AND Newer.Id > E.Id)
GROUP BY
  ApplicationId
SELECT DISTINCT a.FirstName,a.LastName,a.ImagePath, c.MessageText
        FROM [AuthUsers] a 
            INNER JOIN (SELECT MessageFromId,MAX(DateEntered) AS LastEntered FROM ChatRoomConversation GROUP BY MessageFrom) AS b
                ON a.Id=b.MessageFromId
            INNER JOIN ChatRoomConversation c
                ON b.LastEntered=c.DateEntered