2009-08-06 6 views
2

Voici la situation:SQL Server - Sélection disque le plus récent pour générer une liste des changements

J'ai une base de données de « tickets », et nous trace des modifications aux billets à chaque fois qu'ils sont enregistrés. Je cherche en particulier à des changements d'état, qui permettent de suivre le format suivant:

STATUT: {DE}: {TO}

avec {FROM} et {TO} changer les statuts respectifs. Ce que je dois faire est de générer des nombres par semaines du nombre de billets qui étaient 'ouverts' (c'est-à-dire en état de brouillon) à la fin d'une semaine donnée, disons pour les 12 dernières semaines. Cependant, vous n'êtes pas limité à «fermer» un ticket, puis à le rouvrir ou à effectuer plusieurs changements en une seule semaine. Donc, ce que je dois faire est de modifier le SQL ci-dessous pour considérer SEULEMENT la "action" la plus récente pour une entrée donnée. De cette façon, nous évitons le problème d'avoir des entrées qui étaient «fermées» dans le compte ouvert parce qu'elles avaient été ouvertes plus tôt.

SELECT track.historyID 
     FROM RS_HistoryTracker track 
    WHERE (track.action = 'STATUS:INITIAL:DRAFT' 
      OR track.action = 'STATUS:DELETED:DRAFT' 
      OR track.action = 'STATUS:DRAFT:DRAFT') 
     AND track.trackDateTime <= @endOfWeek 

Toutefois, cette déclaration est contenue dans une autre instruction select, et est utilisé pour générer une liste complète des articles d'histoire:

SELECT COUNT(DISTINCT his.historyID) AS theCount 
    FROM RS_History his 
WHERE his.historyID IN 
     (SELECT track.historyID 
      FROM RS_HistoryTracker track 
     WHERE (track.action = 'STATUS:INITIAL:DRAFT' 
       OR track.action = 'STATUS:DELETED:DRAFT' 
       OR track.action = 'STATUS:DRAFT:DRAFT') 
      AND track.trackDateTime <= @endOfWeek) 

Alors, comment puis-je faire considérer la sélection intérieure que le plus récent suivi 'action' qui s'est produite jusqu'à ou à la date endOfWeek? HistoryTracker contient une colonne timbre datetime.

+0

Je suppose que vous avez un champ ID de ticket dans la piste de table. Donc, essentiellement, vous voulez vous joindre à un ensemble de résultats qui contient l'identifiant de l'historique maximum pour chaque ID de ticket. – pjp

+0

Oui, nous suivons l'ID du ticket dans la table des pistes – Ipster

+0

Comme pour l'ID d'historique max, cela peut ne pas être le cas. Si j'exécute une requête depuis une semaine il y a 3 mois, j'ai besoin de l'élément d'historique le plus récent jusqu'à la date de fin de la semaine, en ignorant tous les éléments de l'historique qui se sont produits après cette semaine. – Ipster

Répondre

0

On dirait que cela fonctionne:

SELECT query.historyID 
    FROM 
    (SELECT MAX(track.trackID) AS maxTrackID, track.historyID 
    FROM RS_HistoryTracker track 
    WHERE track.trackDateTime <= '2009-08-06 23:59:59' 
     AND track.action LIKE 'STATUS:%' 
    GROUP BY historyID) AS query, RS_HistoryTracker track 
    WHERE track.historyID = query.historyID 
     AND track.trackID = query.maxTrackID 
     AND track.action LIKE 'STATUS:%:DRAFT' 
+0

Merci pour toutes les suggestions - j'ai pris des morceaux et une partie de chacun et les ai intégrés dans ceci. – Ipster

+0

Bien que l'opérateur LIKE peut vous donner un raccourci pour rendre le code plus court; sérieusement, ne l'utilisez pas si vous pouvez l'éviter. La raison en est que cela empêche l'optimiseur d'utiliser des index, ce qui gâche généralement les performances. – MatBailie

+1

Aussi, juste un commentaire général lors de l'utilisation des plages de dates, il est préférable d'utiliser "> = @start" et "<@end". Donc si vous voulez tout jusqu'à la fin du 6 août, reformulez tout comme avant le 7 août. La raison en est que la logique est vraie quel que soit le type de données que vous utilisez. Dans votre exemple "<= @end", que se passe-t-il si l'horodatage est quelques millisecondes après 23:59:59? Et si TrackDateTime est un SmallDateTime, il vous suffit d'utiliser 23:59 sans les secondes. L'utilisation de "<@end" fait disparaître toutes ces considérations, et beaucoup plus dans d'autres requêtes. – MatBailie

1

En entrée, vous trouverez le dernier élément d'historique pour chaque billet en faisant quelque chose comme ça

select * from 
(
    --find max history id for each ticket 
    select 
     T1.ticketId, 
     max(T1.historyId) As LastHistoryId 
    from #Ticket T1 
    --add WHERE clause to filter out dates 
    group by 
     T1.ticketId 
) MaxTicket 

inner join 
#Ticket T2 --find the ticket so you can get the status 
on MaxTicket.ticketId = T2.ticketId 
and MaxTicket.LastHistoryId=T2.Historyid 

Vous pouvez modifier la façon dont vous trouverez le dernier billet pour être basé sur la date plutôt que l'histoire id.

+0

Ajout d'une clause 'LIKE' pour filtrer les mises à jour qui ne traitent pas de l'état. Travailler à date. Merci pour la suggestion – Ipster

+0

historid est une clé étrangère - il n'y a aucune valeur à faire des fonctions d'agrégat dessus, elles seront toujours les mêmes. –

1

Le sont de nombreuses variantes de cette question flottant autour sur stackoverflow, voici le premier que je trouve :)

