Sql 获取每组的前1行

Sql 获取每组的前1行,sql,tsql,sql-server-2005,group-by,greatest-n-per-group,Sql,Tsql,Sql Server 2005,Group By,Greatest N Per Group,我有一个表,我想获得每个组的最新条目。这是桌子: DocumentStatusLogs表 该表将按DocumentID分组,并按日期降序排列。对于每个DocumentID,我希望获得最新状态 我的首选输出: | DocumentID | Status | DateCreated | | 1 | S1 | 8/02/2011 | | 2 | S3 | 8/01/2011 | | 3 | S1 | 8/02/201

我有一个表,我想获得每个组的最新条目。这是桌子:

DocumentStatusLogs表

该表将按DocumentID分组,并按日期降序排列。对于每个DocumentID,我希望获得最新状态

我的首选输出:

| DocumentID | Status | DateCreated |
| 1          | S1     | 8/02/2011   |
| 2          | S3     | 8/01/2011   |
| 3          | S1     | 8/02/2011   |
是否存在只从每个组中获取顶部的聚合函数?请参见下面的伪代码GetOnlyTheTop:

如果这样的功能不存在,有什么方法可以实现我想要的输出

或者首先,这可能是由未规范化的数据库引起的吗?我在想,既然我要查找的只是一行,那么该状态是否也应该位于父表中? 有关详细信息,请参见父表:

当前文档表

父表应该是这样的,以便我可以轻松访问其状态吗

| DocumentID | Title  | Content  | DateCreated | CurrentStatus |
| 1          | TitleA | ...      | ...         | s1            |
| 2          | TitleB | ...      | ...         | s3            |
| 3          | TitleC | ...      | ...         | s1            |
更新 我刚刚学会了如何使用apply,这使得解决这些问题变得更容易

;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个条目,那么这将任意选择一个条目。若要获取一天的两个条目,请改用密集排名

至于是否正常化,这取决于您是否希望:

在2个位置保持状态 保存状态历史记录 ... 目前,您保留了状态历史记录。如果您希望父表中的最新状态也是非规范化的,那么您需要一个触发器来维护父表中的状态。或者删除此状态历史记录表

SELECT * FROM
DocumentStatusLogs JOIN (
  SELECT DocumentID, MAX(DateCreated) DateCreated
  FROM DocumentStatusLogs
  GROUP BY DocumentID
  ) max_date USING (DocumentID, DateCreated)
什么数据库服务器?这段代码并不是对所有的人都有效

关于你问题的后半部分,在我看来,将状态列为一个专栏是合理的。您可以将DocumentStatusLogs保留为日志,但仍将最新信息存储在主表中

顺便说一句,如果Documents表中已经有DateCreated列,只要DateCreated在DocumentStatusLogs中是唯一的,就可以使用该列连接DocumentStatusLogs

编辑:MsSQL不支持使用,因此将其更改为:

ON DocumentStatusLogs.DocumentID = max_date.DocumentID AND DocumentStatusLogs.DateCreated = max_date.DateCreated
我刚刚学会了如何使用交叉应用。下面介绍如何在此场景中使用它:

 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

在希望避免使用行计数的情况下,还可以使用左联接:

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 
对于示例架构,还可以使用not in子查询,该子查询通常编译为与左联接相同的输出:

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),子查询模式将不起作用


这两种查询都比查询分析器测量的行计数查询更昂贵。但是,您可能会遇到这样的情况:它们返回结果更快,或者启用其他优化。

My code从每个组中选择前1名

select a.* from #DocumentStatusLogs a where datecreated in( select top 1 datecreated from #DocumentStatusLogs b where a.documentid = b.documentid order by datecreated desc )
如果您担心性能,也可以使用MAX:

SELECT *
FROM DocumentStatusLogs D
WHERE DateCreated = (SELECT MAX(DateCreated) FROM DocumentStatusLogs WHERE ID = D.ID)

ROW_NUMBER需要对SELECT语句中的所有行进行排序,而MAX则不需要。这将大大加快您的查询速度。

在SQLite中检查,您可以将以下简单查询与GROUP BY一起使用

这里的MAX帮助可以获取每个组创建的最大日期


但MYSQL似乎没有将*列与max DateCreated的值联系起来:

这是一个相当古老的线程,但我认为我应该投入我的两分钱,因为公认的答案对我来说并不特别有效。我在一个大数据集上尝试了gbn的解决方案,发现它在SQL Server 2012中的500多万条记录上的速度非常慢,超过45秒。从执行计划来看,很明显,问题在于它需要一个排序操作,这会大大降低速度

