SQL: Как вернуть только 1 предыдущую дату для записи, а не все предыдущие даты - PullRequest
0 голосов
/ 24 сентября 2019

У меня есть очень простая таблица идентификаторов и дат входа в систему, и я хочу использовать 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 ]

1 голос
/ 24 сентября 2019

Попробуйте использовать LAG, если вы используете современную версию SQL Server.

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

FROM [SIGNIN]
0 голосов
/ 24 сентября 2019

Я думаю, что если вам нужно сделать это, лучше сделать столбец заказа, например:

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

Надеюсь, это поможет.

0 голосов
/ 24 сентября 2019

Коррелированный подзапрос - очень простое решение:

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 
0 голосов
/ 24 сентября 2019

Мне нравится решение 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.

Также привыкните к написанию дат в формате YYYY-MM-DD, поскольку 03/01/19 может привести к путанице.

0 голосов
/ 24 сентября 2019

Следующее даст вам почти то, что вы ищете, просто без нуля.Вам, вероятно, следует выполнить левое внешнее или правое внешнее объединение во внутреннем запросе, а также выполнить дополнительный маневр для добавления пустых строк.Я горит

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
0 голосов
/ 24 сентября 2019

используя это:

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 
0 голосов
/ 24 сентября 2019

Попробуйте что-то вроде этого:

SELECT 
ID, SignInDate, 
LAG(SignInDate, 1,SignInDate) OVER(order by ID partition by ID)
FROM SIGNIN
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...