sql-query-to-get-most-recent-row-for-each-instance-of-a-given-key

Essentiellement, vous avez besoin de le faire en deux parties.
- Utilisez une requête pour trouver l'horodatage le plus récent par article
- Utilisez une autre requête pour faire le travail que vous mettez à faire

pour trouver tous les éléments encore ouverts à une date donnée:

SELECT 
    [data].* 
FROM 
    track AS [data] 
WHERE 
    [data].trackDateTime = 
    (
     SELECT 
      MAX(trackDateTime) 
     FROM 
      track 
     WHERE 
      track.ticketID = [data].ticketID 
      AND track.DateTime < @endOfWeek 
    ) 
    AND track.action IN ('STATUS:INITIAL:DRAFT','STATUS:DELETED:DRAFT','STATUS:DRAFT:DRAFT')) 

Ceci suppose que ticketID est l'identifiant unique de chaque ticket. (Basé sur un de vos commentaires)

1

Travaillera avec SQL Server 2005+:

WITH history AS (
    SELECT rh.historyID, 
     MAX(rh.action) 'action' 
    FROM RS_HISTORYTRACKER rh 
    WHERE rh.action IN ('STATUS:INITIAL:DRAFT', 'STATUS:DELETED:DRAFT', 'STATUS:DRAFT:DRAFT') 
    AND rh.trackDateTime <= @endOfWeek) 
SELECT COUNT(DISTINCT t.historyID) AS theCount 
    FROM RS_HISTORY t 
    JOIN history h ON h.historyi = t.historyid 

Autre, non-CTE en utilisant la requête:

SELECT COUNT(DISTINCT t.historyID) AS theCount 
    FROM RS_HISTORY t 
    JOIN (SELECT rh.historyID, 
       MAX(rh.action) 'action' 
      FROM RS_HISTORYTRACKER rh 
     WHERE rh.action IN ('STATUS:INITIAL:DRAFT', 'STATUS:DELETED:DRAFT', 'STATUS:DRAFT:DRAFT') 
      AND rh.trackDateTime <= @endOfWeek) h ON h.historyi = t.historyid 
+0

J'ai toujours le problème que cela ne semble pas prendre si le changement d'état le plus récent était STATUS:%: FINAL, qui ne l'incluait pas. C'est un bon début cependant, et je pense. – Ipster

+0

Comment peut-il être inclus dans FINAL si la clause IN ne reçoit que INITIAL: DRAFT, DELETED: DRAFT ou DRAFT: DRAFT? Ou est final une colonne/attribut sur la table RS_HISTORY? –

+0

Désolé, vous avez raison. Je regardais le mauvais code dans ma fenêtre. – Ipster

Questions connexes