2017-07-14 1 views
2

J'ai une table qui contient des événements et j'ai besoin de trouver des événements en double. Le problème est que les événements qui se produisent avec 1 seconde l'un de l'autre sont considérés comme des doublons. Donc, si ma table a ces valeursSql Server: Un moyen de grouper les enregistrements par date en fonction des dates des autres enregistrements?

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 

enregistrements 1 et 2 sont considérés comme des doublons parce qu'ils sont moins d'une seconde, mais 3 n'est pas parce qu'il est plus d'une seconde après 2.

est-il un moyen de écrire une requête qui sélectionne seulement le premier enregistrement dans une série de doublons?

EDIT: Il peut également y avoir des lignes qui ne sont pas dupliquées et qui doivent être capturées. Id est la clé primaire de la table et n'est pas utilisé dans les critères correspondants; c'est juste là pour clarifier.

+1

s'il y a trois enregistrement dans une seconde de l'autre ... TENIR 1? et ID est juste un PK, ce qui signifie que var1, var2 et var3 devraient être considérés pour les doublons? c'est-à-dire s'ils ne sont pas identiques, alors ce n'est pas un doublon? – scsimon

+1

@scsimon Oui. Tous les enregistrements où var1, 2 et 3 sont identiques et la date est dans 1 seconde, seul 1 enregistrement doit être conservé. Peu importe s'il y en a 2 ou 200 dans cette gamme. Id est un PK. – RossD

+0

Merci pour la clarification @RossD. Je viens d'éditer ma réponse et je * thin * ça devrait marcher dans tous les cas. laissez-moi savoir si ce n'est pas le cas. Je l'ai laissé dans plusieurs cte pour plus de clarté. – scsimon

Répondre

2

Voici une façon qui semble que cela devrait fonctionner pour vous.

Quelques hypothèses:

  1. Je suppose en double est une double rangée réelle hors ID, sur la base de 1 seconde clause que vous avez fourni. Si ce n'est pas le cas ... supprimez la partition par une partie de la fonction de fenêtre row_number() et cela changera le comportement
  2. Cela supprime les doublons récursifs. Autrement dit, si 3,4 ou même 15 lignes sont dans une seconde de l'autre, il garde 1.
  3. Cela devrait fonctionner, peu importe si la première ligne ou la dernière ligne est un double

Voici le code . Décommenter les deux lignes de la table pour voir les changements

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 

RETOURS

+----+------+------+------+-----------------------------+ 
| 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 | 
+----+------+------+------+-----------------------------+ 
2

Lag est une solution possible, quelque chose comme ceci:

select * from (
select *, lag(date,1) over(order by date) previoustime from yourtable 
) x 
where datediff(second,previoustime,date)<1 
0
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)