#sql-server #sql-server-2016
#sql-сервер #sql-server-2016
Вопрос:
У меня есть куча пар значений (до, После) от пользователей в таблице. В идеальных сценариях эти значения должны образовывать непрерывную цепочку. например
| UserId | Before | After |
|--------|--------|-------|
| 1 | 0 | 10 |
| 1 | 10 | 20 |
| 1 | 20 | 30 |
| 1 | 30 | 40 |
| 1 | 40 | 30 |
| 1 | 30 | 52 |
| 1 | 52 | 0 |
К сожалению, эти записи происходят из нескольких разных таблиц и импортируются в мою таблицу исследований. Другие значения в таблице не поддаются упорядочению (например, CreatedDate) из-за некоторых особенностей системы, из-за которых они не упорядочены.
Мне нужно создать список пользователей с пробелами в их данных. например
| UserId | Before | After |
|--------|--------|-------|
| 1 | 0 | 10 |
| 1 | 10 | 20 |
| 1 | 20 | 30 |
// Row Deleted (30->40)
| 1 | 40 | 30 |
| 1 | 30 | 52 |
| 1 | 52 | 0 |
Я просмотрел другие вопросы последовательной цепочки в SO (и онлайн в целом), но все они, похоже, относятся к заданному проблемному пространству, где одно значение в паре всегда предсказуемым образом ниже другого. В моем случае может быть увеличение или уменьшение.
Есть ли способ быстро вычислить самую длинную цепочку, которую можно создать? У меня есть CreatedAt
столбец, который обеспечивал бы некоторый (очень приблизительный) относительный порядок — когда даты отличаются друг от друга более чем на 10 секунд, мы могли бы считать их упорядоченными)
Ответ №1:
Не поэтому ли вы просто после этого получаете первую строку, в которой «цепочка» разорвана?
SELECT UserID, Before, After
FROM dbo.YourTable YT
WHERE NOT EXISTS (SELECT 1
FROM dbo.YourTable NE
WHERE NE.After = YT.Before)
AND YT.Before != 0;
Если вы хотите, чтобы последняя строка находилась там, где строка, в которой «цепочка» разорвана, просто поменяйте местами псевдонимы в столбцах в WHERE
в NOT EXISTS
.
Комментарии:
1. К сожалению, нет, поскольку значения могут увеличиваться или уменьшаться с помощью любых значений, может существовать один (или много) экземпляров значений до / после. Я попытался использовать
LAG
функцию (вероятно, мне следует добавить это к моему вопросу на самом деле), чтобы сравнить ожидаемое с фактическим, но порядок все испортил.2. Обычно ошибка уравновешивается сама собой, но если последняя запись в моем заказе также имеет разницу, она, как правило, не уравновешивается в целом
3. Итак, что вам здесь нужно. Укажите ожидаемые результаты в своем вопросе, @ObsidianPhoenix .
4. Примечание
LAG
здесь вам не поможет, если значение может уменьшаться и увеличиваться, поскольку у вас (согласно вашим выборочным данным) нет ключа / идентификатора always ascending.
Ответ №2:
далее выполняется иерархическая рекурсия данных вашего примера и вычисляется столбец «chain» count с именем ‘h_level’.
;with recur_cte([UserId], [Before], [After], h_level) as (
select [UserId], [Before], [After], 0
from dbo.test_table
where [Before] is null
union all
select tt.[UserId], tt.[Before], tt.[After], rc.h_level 1
from dbo.test_table tt join recur_cte rc on tt.UserId=rc.UserId
and tt.[Before]=rc.[After]
where tt.[Before]<tt.[after])
select * from recur_cte;
Результаты:
UserId Before After h_level
1 NULL 10 0
1 10 20 1
1 20 30 2
1 30 40 3
1 30 52 3
Полезно ли это? Не могли бы вы дополнительно определить, какие строки исключить?
Ответ №3:
Если вы хотите, чтобы пользователи имели более одной цепочки:
select t.UserID
from <T> as t left outer join <T> as t2
on t2.UserID = t.UserID and t2.Before = t.After
where t2.UserID is null
group by t.UserID
having count(*) > 1;