2010-11-17 11 views
5

Deux tables temporaires sont créées puis chargées ... Voici le schéma.Script T-SQL - problèmes logiques avec chronologie

Create table #SH ([date] datetime, 
     sched_id int, 
     agent_id int) 

Create table #SD (sched_id int, 
     start_minute int, 
     length int, 
     exception_code int) 

(schéma et la conception est quelque chose que je ne peux pas changer, malheureusement, les deux tables temporaires sont chargés à partir des fichiers plats. Je peux présenter et charger de nouvelles tables temporaires si nécessaire). Un peu d'arrière-plan - La table d'en-tête #SH contient un calendrier de personnes comme «Start_minute» et va pour «schedule_length» en minutes. Par exemple, si la minute de début et la durée du programme étaient 480, 8 heures (8 heures = 480 minutes) et 16 heures (480 minutes plus tard, 4 heures = 960 minutes)

La table #SD contient des exceptions à la entête. Dans l'exemple ci-dessus, la personne aurait probablement une exception de déjeuner qui serait start_minute = 720 et une longueur de 30 (12:00 - 12:30).

Date et agent_id est la seule chose que je suis intéressé de #SH, les informations d'exception dans #sd est ce que je suis intéressé par

Cette requête fonctionne:.

Select [date],#sd.start_minute,#sd.length,#sd.start_minute + #sd.length as 'end_minute',agent_id 
from #SH 
inner join #SD on #SD.sched_id = #sh.sched_id 

* end_minute est en fin de compte une valeur calculée de début + longueur = fin

Ce retourne quelque chose comme:

Date  Start length end 

1 2010-11-11 600 30 630 

2 2010-11-11 630 40 670 

3 2010-11-11 750 15 765 

4 2010-11-11 800 40 840 

Maintenant, je voudrais pouvoir dire que c'était fini et repartir ... mais des problèmes de saisie de données existent. Dans la ligne 1 et 2, l'heure de fin de la ligne 1 aligne avec l'heure de début de la ligne 2 et devraient être combinées pour que mes résultats ressemblent à ceci:

Date  Start length  end 

1 2010-11-11 600 70 670 

2 2010-11-11 750 15 765 

3 2010-11-11 800 40 840 

Toute idée est sur la façon de construire cette logique si je reçois les 3 lignes de retour au lieu de 4? Je travaille à rejoindre la table sur # sd1.start + # sd1.length = # sd2.start pour le moment. Et pour compliquer davantage ... l'exemple ci-dessus était de 2 lignes qui devaient être combinées. J'ai rencontré un enregistrement qui avait successivement 30 entrées de 1 minute que je devrais faire dans un seul enregistrement. Heureusement, ils ne peuvent pas se chevaucher (vous n'aurez pas 2 enregistrements occupant les mêmes minutes), mais je ne pense pas que la déclaration de jointure que je considère ci-dessus fonctionnera pour cela.

+0

J'ai fait quelques modifications sur ma propre grammaire ... il y a quelques colonnes supplémentaires dans #SH, mais elles ne sont pas pertinentes. – Twelfth

Répondre

2

Pas besoin d'un CTE, tout ce que vous avez besoin est une table d'aide. Créer une fois, comme ceci:

Create Table DayMinute(Minute Integer) 
Declare @M Integer 
Set @M = 1 
While (@M <= 24*60) 
Begin 
    Insert Into DayMinute(Minute) Values(@M) 
    Set @M = @M + 1 
End 

Ensuite, tout ce dont vous avez besoin est un peu de tricksiness:

Select 
    DM.Minute, 
    SD.Sched_ID 
Into #MinutesWithException 
From 
    DayMinute As DM 
    Inner Join #SD As SD 
    On DM.Minute Between SD.Start_Minute And SD.Start_Minute + Length 

Select 
    MWE.Sched_ID, 
    SH.[Date], 
    SH.Agent_ID, 
    [Start_Minute] = MWE.Minute, 
    [End_Minute] = (Select Min(Last.Minute) -- First one to have no successor 
        From #MinutesWithException As Last 
        Where Last.Sched_ID = MWE.Sched_ID 
        And Last.Minute > MWE.Minute 
        And Not Exists(Select * 
            From #MinutesWithException As Next 
            Where Next.Sched_ID = MWE.Sched_iD 
            And Next.Minute = Last.Minute + 1)) 
From 
    #MinutesWithException As MWE 
    Inner Join #SH As SH 
    On MWE.Sched_ID = SH.Sched_ID 
Where 
    Not Exists(Select * -- All those without predecessor 
      From #MinutesWithException As Previous 
      Where Previous.Sched_ID = MWE.Sched_ID 
       And Previous.Minute = MWE.Minute - 1) 

Rappelez-vous, beaucoup de problèmes SQL peuvent être résolus en les reformulant. Ne demandez pas "quelles plages n'ont pas d'écart", demandez "quelles minutes ont un intervalle". Le reste suit de là.

+0

Je pensais la même chose sur ce Stu ... nouveau il devait y avoir une autre façon de le regarder. Je dois faire quelques modifications pour l'adapter au reste du script, je vais vous laisser savoir comment ça se passe. Et wow, processeur intensif. – Twelfth

+0

Est-ce vraiment plus efficace ou plus facile à lire? –

+0

Pas sûr sur le plus efficace ... il épingle mon processeur à 100% quand je l'exécute. Il semble faire le tour atleast, je me demande comment ça va réagir quand je le nourris 20k disques au lieu de 4 si .. – Twelfth

1

Si vous utilisez un CTE récursif pour combiner les résultats de la requête ci-dessus, vous pouvez regrouper jusqu'à 32767 enregistrements ensemble. Vous pourriez envisager cette approche si vous ne pensez pas que vous atteindrez jamais ce montant.

J'ai créé un exemple de travail, parce que je n'étais pas sûr. Votre groupe serait différent, mais c'est l'idée générale:

CREATE TABLE times 
(
[Date] datetime, 
[start] int, 
[length] int, 
[end] int 
) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',600,30,630) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',630,40,670) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',750,15,765) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',800,40,840) 

;WITH chaintimes AS 
(
    SELECT t1.Date, t1.start, t1.length, t1.[end] 
    FROM times t1 LEFT JOIN times t2 ON t1.start = t2.[end] 
    WHERE t2.[end] IS NULL 
    UNION ALL 
    SELECT times.Date, chaintimes.start, chaintimes.length + times.length AS length, times.[end] 
    FROM times INNER JOIN chaintimes ON times.start = chaintimes.[end] 
) 
, start_maxlength AS 
(
    SELECT date, start, max(length) AS maxlength 
    FROM chaintimes 
    group by date, start 
) 
SELECT * FROM chaintimes ct 
INNER JOIN start_maxlength ml 
ON ct.Date = ml.Date AND ct.start = ml.start AND ct.length = ml.maxlength 
+0

J'aurais dû mentionner mon environnement sur ce ... MSSQL 2000. Cela ressemble à PL/SQL. Je vais essayer, mais je soupçonne une erreur. Je pense que je comprends la logique que vous avez ici au moins ... laissez-moi essayer de modifier mon code actuel en utilisant ce style de logique – Twelfth

+0

Ah, oui CTE a été introduit dans MS SQL 2005, pas besoin de déranger. –

+0

Bien que je ne sois pas du tout d'accord, ce manque de contexte est une bonne raison pour un * downvote *. –