2010-10-28 2 views
1

J'ai une table comme celui-ciMax dans un délai avec la date duplique

DateTime start_time not null, 
DateTime end_time not null, 
Status_Id int not null, 
Entry_Id int not null 

Je veux obtenir le nombre de chaque état dans un délai de temps, où seul le dernier démarrage est valable pour une entry_id donnée.

Ce que je me sers maintenant est la suivante (avec des dates dynamiques):

with c (Status_Id, Entry_Id, Start_Date) AS (
    select Status_Id, Entry_Id, Start_Date from tbl where 
    (End_Date BETWEEN '19000101' AND '21000101') 
    AND ((Start_Date BETWEEN '19000101' AND '21000101') 
    OR End_Date <= '21000101')) 
select Status_Id, count(*) as cnt from 
(select Entry_Id, max(start_date) as start_date from c 
    group by Entry_Id) d inner join 
c on c.Entry_Id = d.Entry_Id 
and c.start_date = d.start_date 
GROUP BY Status_Id WITH ROLLUP 

Le problème est qu'il compte mal quand il y a des entry_id qui ont des entrées multiples de la même start_date. (Je ne me soucie pas particulièrement que le statut est choisi dans ce cas, il suffit que seulement 1 est choisi)

Certaines données de test:

status_id Entry_id Start_date 
496 45173 2010-09-29 18:04:33.000 
490 45173 2010-09-29 18:48:20.100 
495 45173 2010-09-29 19:25:29.300 
489 45174 2010-09-29 18:43:01.500 
493 45175 2010-09-29 18:48:00.500 
493 45175 2010-09-29 21:16:02.700 
489 45175 2010-09-30 17:52:12.100 
493 45176 2010-09-29 17:55:21.300 
492 45176 2010-09-29 18:20:52.200 <------ This is the one that gives the problems 
493 45176 2010-09-29 18:20:52.200 <------ This is the one that gives the problems 

Le résultat devrait être

495 1 
489 2 
492 1 (or 493 1) 

Répondre

1

Alter réponse native basée sur OPs lovely commentaires.

WITH 
    [sequenced_data] 
AS 
(
    SELECT 
    *, 
    ROW_NUMBER() OVER (PARTITION BY entry_id ORDER BY start_time DESC, status_id DESC) AS [sequence_id] 
    FROM 
    tbl 
    WHERE 
    start_time < '21:00' AND end_time > '19:00' 
) 
SELECT status_id, COUNT(*) 
FROM [sequenced_data] 
WHERE sequence_id = 1 
GROUP BY status_id 

La fonction ROW_NUMBER() est nécessaire que là où il n'y a pas un seul champ qui peut identifier de manière unique les enregistrements individul. Des requêtes alternatives peuvent être écrites lorsqu'il existe une colonne d'identité unique dans les données. SQL Server, cependant, est extrêmement efficace pour optimiser les requêtes ROW_NUMBER() comme ci-dessus et il devrait (en supposant que les index pertinents) soient efficaces.

EDIT

Quelqu'un vient de me suggéré que les gens n'aiment pas le code long, ils préfèrent code compact. Ainsi, la version CTE a été remplacée par une version en ligne (La CTEs ventilation vraiment juste aidé à la requête pour des raisons explicatives, et dans l'historique des modifications si nécessaire) ...

EDIT

ROW_NUMBER() ne peut pas faire partie de la clause WHERE, comme indiqué par OP. Requête mise à jour en remettant un CTE.

+0

Ohh, c'est une très très belle solution! (Votre édition est cassée cependant, le numéro de la ligne doit être dans le select). Il est aussi deux fois plus rapide que celui que j'ai inventé, puisqu'il n'a pas à dupliquer l'élimination, juste un type spécialisé – Cine

2

Si i correctement compris, vous voulez comptabiliser une entrée distincte pour un statut spécifique dans votre période de temps ... si c'est le cas, vous devez utiliser la clause DISTINCT dans votre count() changement de compte (*) en compte (distinct Entry_id)

with c (Status_Id, Entry_Id, Start_Date) AS (
    select Status_Id, Entry_Id, Start_Date from tbl where 
    (End_Date BETWEEN '19000101' AND '21000101') 
    AND ((Start_Date BETWEEN '19000101' AND '21000101') 
    OR End_Date <= '21000101')) 
select Status_Id, count(distinct Entry_Id) as cnt from 
(select Entry_Id, max(start_date) as start_date from c 
    group by Entry_Id) d inner join 
c on c.Entry_Id = d.Entry_Id 
and c.start_date = d.start_date 
GROUP BY Status_Id WITH ROLLUP 

EDIT

Tant que vous ne se soucient pas de quel statut est retour pour une entrée donnée, je pense que vous pouvez modifier la requête interne pour renvoyer le premier statut et rejoindre le statut trop

with c (Status_Id, Entry_Id, Start_Date) AS (
    select Status_Id, Entry_Id, Start_Date from tbl where 
    (End_Date BETWEEN '19000101' AND '21000101') 
    AND ((Start_Date BETWEEN '19000101' AND '21000101') 
    OR End_Date <= '21000101')) 
select c.Status_Id, count(c.Entry_Id) as cnt from 
(select Entry_Id, Start_Date, (select top 1 Status_id from c where Entry_Id = CC.Entry_Id and Start_Date = CC.Start_Date) as Status_Id 
    from (select Entry_Id, max(start_date) as start_date from c 
    group by Entry_Id) as CC) d inner join 
c on c.Entry_Id = d.Entry_Id 
and c.start_date = d.start_date 
and c.status_id = d.status_id 
GROUP BY c.Status_Id 

Résultat

Status_id Count 
489  2 
492  1 
495  1 
+0

Celui-ci ne fonctionnera que si les status_ids sont les mêmes pour les doublons – Cine

+0

Vous devez compter différent Entry_Id qu'une seule fois? –

+0

S'il y a un entry_id qui a plusieurs entrées avec le même max (start_time), alors il ne doit être inclus qu'une seule fois. Peu importe ce qu'est le status_id. Le vôtre l'inclura, si la date du doublon a un status_id différent – Cine

0

J'ai trouvé une solution moi-même:

with c (Status_Id, Entry_Id, Start_Date) AS (
    select Status_Id, Entry_Id, Start_Date from tbl where 
    (End_Date BETWEEN '19000101' AND '21000101') 
    AND ((Start_Date BETWEEN '19000101' AND '21000101') 
    OR End_Date <= '21000101')) 
select Status_Id, count(*) as cnt from 
(select max(Status_Id) as Status_Id, c.Entry_Id from --<--- ADDED 
(select Entry_Id, max(start_date) as start_date from c 
    group by Entry_Id) d inner join 
c on c.Entry_Id = d.Entry_Id 
and c.start_date = d.start_date 
group by c.Entry_Id) y --<--- ADDED 
GROUP BY Status_Id WITH ROLLUP 
Questions connexes