2010-09-22 4 views
2

J'ai une table qui contient une liste de tâches;Comment structurer l'instruction SQL

TableName: Tâches. Champs: (ID Int, Description nvarchar)

Les tâches sont effectuées quotidiennement et sont consignées dans un tableau comme suit;

TableName TâchesDone. Champs: (TaskID Int, TaskDate DateTime)

J'ai besoin d'une requête qui s'exécute pour une plage de dates et affiche les tâches qui n'ont pas été effectuées (n'existent pas dans la table TasksDone) pour chaque date de la plage.

J'espère que cela a du sens ... Merci pour toute aide.

+0

Il n'y a pas construit dans la fonctionnalité dans SQL Server pour obtenir toutes les dates dans une gamme, ou similaire. Vous devez le construire en utilisant une table de nombres ou une table de calendrier. –

Répondre

2

Vous aurez besoin d'un nombre ou table de calendrier pour rendre les choses faciles, ou nous pouvons simuler une si la plage est faible. Est-ce que la TaskDate est une date claire ou a-t-elle aussi une composante de temps?

Plan de base d'attaque est:

declare @StartDate datetime 
declare @EndDate datetime 

/* Set @StartDate and @EndDate to represent the range */ 

with Digits as (
    select 0 as d union all select 1 union all select 2 union all select 3 union all select 4 union all 
    select 5 union all select 6 union all select 7 union all select 8 union all select 9 
), Numbers as (
    select (D1.d * 100) + (D2.d * 10) + D3.d as n 
    from Digits D1,Digits D2,Digits D3 
), TaskDates as (
    select 
     t.TaskID, 
     DATEADD(day,n.n,@StartDate) as TaskDate 
    from 
     Tasks t 
      inner join 
     Numbers n 
      on 
       DATEADD(day,n.n,@StartDate) <= @EndDate 
) 
select 
    * 
from 
    TaskDates td1 
     left join 
    TasksDone td2 
     on 
      td1.TaskID = td2.TaskID and 
      DATEDIFF(day,td1.TaskDate,td2.TaskDate) = 0 
where 
    td2.TaskID is null 

Les deux premiers CTEs construire un petit nombre table, le 3ème CTE construit un ensemble de TaskIDs et des dates dans la plage requise. La sélection finale fait correspondre les thèses avec la table TasksDone, puis supprime les lignes où une correspondance est trouvée. Si TasksDone.TaskDate est une date normale (pas de composant time) et que @StartDate est également sans composant time, vous pouvez abandonner DATEDIFF et utiliser simplement td1.TaskDate = td2.TaskDate.

Si vous avez besoin d'une large gamme (ci-dessus peuvent couvrir ~ 3 ans), je vous suggère de construire une table de nombre approprié ou une table de calendrier

+0

Génial, ça a l'air génial! Donnez-moi quelques minutes pour l'essayer! – rickj

+0

@rickj - assurez-vous d'utiliser la version actuelle - J'avais les arguments de DATEADD à l'envers quand je l'ai posté pour la première fois. –

+0

Absolument fantastique! Fonctionne un régal. Merci aussi pour la bonne explication, m'aide à comprendre exactement ce qui se passe. – rickj

2

Ceci est assez simple, si je comprends bien le problème:

SELECT * 
FROM Tasks 
WHERE ID NOT IN (SELECT TaskID FROM TasksDone WHERE TaskDate BETWEEN x AND y) 

Remplacer x et y à la date que vous êtes après.

+0

Bonjour, Merci pour cela. Le hic, c'est que je dois montrer les tâches manquantes pour chaque date dans la gamme, toutes les tâches doivent être effectuées tous les jours. Je dois montrer les tâches manquantes pour chaque jour de la gamme. Avoir du sens? – rickj

+0

@rickj, avez-vous déjà essayé la requête? Il fait exactement cela ... –

+0

@Lieven, oui je l'ai fait, et non ça ne me donne pas ce que je suis après. Il montre uniquement les tâches qui ne sont pas effectuées dans toute la plage, pas pour chaque date de la plage.Toutes les tâches du tableau Tâches doivent être complétées CHAQUE jour. J'ai besoin de montrer toutes les tâches qui manquaient pour CHAQUE jour dans la gamme. – rickj

0

Je n'ai pas testé ceci mais voir si cela aide:

select ID, TaskDate as A from Tasks,TasksDone 
where TaskID not in (select TaskID from TasksDone where TaskDate = A) 
GROUP BY TaskDate 
0

Si je comprends correctement, la déclaration suivante devrait vous obtenir les tâches qui n » ai pas t être exécuté tous les jours dans toute la gamme.

Déclaration SQL

SELECT t.* 
FROM @Tasks t 
     INNER JOIN (
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE td.TaskDate BETWEEN @RangeStart AND @RangeEnd   
      GROUP BY 
        td.TaskID 
      HAVING COUNT(TaskID) < CAST(@RangeEnd - @RangeStart AS INTEGER)+1 
      UNION ALL 
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE TaskID NOT IN (SELECT TaskID 
           FROM @TasksDone 
           WHERE TaskDate BETWEEN @RangeStart AND @RangeEnd) 
     ) td ON td.TaskID = t.ID  

script Test

DECLARE @Tasks TABLE (
    ID INTEGER 
    , DESCRIPTION NVARCHAR(32) 
) 

DECLARE @TasksDone TABLE (
    TaskID INTEGER 
    , TaskDate DATETIME 
) 

DECLARE @RangeStart DATETIME 
DECLARE @RangeEnd DATETIME 

SET @RangeStart = GetDate() - 1 
SET @RangeEnd = GetDate() + 1 

INSERT INTO @Tasks 
      SELECT 1, 'Done Every Day in range.' 
UNION ALL SELECT 2, 'Done a few times in range.' 
UNION ALL SELECT 3 , 'Not done anytime in range.' 

INSERT INTO @TasksDone 
      SELECT 1, @RangeStart 
UNION ALL SELECT 1, GetDate() 
UNION ALL SELECT 1, @RangeEnd 
UNION ALL SELECT 2, GetDate() 
UNION ALL SELECT 3, GetDate() + 2 

SELECT t.* 
FROM @Tasks t 
     INNER JOIN (
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE td.TaskDate BETWEEN @RangeStart AND @RangeEnd   
      GROUP BY 
        td.TaskID 
      HAVING COUNT(TaskID) < CAST(@RangeEnd - @RangeStart AS INTEGER)+1 
      UNION ALL 
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE TaskID NOT IN (SELECT TaskID FROM @TasksDone WHERE TaskDate BETWEEN @RangeStart AND @RangeEnd) 
     ) td ON td.TaskID = t.ID  
+0

Merci Lieven, viens de remarquer ta réponse. Je vais essayer aussi! – rickj

+0

Pas de problème. Un peu éclairé à la fête il semble mais si vous pouviez, laissez-nous savoir les résultats. –

+0

@rickj, j'ai raté la clause 'HAVING'. La réponse a été modifiée pour corriger l'erreur. –

Questions connexes