如何使用Join在SQL Server中基于Timestamp获取最新记录

时间:2016-04-27 00:29:34

标签: sql-server

我正在使用SQL Server 2012.我有一个包含多列的表(LocationResults)。我想获得另一个表中存在的所有列(TargetAssociationDetails) - 但只有那些插入最新的行并且也出现在第二个表中。

我的TargetAssociationDetails表:

MSISDN        IMEI    IMSI
--------------------------
14085551107   NULL    NULL
14085551108   NULL    NULL
14085551113   NULL    NULL

我的LocationResults表:

Latitude    Longitude       MSISDN        IMSI  IMEI    RecordedTimeStamp
---------------------------------------------------------------------------
57.785125   -123.40476      14085551107   NULL  NULL    05:54.8
57.78374111 -123.4027269    14085551107   NULL  NULL    19:12.6
57.78476194 -123.4045131    14085551107   NULL  NULL    09:08.3
57.61768861 -123.4081439    14085551108   NULL  NULL    19:08.4
57.801585   -123.45619      14085551114   NULL  NULL    19:08.5
57.55303194 -123.3019161    14085551113   NULL  NULL    19:08.5

这是我的查询 -

SELECT MAX(lr.RecordTimestamp) AS RecordedTimeStamp, lr.IMEI, lr.IMSI, lr.MSISDN 
FROM LocationResults lr LEFT OUTER JOIN TargetAssosiationDetails tad ON 
tad.IMEI = lr.IMEI AND tad.IMSI = lr.IMSI AND tad.MSISDN = lr.MSISDN
GROUP BY lr.IMEI, lr.IMSI,lr.MSISDN

但在这种情况下,我无法获得所有列。我只能得到那些我正在分组的专栏。

我想要的结果是 -

Latitude    Longitude       MSISDN        IMSI  IMEI    RecordedTimeStamp
---------------------------------------------------------------------------
57.78374111 -123.4027269    14085551107   NULL  NULL    19:12.6
57.61768861 -123.4081439    14085551108   NULL  NULL    19:08.4
57.55303194 -123.3019161    14085551113   NULL  NULL    19:08.5

我对数据库查询非常陌生,所以如果您可以发布一个脚本并解释它的作用,那将会很棒。

提前致谢。

5 个答案:

答案 0 :(得分:2)

只要您使用的是MS SQL Server 2008或更高版本,就可以使用窗口函数ROW_NUMBER()来实现这些结果。 ROW_NUMBER()函数允许您根据特定顺序(在这种情况下,时间戳降序)对每一行进行编号,并根据值(在本例中为MSISDN)重新启动该排序:因此,对于MSISDN编号的每个不同值,行从最高时间戳到最低时间顺序。然后从LocationResults表 ONLY 对该查询进行内部联接,其中行号为1,因为这意味着它是最新的行。

由于似乎每一行都由三个字段而不是一个字段标识,因此您需要将这三个字段划分为行,并在连接中使用它们。这只需通过在PARTITION BY子句中用逗号分隔它们来完成。

SELECT *
FROM LocationResults AS lr
INNER JOIN (
    SELECT *,
        -- number the rows based on the time stamp descending and restart the ordering for each MSISDN value
        ROW_NUMBER() OVER (PARTITION BY MSISDN, IMEI, IMSI ORDER BY RecordedTimeStamp DESC) AS rnum
    FROM TargetAssociationDetails
) AS d ON d.MSISDN=lr.MSISDN
    AND d.IMEI=lr.IMEI
    AND d.IMSI=lr.IMSI
    -- join on row number 1 because that will be the newest row
    AND d.rnum=1

答案 1 :(得分:1)

试试这个

;with cte as (
select Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp
  , row_number() over (partition by MSISDN, order by RecordedTimeStamp desc) as RN
from LocationResults
)
select *
from cte 
where RN = 1

答案 2 :(得分:0)

