SQL:如何只返回一个记录的前一个日期,而不是所有前一个日期

时间:2019-09-24 14:05:10

标签: sql sql-server datetime

我有一个非常简单的ID和登录日期表,我想使用SQL来创建一列以显示以前的登录日期:

Table: SIGNIN

| ID     | Sign-in Date |
| A      | 01/01/19     |
| B      | 01/01/19     |
| C      | 02/01/19     |
| A      | 02/01/19     |
| A      | 03/01/19     |
| B      | 03/01/19     |
| A      | 04/01/19     |
| C      | 04/01/19     |
| B      | 05/01/19     |

我尝试对自己进行联接,但它显示的是以前的所有登录日期,而不仅仅是最近的登录日期。

SELECT [SIGNIN].ID
       [SIGNIN].SignInDate

FROM [SIGNIN]

INNER JOIN [SIGNIN] as [Prev] on  [SIGNIN].ID         = [Prev].ID
                              and [SIGNIN].SignInDate < [Prev].SignInDate

ORDER BY [SIGNIN].ID, [SIGNIN].SignInDate 

我想要的结果

Table: SIGNIN

| ID     | Sign-in Date | Previous  |
| A      | 01/01/19     | NULL      |
| B      | 01/01/19     | NULL      |
| C      | 02/01/19     | NULL      |
| A      | 02/01/19     | 01/01/19  |
| A      | 03/01/19     | 02/01/19  |
| B      | 03/01/19     | 01/01/19  |
| A      | 04/01/19     | 03/01/19  |
| C      | 04/01/19     | 02/01/19  |
| B      | 05/01/19     | 03/01/19  |

我得到的是什么

| ID     | Sign-in Date | Previous  |
| A      | 01/01/19     | NULL      |
| B      | 01/01/19     | NULL      |
| C      | 02/01/19     | NULL      |
| A      | 02/01/19     | 01/01/19  |
| A      | 03/01/19     | 01/01/19  |
| A      | 03/01/19     | 02/01/19  |
| B      | 03/01/19     | 01/01/19  |
| A      | 04/01/19     | 01/01/19  |
| A      | 04/01/19     | 02/01/19  |
| A      | 04/01/19     | 03/01/19  |
| C      | 04/01/19     | 02/01/19  |
| B      | 05/01/19     | 01/01/19  |
| B      | 05/01/19     | 03/01/19  |

我敢肯定,这已经在其他地方得到了解决,但是我遇到的最大问题是不知道如何表达我的问题!

编辑:到目前为止,这确实很有帮助,但是有没有可以更改日期“截止”日期的解决方案,例如:

Cut off: 03/01/19
Table: The same
Desired result:

| ID     | Sign-in Date | Previous  |
| A      | 03/01/19     | 02/01/19  |
| B      | 03/01/19     | 01/01/19  |
| A      | 04/01/19     | 03/01/19  |
| C      | 04/01/19     | 02/01/19  |
| B      | 05/01/19     | 03/01/19  |

7 个答案:

答案 0 :(得分:1)

假设您使用的是现代版本的SQL Server,请尝试使用LAG。

SELECT [SIGNIN].ID,
       [SIGNIN].SignInDate,
       LAG([SIGNIN].SignInDate) OVER (PARTITION BY [SIGNIN].ID ORDER BY [SIGNIN].SignInDate DESC) AS Previous

FROM [SIGNIN]

答案 1 :(得分:0)

尝试这样的事情:

SELECT 
ID, SignInDate, 
LAG(SignInDate, 1,SignInDate) OVER(order by ID partition by ID)
FROM SIGNIN

答案 2 :(得分:0)

使用此:

SELECT  [SIGNIN].ID,
        [SIGNIN].SignInDate,
        MAX([Prev].SignInDate) as Previous


FROM [SIGNIN]

LEFT JOIN [SIGNIN] as [Prev] on  [SIGNIN].ID         = [Prev].ID
                              and [SIGNIN].SignInDate > [Prev].SignInDate
GROUP BY [SIGNIN].ID, [SIGNIN].SignInDate
ORDER BY [SIGNIN].ID, [SIGNIN].SignInDate 

答案 3 :(得分:0)

以下内容将为您提供几乎所有您想要的内容,只是没有空值。 您可能应该在内部查询中进行左外部联接或右外部联接,并进行一些额外的操作以添加空行。我是被点燃

select id, max(prev) as prev, signindate from
(
SELECT SIGNIN.ID,
       SIGNIN.SignInDate as prev,
       prev.signindate
FROM SIGNIN
 JOIN SIGNIN as Prev on  SIGNIN.ID         = Prev.ID
                              and SIGNIN.SignInDate < Prev.SignInDate
ORDER BY SIGNIN.ID, SIGNIN.SignInDate 
) a 

group by 1,3

答案 4 :(得分:0)

我喜欢APPLY解决方案,因为您可以从匹配的行中添加任意数量的列:

DECLARE @CutOffDate DATE = '2019-01-03'

SELECT 
    S.ID,
    S.SignInDate,
    PreviousSignInDate = R.SignInDate
FROM 
    [SIGNIN] AS S
    OUTER APPLY (
        SELECT TOP 1
            P.* -- Can incorporate many columns (will also have to add them on the outmost SELECT list)
        FROM
            SIGNIN AS P
        WHERE
            S.ID = P.ID AND
            P.SignInDate < S.SignInDate
        ORDER BY
            P.SignInDate DESC
        ) AS R
WHERE
    S.SignInDate >= @CutOffDate
ORDER BY
    S.SignInDate,
    S.ID

在这种情况下,只要您具有链接TOP 1并确保ORDER BY,就可以使用S.ID = P.ID + P.SignInDate < S.SignInDate来获取前一个。 / p>

还习惯于以YYYY-MM-DD格式写日期,因为03/01/19可能会引起混乱。

答案 5 :(得分:0)

相关子查询是一个非常简单的解决方案:

SELECT ID, SignInDate,
       (SELECT top 1 SigInDate
        FROM SIGNIN as S2
        WHERE S2.ID = S1.ID and S2.SignInDate < S1.SignInDate
        ORDER BY S2.SignInDate desc) as Previous
FROM SIGNIN as S1
ORDER BY S1.ID, S1.SignInDate 

答案 6 :(得分:0)

我认为,如果您需要这样做,最好设置一个排序列,例如:

SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY SignInDate) AS O FROM [SIGNIN]

所以最终结果将是:

SELECT t.ID, t.SignInDate [Sign-In Date], t2.SignInDate as Previous FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY SignInDate) AS O FROM [SIGNIN]) t LEFT JOIN (SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY SignInDate) AS O FROM [SIGNIN]) t2 ON t.ID = t2.ID AND t.O = t2.O+1

应该给什么类似于:

A 2019-01-01 NULL A 2019-01-04 2019-01-01 A 2019-02-01 2019-01-04 B 2019-01-01 NULL B 2019-01-05 2019-01-01 C 2019-01-01 NULL

希望这会有所帮助。