Группировка по + - 30 минут относительно текущего значения даты и времени - PullRequest
0 голосов
/ 04 июня 2018

У меня есть дубликаты в таблице, и я должен удалить их.

Почему?Пользователь может нажимать кнопки «Сохранить» или «Сохранить и закрыть», и из-за ошибки были клоны записи, когда он несколько раз нажимал кнопку «Сохранить».Типичная ситуация.

Пользователь может добавлять дубликаты в таблицу, но не чаще, чем один раз в час, сеанс с окном около 30 минут.

Другими словами - мы должны удалить записи, которые были созданы в + -30 минут.

Мне нужна помощь, могу ли я решить свою задачу без циклов (курсоров)?

Мои попытки и примеры данных:

declare @testData table(id int, createdOn datetime, val varchar(20))

insert into @testData(id, createdOn, val)
select 1, '2018-06-01 14:00:00' as CreatedOn, 'value1' as value1
union select 2, '2018-06-01 14:02:00', 'value1'  -- duplicate
union select 3, '2018-06-01 14:04:00', 'value1'  -- duplicate
union select 4, '2018-06-01 15:00:00', 'value2'
union select 5, '2018-06-01 15:02:00', 'value2'  -- duplicate
union select 6, '2018-06-01 15:03:00', 'valueUniq1'
union select 7, '2018-06-01 15:04:00', 'valueUniq2'
union select 8, '2018-06-01 15:40:00', 'value2'
union select 9, '2018-06-01 15:41:00', 'valueUniq3'
union select 10, '2018-06-01 15:59:00', 'value1'  -- NOT DUPLICATE!!!
union select 11, '2018-06-01 16:05:00', 'value1'  -- duplicate

-- Option 1

;
with duplicates(IdDup, CreatedOnDup, valueDup)
as (
    select a.Id, a.CreatedOn, a.val
    from @testData a, @testData b
    where a.id <> b.id
        and a.val = b.val
        and a.CreatedOn between dateadd(minute, -30, b.CreatedOn) and dateadd(minute, 30, b.CreatedOn)
)
select * from @testData
where Id in (
    select IdDup
    from duplicates)
and Id not in (
    select min(IdDup)
    from duplicates
    group by valueDup)

-- Option 2

;
with duplicates(CounterDup, IdDup)
as (
    select ROW_NUMBER() OVER(
        Partition By 
            a.val
            , cast(a.CreatedOn as date)  -- Incorrect, must be +- 30 minutes, not the whole day
        Order By a.Id ASC) As counterDup
        , a.Id as idDup
    from @testData a, @testData b
    where a.id <> b.id
        and a.val = b.val
        and a.CreatedOn between dateadd(minute, -30, b.CreatedOn) and dateadd(minute, 30, b.CreatedOn)
    )
select * from @testData
where Id in (
    select IdDup
    from duplicates
    where CounterDup > 1)
and Id not in (
    select IdDup
    from duplicates
    where CounterDup = 1)

Оба подхода возвращаютсяте же результаты, строки для удаления (дубликаты):

2 2018-06-01 14:02:00.000   value1
3 2018-06-01 14:04:00.000   value1
5 2018-06-01 15:02:00.000   value2
10 2018-06-01 15:59:00.000  value1
11 2018-06-01 16:05:00.000  value1

Предпоследняя строка не должна быть в наборе результатов.

10  2018-06-01 15:59:00.000 value1

Это не дубликат, это новый сеанс,потому что> 30 минут после предыдущего "value1".

Ответы [ 2 ]

0 голосов
/ 04 июня 2018

Если вы хотите попробовать без задержки, вы можете использовать этот запрос для предыдущей версии SQL

 Select * into  #tmp from 
(Select ROW_NUMBER() OVER(partition by val order by createdOn) valorder ,* 
 from @testData 
) t


Select * from #tmp a
inner join #tmp b on a.id = (b.id + 1) and a.val = b.val
where DATEDIFF(mi, b.CreatedOn, a.CreatedOn) <=30

drop table #tmp;
0 голосов
/ 04 июня 2018

Вы можете использовать LAG https://docs.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-2017 с разделом здесь для этого.Это вернет те, которые дублируют правила, которые вы заявили, что вы хотите.Я использовал cte и добавил пару столбцов, чтобы вы могли визуализировать, что там происходит.

with MyCte as
(
    select *
        , LagResult = LAG(createdon, 1) over(partition by val order by createdon)
        , IsNewSession = case when datediff(minute, createdon, LAG(createdon, 1) over(partition by val order by createdon)) < -30 then 1 else 0 end
    from @testData
)
select *
from MyCte
where IsNewSession = 0
    and LagResult is not null
...