2009-07-22 3 views
0

J'ai une table avec des enregistrements datés. Je veux une requête qui montrera le nombre d'enregistrements pour chaque date.Est-il possible d'effectuer une jointure sur une table de dates consécutives sans devoir gérer une table réelle de dates consécutives?

Pas dur, juste un GROUP BY, non? Sûr.

Mais je veux aussi montrer les jours où il n'y avait pas d'enregistrements. Dans ce cas, il faudrait que je me joigne à une table de dates consécutives à une seule colonne, car un GROUP BY ne montrera pas de date qui n'existe pas.

Y a-t-il une meilleure façon de procéder? Ou est-ce que je dois créer une table de dates pour que je puisse me joindre à elle?

+0

Je devais choisir une seule réponse, mais toutes étaient très bonnes et très intéressantes. Merci à tout le monde. – Deane

Répondre

3

Il serait probablement plus facile de construire le tableau des dates

Par exemple, si vous voulez que toutes les dates partir du 1er janvier 2009 au aujourd'hui, vous pouvez effectuer les opérations suivantes:

DECLARE @start AS DATETIME, 
     @end AS DATETIME 

SELECT @start = '2009-01-01', 
     @end = getdate() 

DECLARE @dates TABLE (
         dt DATETIME 
         ) 

WHILE (@start < @end) 
BEGIN 
    INSERT @dates 
    SELECT @start 

    SELECT @start = DATEADD(day, 1, @start) 
END 

SELECT * 
FROM @dates 

@dates vous avoir un enregistrement pour chaque jour de @start à @end.

1

Ce serait la solution la plus simple. Une fois que vous avez cette table, j'imagine que vous trouverez beaucoup d'utilisations pour cela. La clé est d'avoir un travail annuel qui va ajouter de nouvelles dates ou il ne sera pas synchronisé avec les dates actuelles dans le système. Et n'oublie pas de l'indexer puisque tu vas y participer. Vous pouvez également remplir cette variable dans une table temporaire ou une variable de table au moment de l'exécution, mais cela peut prendre plus de temps au moment de l'exécution en fonction du nombre de dates dont vous avez besoin pour chaque exécution. Si vous cherchez simplement les dates du mois dernier, c'est peut-être le chemin à parcourir, si vous avez besoin des 5 dernières années, j'irais avec un tableau de dates pré-rempli.

0

Une variante amusante sur la solution de Jon en utilisant des jointures croisées:

DECLARE @BaseDate DATETIME 
Select @BaseDate = getdate() - 365 

DECLARE @digits TABLE (d char(1)) 

DECLARE @i int 
SET @i = 0 
WHILE (@i < 10) BEGIN 
    INSERT @digits SELECT cast(@i as char(1)) 
    Select @i = @i +1 
END 

SELECT DATEADD(d, cast(d1.d + d2.d + d3.d as int), @BaseDate) 
FROM @digits d1 CROSS JOIN @digits d2 CROSS JOIN @digits d3 
WHERE cast(d1.d + d2.d + d3.d as int) < 365 
ORDER BY d1.d, d2.d, d3.d 
2

Si vous ne voulez vraiment pas une table.

Sur Oracle, vous pouvez faire quelque chose comme:

select D.D, count(T.CompareDate) 
from (select to_date('2009-07-01', 'YYYY-MM-DD') + LEVEL - 1 as D 
    from dual 
    connect by LEVEL <= 30) D 
left outer join T ON T.CompareDate = D.D 
group by D.D 

Lorsque la date est à l'intérieur to_date la date de départ, et vous tester contre niveau pour le nombre de jours que vous voulez dans vos résultats.

Sur SQL Server 2005/2008:

; with X1 (X) as 
    (select 1 union all select 1) 
    , X2 (X) as 
    (select 1 from X1 a cross join X1 b) 
    , X4 (X) as 
    (select 1 from X2 a cross join X2 b) 
    , X8 (X) as 
    (select 1 from X4 a cross join X4 b) 
    , X16 (X) as 
    (select 1 from X8 a cross join X8 b) 
    , NUM (N) as 
    (select row_number() over (order by X) from X16) 
    , D (D) as 
    (select dateadd(day, N-1, '20090701') 
    from NUM 
    where NUM.N <= 30) 
select D.D, count(T.CompareDate) 
from D 
left outer join T on T.CompareDate = D.D 
group by D.D 

La clause with est de construire les dates. La date spécifiée dans le dateadd est la date de début, et le nombre de jours est testé au NUM.N <=30. Vous pouvez également tester contre la date de fin where dateadd(day, N-1, StartDate) <= EndDate.

Je recommanderais d'encapsuler la clause with pour créer les plages en tant que fonction de valeur de table en ligne.

La génération du nombre est basée sur le code que j'ai vu d'Itzik Ben-gan. Chaque X vous donne un nombre de lignes égal à la puissance de 2 du nombre X (X1 = 2 lignes et X8 = 256 lignes). Si vous avez besoin de plus de 65 536, vous devrez ajouter plus de jointures croisées. Si vous n'avez jamais besoin de plus de 256, vous pouvez éliminer X16.En outre, si vous disposez d'une table de nombres, vous pouvez utiliser cette arithmétique et cette date pour générer les dates dont vous avez besoin à la volée.

+0

Votre exemple Oracle peut également être écrit à l'aide de l'affacturage sous-requête/expression de la table commune - la clause WITH est exactement la même entre Oracle 9i + et SQL Server 2005+. –

1

Avec SQL Server 2005, il est beaucoup plus facile - vous pouvez utiliser cette date/heure fonction d'intervalle:

fn_DateRange()

œuvres comme:

-- Hourly blocks 
select 
    dr.startdate 
from 
    fn_DateRange('12/14/2008 08:00:00', '12/14/2008 12:00:00', '01:00:00') dr 
go 
startdate 
----------------------- 
2008-12-14 08:00:00.000 
2008-12-14 09:00:00.000 
2008-12-14 10:00:00.000 
2008-12-14 11:00:00.000 
2008-12-14 12:00:00.000 

-- Daily blocks 
select 
    dr.startdate 
from 
    fn_DateRange('12/14/2008', '12/18/2008', 1) dr 
go 
startdate 
----------------------- 
2008-12-14 00:00:00.000 
2008-12-15 00:00:00.000 
2008-12-16 00:00:00.000 
2008-12-17 00:00:00.000 
2008-12-18 00:00:00.000 

-- Weekly blocks 
select 
    dr.startdate 
from 
    fn_DateRange('12/14/2008', '01/14/2009', 7) dr 
go 
startdate 
----------------------- 
2008-12-14 00:00:00.000 
2008-12-21 00:00:00.000 
2008-12-28 00:00:00.000 
2009-01-04 00:00:00.000 
2009-01-11 00:00:00.000 

très pratique pour se joindre à votre requête. ..

Questions connexes