这里有一个我从实体框架中提取的替代方案,它不需要排序操作,并且执行非聚集索引搜索。这将上述记录集的执行时间减少到<2秒

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排序而不是按日期排序,实际获得gbn解决方案相当大的性能提升,大约一半的执行时间,因为这将提供相同的排序顺序,而且排序速度更快。

我在这里对各种建议进行了一些计时,结果实际上取决于所涉及的表的大小,但最一致的解决方案是使用交叉应用。这些测试是针对SQL Server 2008-R2运行的,使用一个包含6500条记录的表和另一个包含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

我认为真正令人惊奇的是,无论涉及多少行,交叉应用的时间都是如此一致。

这是我能做到的最普通的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

从上面验证Clint令人敬畏的正确答案:

下面两个查询之间的性能很有趣。52%是排名第一的。48%是第二位。使用DISTINCT而不是ORDER BY将性能提高4%。但是ORDERBY具有按多列排序的优势

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$的管理工作室:高亮显示并运行第一个块后,高亮显示选项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
注:

当我希望一个联接是多个联接的1对1时,我倾向于使用APPLY

如果希望联接为1对多或多对多,则使用联接

我避免使用行号的CTE,除非我需要做一些高级的事情,并且对窗口性能惩罚没有意见

我还避免WHERE或ON子句中的EXISTS/IN子查询,因为我经历过这会导致一些糟糕的执行计划。但里程数各不相同。在需要时随时审查执行计划和绩效档案

试试这个:

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]

如果您只想按日期返回最近创建的文档顺序,它将仅按文档ID返回排名前1的文档这是该主题中最容易找到的问题之一,因此我想给出一个现代的答案,以供参考和帮助他人。通过使用first_值和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

这在SQLServer2008及更高版本中应该可以使用。当使用over子句时,可以将第一个_值视为完成选择Top 1的一种方法。Over允许在选择列表中进行分组,因此它不像许多现有答案那样编写嵌套子查询,而是以更可读的方式进行。希望这能有所帮助。

我知道这是一条老线索,但“带领带的前1个解决方案”非常不错,可能有助于阅读解决方案

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如何知道如何对数据进行分组呢?这就是按文档ID分区按行编号排序按日期排序创建的描述的位置。分区后的列定义了SQL Server如何对数据进行分组。在每个组中,将根据列的顺序对行进行排序。排序后,将在查询中返回每个组中的第一行


可以找到关于TOP子句的更多信息。

此解决方案可用于获取示例中每个分区的前N个最新行,在WHERE语句中N为1,分区为doc\u 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;

这里有3种解决问题的方法,以及针对每个查询的最佳索引选择。请自己尝试索引,并查看逻辑读取、运行时间和执行计划。我根据自己在此类查询中的经验提供了建议,但没有针对此特定问题执行

方法1:使用行数。如果行存储索引无法提高性能,则可以尝试使用非聚集/聚集的列存储索引。对于具有聚合和分组的查询以及始终按不同列排序的表,列存储索引通常是最佳选择

;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;
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;
方法2:使用第一个_值。如果行存储索引无法提高性能,则可以尝试使用非聚集/聚集的列存储索引。对于具有聚合和分组的查询以及始终按不同列排序的表,列存储索引通常是最佳选择

;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;
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:使用交叉应用。在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;

交叉应用是我用于解决方案的方法,因为它适用于我和我的客户需求。据我所知,如果他们的数据库大幅增长,他们应该提供最佳的总体性能。

我相信这可以像这样做到。这可能需要一些调整,但您可以从组中选择最大值

这些答案太过分了

SELECT
  d.DocumentID,
  MAX(d.Status),
  MAX(d1.DateCreated)
FROM DocumentStatusLogs d, DocumentStatusLogs d1
USING(DocumentID)
GROUP BY d.DocumentID
ORDER BY DateCreated DESC

和什么是分区?With对我来说也是新的:反正我正在使用mssql 2005。@domanokz:Partition By重置计数。所以在这种情况下,它说要计算每个文档IDHM,我担心性能,我将查询数百万行。是选择*从选择。。。影响性能?另外,行号是每行的子查询吗?@domanokz:不,它不是子查询。如果你有正确的索引,那么数百万应该不是问题。无论如何,只有两种基于集合的方法:这和聚合Ariel的解决方案。所以试一下这两个…@domanokz:只要把ORDER BY DateCreated DESC改为ORDER BY ID DESC就行了。线索在标题中:MSSQL。SQL Server没有“使用”功能,但这个想法还可以。@gbn愚蠢的版主通常会从标题中删除重要的关键字,例如