有很多方法可以做到.Below是一种不需要CTE或派生表的方法..

select top 1 with ties Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp
from 
LocationResults
order by  row_number() over (partition by MSISDN, order by RecordedTimeStamp desc) 

答案 3 :(得分:0)

正如 Mike D。 所提及的那样,还有很多方法可以做到。这是我的方法:

DECLARE
    @TargetAssociationDetails TABLE (
                                        MSISDN  BIGINT,
                                        IMEI    VARCHAR(100),
                                        IMSI    VARCHAR(100)
                                    )

DECLARE
    @LocationResults    TABLE   (
                                    Latitude            FLOAT,
                                    Longitude           FLOAT,
                                    MSISDN              BIGINT,
                                    IMSI                VARCHAR(100),
                                    IMEI                VARCHAR(100),
                                    RecordedTimeStamp   DATETIME
                                )

INSERT INTO @LocationResults (Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp)
VALUES (57.785125, -123.40476, 14085551107, NULL, NULL, '05:54.8')

INSERT INTO @LocationResults (Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp)
VALUES (57.78374111, -123.4027269, 14085551107, NULL, NULL, '19:12.6')

INSERT INTO @LocationResults (Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp)
VALUES (57.78476194, -123.4045131, 14085551107, NULL, NULL, '09:08.3')

INSERT INTO @LocationResults (Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp)
VALUES (57.61768861, -123.4081439, 14085551108, NULL, NULL, '19:08.4')

INSERT INTO @LocationResults (Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp)
VALUES (57.801585, -123.45619, 14085551114, NULL, NULL, '19:08.5')

INSERT INTO @LocationResults (Latitude, Longitude, MSISDN, IMSI, IMEI, RecordedTimeStamp)
VALUES (57.55303194, -123.3019161, 14085551113, NULL, NULL, '19:08.5')

INSERT INTO @TargetAssociationDetails (MSISDN, IMEI, IMSI)
VALUES (14085551107, NULL, NULL)

INSERT INTO @TargetAssociationDetails (MSISDN, IMEI, IMSI)
VALUES (14085551108, NULL, NULL)

INSERT INTO @TargetAssociationDetails (MSISDN, IMEI, IMSI)
VALUES (14085551113, NULL, NULL)

SELECT
    LR.Latitude,
    LR.Longitude,
    TAD.MSISDN,
    TAD.IMSI,
    TAD.IMEI,
    LR.RecordedTimeStamp
FROM @TargetAssociationDetails TAD
    INNER JOIN  (
                    SELECT
                        MAX(LR.RecordedTimeStamp) AS RecordedTimeStamp,
                        LR.MSISDN
                    FROM @LocationResults LR
                    GROUP BY
                        LR.MSISDN
                ) _LR ON _LR.MSISDN = TAD.MSISDN
    INNER JOIN @LocationResults LR ON [_LR].MSISDN = LR.MSISDN AND [_LR].RecordedTimeStamp = LR.RecordedTimeStamp

但我公平地说,我已经习惯了迈克的方式。它是一个较小而且很好的例子。

答案 4 :(得分:0)

当可用的dor使用时,ROW_NUMBER() OVER()只能找到您需要的行。

然后你可以加入相关的表格。以下是使用"派生表":

的示例
SELECT *
FROM (
        SELECT *,
        row_number() over (partition by MSISDN, order by RecordedTimeStamp desc)  as rowno
    FROM LocationResults 
     ) as lr 
LEFT OUTER JOIN TargetAssosiationDetails tad ON tad.IMEI = lr.IMEI AND tad.IMSI = lr.IMSI AND tad.MSISDN = lr.MSISD
WHERE lr.rowno = 1

其他人提出了一个"公用表表达式" (CTE - 这些要求使用WITH ... AS)但使用该方法没有任何优势。我个人认为,在不加区别地使用CTE之前,SQL的相关新手理解并熟悉派生表非常重要。