如何查询SQL Server 2012中的最新记录?

时间:2016-09-19 18:59:00

标签: sql sql-server sql-server-2012

我正在尝试查询文档的最新状态,但没有取得任何成功。我用于查询的表格为docsdocStatsstatusList

docs表:

| docId |  docTitle  | venIdFk |
+-------+------------+---------+
|     1 | Contract 1 |      81 |
|     2 | Contract 2 |      37 |
+-------+------------+---------+

docStats表:

| docStatIdPk | docStat |        docStatDt        | docIdFk |
+-------------+---------+-------------------------+---------+
|           7 |       1 | 2016-09-16 09:00:00.000 |       1 |
|          10 |       2 | 2016-09-16 09:30:00.000 |       1 |
|          11 |       4 | 2016-09-17 08:30:00.000 |       1 |
|          12 |       1 | 2016-09-17 10:00:00.000 |       2 |
+-------------+---------+-------------------------+---------+

statusList表:

| statId |     stat     |
+--------+--------------+
|      1 | Needs Review |
|      2 | In Review    |
|      3 | Denied       |
|      4 | Accepted     |
+--------+--------------+

使用以下内容,我可以在docs表中获取文档的最新状态:

SELECT 
    d2.docId, MAX(ds2.docStatDt) AS maxStatDt
FROM
    docs d2
INNER JOIN 
    docStats ds2 ON d2.docId = ds2.docIdFk
GROUP BY 
    d2.docId

结果是正确的:

| docId |        maxStatDt        |
+-------+-------------------------+
|     1 | 2016-09-17 08:30:00.000 |
|     2 | 2016-09-17 10:00:00.000 |
+-------+-------------------------+

但是我需要在查询中添加其他列,这就是它中断的地方。它添加了docId 1的所有记录,因为' stat'包含在SELECT中,docId 1有多种状态。如何编写此查询以便它只返回docId 1的最新状态?

以下是我现在拥有的查询:

SELECT 
    d.docid, d.doctitle, d.doctype, d.docorg, d.docdept, 
    s.stat, 
    mdt.maxdate AS crntDocStatDtSet 
FROM
    docs d 
INNER JOIN 
    docstats ds ON d.docid = ds.docidfk 
INNER JOIN 
    statuslist s ON ds.docstat = s.statid 
INNER JOIN 
    (SELECT 
         d2.docid, Max(ds2.docstatdt) AS MaxDate 
     FROM   
         docs d2 
     INNER JOIN 
         docstats ds2 ON d2.docid = ds2.docidfk 
     GROUP BY 
         d2.docid) mdt ON d.docid = mdt.docid 
WHERE  
    d.docid = '1' 
GROUP BY 
    d.docid, d.doctitle, d.doctype, d.docorg, d.docdept, 
    s.stat, mdt.maxdate 

结果如下:

| docId |  docTitle  | docType | docOrg | docDept |     stat     |    crntDocStatDtSet     |
+-------+------------+---------+--------+---------+--------------+-------------------------+
|     1 | Contract 1 |       3 |      3 |       2 | Accepted     | 2016-09-17 08:30:00.000 |
|     1 | Contract 1 |       3 |      3 |       2 | In Review    | 2016-09-17 08:30:00.000 |
|     1 | Contract 1 |       3 |      3 |       2 | Needs Review | 2016-09-17 08:30:00.000 |
+-------+------------+---------+--------+---------+--------------+-------------------------+

2 个答案:

答案 0 :(得分:3)

;WITH cte AS (
    SELECT
       d.docId, 
       d.docTitle, 
       d.doctype, 
       d.docOrg, 
       d.docDept, 
       s.stat,
       ROW_NUMBER() OVER (PARTITION BY d.docId ORDER BY ds.docStatDt DESC) as RowNumber
    FROM
       docs d
       INNER JOIN docStats ds
       ON d.docId = ds.docIdFk
       INNER JOIN statusList s
       ON ds.docStat = s.statId
)

SELECT *
FROM
    cte
WHERE
    RowNumber = 1

最好使用分区的ROW_NUMBER()。 MAX()你使用它的方式最终会得到1个以上的结果。如果它们相等则需要多于1个,那么只需使用DENSE_RANK()代替ROW_NUMBER()所在的位置。

您要前往的路线也是一种有效的方法,但您的加入的ON条件没有ON ds.docStatDt = mdt.MaxDate,这意味着您实际上并未将结果限制为最后一条记录。添加该条件可能会给你你想要的东西。

 SELECT
    d.docId, 
    d.docTitle, 
    d.doctype, 
    d.docOrg, 
    d.docDept, 
    s.stat,
    mdt.MaxDate AS crntDocStatDtSet
FROM
    docs d
    INNER JOIN docStats ds
    on d.docId = ds.docIdFk
    INNER JOIN statusList s
    ON ds.docStat = s.statId
    INNER JOIN (
       SELECT d2.docId, MAX(ds2.docStatDt) as MaxDate
       FROM docs d2
       INNER JOIN docStats ds2
       ON d2.docId = ds2.docIdFk
       GROUP BY d2.docId
    ) mdt
    ON d.docId = mdt.docId

    AND ds.docStatDt = mdt.MaxDate

WHERE d.docId = '1'
GROUP BY
    d.docId, 
    d.docTitle, 
    d.doctype, 
    d.docOrg, 
    d.docDept, 
    s.stat, 
    mdt.MaxDate

答案 1 :(得分:1)

一种非常有效的方法是使用outer apply

select d.*, ds.*
from docs d outer apply
     (select top 1 ds.*
      from docstats ds
      where ds.docIdFk = d.docId
      order by docStatDt desc
     ) ds;

当然,您也可以加入状态字符串,但这似乎不是您问题的要点。