Sql Server: Есть ли способ группировать записи по датам на основе дат других записей?
У меня есть таблица, в которой есть события, и мне нужно найти повторяющиеся события. Проблема в том, что события, происходящие в течение 1 секунды друг от друга, считаются дубликатами. Итак, если моя таблица имеет эти значения
id | var1 | var2 | var3 | date
1 | 1 | 2 | 3 | 2001-01-01 01:01:01.456
2 | 1 | 2 | 3 | 2001-01-01 01:01:02.234
3 | 1 | 2 | 3 | 2001-01-01 01:01:04.789
Записи 1 и 2 считаются дубликатами, потому что они находятся в пределах одной секунды, но 3-не потому, что это больше, чем одна секунда после 2.
Есть ли способ написать запрос, который выбирает только первую запись в серии дубликатов?
Правка: там также могут быть строки, которые не дублируются,которые нужно будет захватить. Id является первичным ключом таблицы и не используется в критериях соответствия; он просто существует для уточнения.
3 ответов:
Вот способ, который, кажется, должен работать для вас.
Некоторые предположения:
- я предполагаю, что дубликат-это фактический дубликат строки, исключающий ID, основанный на 1-м предложении, которое вы предоставили. Если это не так... удалите раздел по части функции окна
row_number(), и это изменит поведение- это удаляет рекурсивные дубликаты. То есть, если 3,4 или даже 15 строк находятся в пределах секунды друг от друга, он сохраняет 1.
- это должно работать независимо от того, если первая или последняя строка является дубликатом
Вот код. Раскомментируйте две строки в таблице, чтобы увидеть изменения
declare @table table(id int, var1 int, var2 int, var3 int, date datetime2) insert into @table values --(0,1,2,3,'2001-01-01 00:01:01.456'), (1,1,2,3,'2001-01-01 01:01:01.456'), --dupe of 1/2/3 (2,1,2,3,'2001-01-01 01:01:02.214'), --dupe of 1/2/3 (3,1,2,3,'2001-01-01 01:01:02.234'), --dupe of 1/2/3 (4,1,2,3,'2001-01-01 01:01:02.244'), --dupe of 1/2/3 (5,1,2,3,'2001-01-01 01:01:04.789'), --dupe of 4/5 (6,1,2,3,'2001-01-01 01:01:04.989'), --dupe of 4/5 --(7,1,2,3,'2001-01-01 01:01:06.789'), --dupe of 6/7 (8,1,2,3,'2001-01-01 01:01:06.799') --dupe of 6/7 --apply the sequence ;with cte as( select *, ROW_NUMBER() over (partition by var1, var2, var3 order by date) as RN --just in case... change this to just order by id, date if need be and remove the partition from @table), --get first / most of the batch to remove cte2 as( select c1.* ,c2.RN as RowsToRemove from cte c1 left join cte c2 on c1.RN < c2.rn and datediff(second,c1.date,c2.date) < 1), --remove the rows identified in the above cte cte3 as( select distinct ID, var1, var2, var3, date, RN from cte2 where RN not in (select distinct isnull(RowsToRemove,0) from cte2)), --add another sequence. This is necessary for first/last row check for duplicate cte4 as( select f.*, row_number() over (partition by var1, var2, var3 order by date) RN2 from cte3 f) --return the results select f.ID, f.var1, f.var2, f.var3, f.date from cte4 f left join cte4 d on d.RN = f.RN - 1 where isnull(datediff(second,d.date,f.date),500) > 1Возвращает
+----+------+------+------+-----------------------------+ | ID | var1 | var2 | var3 | date | +----+------+------+------+-----------------------------+ | 1 | 1 | 2 | 3 | 2001-01-01 01:01:01.4560000 | | 5 | 1 | 2 | 3 | 2001-01-01 01:01:04.7890000 | | 8 | 1 | 2 | 3 | 2001-01-01 01:01:06.7990000 | +----+------+------+------+-----------------------------+
ЛАГ является одним из возможных решений, что-то вроде этого:
select * from ( select *, lag(date,1) over(order by date) previoustime from yourtable ) x where datediff(second,previoustime,date)<1
select T1.date,... from MyTable T1 left outer join MyTable T2 on cast(T1.date as date) = cast(T2.date as date) and datediff(second,T1.date,T2.date)<=1 group by cast(T1.date as date)
Comments