MSSQL 2008:按特定字段获取上次更新记录(第2部分)

时间:2014-10-16 12:22:39

标签: sql sql-server sql-server-2008

昨天,我在这里发布了这个问题:MSSQL 2008: Get last updated record by specific field

戈登·林诺夫提出了一个很好的解决方案,直到今天我才开心,当时我意识到我只发布了一半的情景。这是我的新问题:

鉴于此表Content

ContentId     lastUpdate              FileId    IrrelevantField
1             2014-01-01 00:00:00     File-A    Dr. Hoo     /* user uploads file*/
1             2014-01-02 00:00:00     File-B    Dr. Hoo     /* (!) user uploads new file */
1             2014-01-03 00:00:00     File-B    Dr. Who     /* user updates info */

2             2014-02-01 00:00:00     File-M    41          /* (!) user uploads file */
2             2014-02-02 00:00:00     File-M    42          /* user updates info */

3             2014-03-01 00:00:00     File-S    Donald Duck /* user uploads file*/

基本上我想要的是获得满足这些条件的所有行:

  • 他们的上一行FileIdContentId相同。
  • 如果FileId从未更改,请获取第一个提交的行(这在示例中适用于ContentId = 2& 3.)

IrrelevantField触发行更新。我的目标是,在FileId发生变化时获取行。


输出结果如下:

ContentId     lastUpdate              FileId    IrrelevantField
1             2014-01-02 00:00:00     File-B    Dr. Hoo
2             2014-02-01 00:00:00     File-M    41
3             2014-03-01 00:00:00     File-S    Donald Duck

FileId永远不会NULL


我尝试向Gordon Linoff的解决方案添加OUTER APPLY,因此我可以检查FileId是否仍与初始上传时相同。但这也让我无关紧要的更新。

4 个答案:

答案 0 :(得分:2)

尝试这应该适用于所有场景..

;with cte
as
(
select rank() over(partition by fileid,contentid order by lastupdate ) id, ContentId,lastUpdate,FileId,IrrelevantField from tablename
)
select ContentId,lastUpdate,FileId,IrrelevantField from(
select row_number() over(partition by contentid order by lastupdate desc) fstid, * from cte where id=1) a where fstid=1

答案 1 :(得分:2)

您沿着正确的路线将其更改为外部申请,然后您可以稍微更改WHERE子句以允许记录没有先前记录的记录。

SELECT  c.ContentID,
        c.LastUpdate,
        c.FileID,
        c.IrrelevantField,
        FileID2 = c2.FileID
FROM    Content AS c 
        OUTER APPLY
        (   SELECT  TOP 1 c2.FileID
            FROM    Content AS c2
            WHERE   c2.ContentID = c.ContentID
            AND     c2.LastUpdate < c.LastUpdate
            ORDER BY c2.LastUpdate DESC
        ) AS c2
WHERE   c.FileId != c2.FileId
OR      c2.FileID IS NULL;

这意味着虽然为ContentID = 1(以及任何其他包含更改的内容)返回了两条记录:

ContentID   LastUpdate  FileID  IrrelevantField FileID2
1           2014-01-01  File-A  Dr. Hoo         NULL
1           2014-01-02  File-B  Dr. Hoo         File-A
2           2014-02-01  File-M  41              NULL
3           2014-03-01  File-S  Donald Duck     NULL

因此,您需要使用进一步的排名功能将此限制为仅限最新记录:

WITH CTE AS
(   SELECT  c.ContentID,
            c.LastUpdate,
            c.FileID,
            c.IrrelevantField,
            RowNumber = ROW_NUMBER() OVER(PARTITION BY c.ContentID ORDER BY c.LastUpdate DESC)
    FROM    Content AS c 
            OUTER APPLY
            (   SELECT  TOP 1 c2.FileID
                FROM    Content AS c2
                WHERE   c2.ContentID = c.ContentID
                AND     c2.LastUpdate < c.LastUpdate
                ORDER BY c2.LastUpdate DESC
            ) AS c2
    WHERE   c.FileId != c2.FileId
    OR      c2.FileID IS NULL
)
SELECT  c.ContentID,
        c.LastUpdate,
        c.FileID,
        c.IrrelevantField
FROM    CTE AS c
WHERE   RowNumber = 1;

答案 2 :(得分:1)

下面的查询使用lead创建一个派生表,其中包含FileId已更改的所有行以及第1行。然后,如果没有针对该ContentId(row_number())更改的FileId或FileId已更改的所有其他行(rn_asc = 1 and rn_desc = 1),则使用rn_asc > 1显示第1行。如果您只想要更改FileId的第一行,请使用rn_asc = 2代替rn_asc > 1

select * from (
    select * ,
    row_number() over (partition by ContentId order by lastUpdate asc) rn_asc,
    row_number() over (partition by ContentId order by lastUpdate desc) rn_desc
    from (
        select * 
        from (
            select * , 
            lead(FileId) over (partition by ContentId order by lastUpdate) previousFileId
            from content c
        ) t1 where previousFileId <> FileId or previousFileId IS NULL
    ) t2 
) t3 where (rn_asc = 1 and rn_desc = 1) or rn_asc > 1

答案 3 :(得分:0)

select ContentId, lastUpdate, FileId, IrrelevantField
from (  
    select  row_number() over(partition by contentid order by lastupdate desc) LastChgsl, *
    from   
    (  
        select  
        row_number() over(partition by fileid order by lastupdate) FileChgSl, *   
        from tablename  
    )V where FileChgSl = 1
)V1 where LastChgsl = 1