他们在这里做了什么。这使得在搜索结果或Google中很难找到正确的答案。Jus要指出的是,如果您在maxDateCreatedRemove中使用maxDateCreatedRemove并完成连接代码,此解决方案仍然可以为您提供多条记录,那就行了。事实上,这没什么区别,因为问题仍然在解决。我刚刚发布了针对所有建议解决方案的计时测试结果,而你的结果排在了前面。给你一张赞成票:-+1表示速度大幅提升。这比诸如ROW_NUMBER之类的窗口功能快得多。如果SQL能够识别类似于行数=1的查询并将其优化为应用程序,那就太好了。注意:我在需要结果时使用了外部应用,即使它们在应用中不存在。@TamusJRoyce您不能仅仅因为它更快就推断出来,因为它总是这样。视情况而定。如本文所述,如果您已经有一个单独的Documents表,根据输出中的需要,该表为每个组提供一行,那么这种方法非常有效。但在这种情况下,如果您只使用一个表DocumentStatusLogs,则首先必须对DocumentID或ROW_NUMBER、MAXID等执行某种不同的操作,从而失去所有获得的性能。ROW_NUMBER的性能问题不能通过适当的索引来解决吗?我觉得无论如何都应该用datetime完成,你不能保证两个条目不会在同一个日期和时间添加。精度不够高。+1表示简单@塔穆斯杰罗伊斯是对的。那怎么办?”从DocumentStatusLog D中选择*,其中ID=从DocumentStatusLog中选择ID,其中D.DocumentID=DocumentID按日期排序已创建描述限制1;'SELECT*FROM EventScheduleTbl D WHERE DatesPicked=SELECT top 1 minDatesPicked FROM EventScheduleTbl WHERE EventIDf=D.EventIDf和DatesPicked>=convertdate,getdate在我的例子中,由于引入了子查询,这种方法比使用行号慢。您应该测试不同的方法,看看哪些方法最适合您的数据。不幸的是,MaxDate不是唯一的。可以同时输入两个日期。因此,这可能会导致每组重复。但是,您可以使用标识列或GUID。“身份”列将为您提供输入的最新信息使用默认身份计算,1…x步骤1。我有点同意,但作者要求提供最新信息-除非您包含自动递增身份列,否则意味着在同一时间添加的两个项目是相等的“最新”最新记录将是一条记录。所以是的。您需要考虑自动增量标识列,这取决于数据分布和可用索引。关于这个问题,我们在上进行了详细的讨论。为了更详细地讨论和比较可能的解决方案,我建议阅读dba.se.上的类似问题:。我看了这篇文章并尝试了一下。使用group by StoreID生成错误。相关:这是否回答了您的问题?这在SQL Server 2008 R2中不起作用。我认为第一个价值观是在2012年引入的!非常快!我使用的是@dpp提供的交叉应用解决方案,但这一个更快。对于大量列Status、DateCreated等,这会对每个列进行单独的分区/排序吗,这是我所同意的最优雅的解决方案——它最好地复制了在SQL和其他语言的其他版本中很容易做到的事情,我希望我能不止一次地向上投票。我已经回答了大约7000次了。也许有一天,我会花时间去理解这一点,这样我就不必回来了。但现在不是。嗯,‘With Ties’可能会导致返回的行数超过表达式TOP 1中指定的值。如果OP只需要1,那么您需要删除这个短语,对吗?@tkbruite这就是为什么需要按行编号排序的原因。这允许检索每个分区的顶部记录。您应该始终描述SQL语句的工作方式,并解决OP的查询。这将返回表中的所有内容。感谢提出的不同解决方案。我今天通过了第二个,救了我,伙计!我有一张100米行的表格,在那里我需要获得每组的第一个和最后一个记录。前两种方法需要几分钟才能执行。方法3用了不到一秒钟。这是t-sql吗?使用是不受支持的…mysql 8应该支持@PedroC88Yeah我提到它是因为OP指定了sql-server@PedroC88该问题似乎已更改,因此不再引用sql server。这是一个确定的答案,在标签上
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;
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;
SELECT documentid, 
       status, 
       datecreated 
FROM   documentstatuslogs dlogs 
WHERE  status = (SELECT status 
                 FROM   documentstatuslogs 
                 WHERE  documentid = dlogs.documentid 
                 ORDER  BY datecreated DESC 
                 LIMIT  1) 
SELECT
  d.DocumentID,
  MAX(d.Status),
  MAX(d1.DateCreated)
FROM DocumentStatusLogs d, DocumentStatusLogs d1
USING(DocumentID)
GROUP BY d.DocumentID
ORDER BY DateCreated DESC