2011-01-21 2 views
4

Merci d'avance pour toute aide sur celui-ci. Disons que j'ai une requête qui compare les données d'une année à l'autre, en commençant par une année arbitraire et sans fin (à l'avenir), pour la même période chaque année jusqu'au dernier mois terminé (qui a pour caractéristique que les données Jan. ne montre jamais avant le 1er février). Dites aussi, que l'on ne peut pas utiliser T-SQL. Existe-t-il un moyen de reformuler la requête suivante pour générer dynamiquement les dates à partir de 2008/01/01 (ou même simplement de le faire pour toutes les années) et de continuer indéfiniment sans codage en dur?Dates dynamiques dans l'instruction SQL SQL Server

select 
     case 
     when oact.fathernum like '112%' then sum(jdt1.debit) - sum(jdt1.credit) 
     end as [Accounts Receivable], 
     jdt1.refdate as [Posting Date] 
from jdt1 
    inner join oact on jdt1.account = oact.AcctCode 
where (oact.fathernum like '1%') 
      and 
      (jdt1.refdate between '2008/01/01' and dateadd(day, -1, '2008/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2009/01/01' and dateadd(day, -1, '2009/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2010/01/01' and dateadd(day, -1, '2010/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2011/01/01' and dateadd(day, -1, '2011/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2012/01/01' and dateadd(day, -1, '2012/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2013/01/01' and dateadd(day, -1, '2013/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2014/01/01' and dateadd(day, -1, '2014/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2015/01/01' and dateadd(day, -1, '2015/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2016/01/01' and dateadd(day, -1, '2016/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or jdt1.refdate between '2017/01/01' and dateadd(day, -1, '2017/' + cast(month(getdate()) as varchar(2)) + '/01')) 

group by oact.fathernum, jdt1.refdate 

A défaut, une soin d'essayer à une reformulation utilisant T-SQL dans une procédure stockée qui résout le problème? La borne supérieure de la date peut toujours être l'année en cours tant qu'elle est dynamique.

+1

Est-ce SQL Server 2005 ou supérieur? – RichardTheKiwi

+0

@cyberwiki - oui – m7d

Répondre

2

Le TSQL ci-dessous montre un méthode de construction d'une table de calendrier dynamique. La requête telle qu'illustrée change la date du pivot avec chaque année, mais vous voyez plus loin comment vous pouvez fixer la date de début du calendrier pour une année donnée.

select 
     case 
     when oact.fathernum like '112%' then sum(jdt1.debit) - sum(jdt1.credit) 
     end as [Accounts Receivable], 
     jdt1.refdate as [Posting Date] 
from jdt1 
inner join oact on jdt1.account = oact.AcctCode 

inner join (select 
    FirstDayOfYear =DATEADD(m,datediff(m,0,getdate())-MONTH(getdate())+1,0), 
    FirstDayOfMonth =DATEADD(m,datediff(m,0,getdate()),0)) D 
inner join master..spt_values v on v.type='P' 
    and v.number between 0 and 500 -- is 500 years enough? max=2047 from this table 
    on jdt1.refdate >= DATEADD(year,v.number,D.FirstDayOfYear) 
    and jdt1.refdate < DATEADD(year,v.number,D.FirstDayOfMonth) 

where (oact.fathernum like '1%') 
group by oact.fathernum, jdt1.refdate 

La sélection crée un seul enregistrement de 2 dates de pivot, comme nommé

inner join (select 
    FirstDayOfYear =DATEADD(m,datediff(m,0,getdate())-MONTH(getdate())+1,0), 
    FirstDayOfMonth =DATEADD(m,datediff(m,0,getdate()),0)) D 

Les 2 dates pivots sont le premier jour du **current year**, et le premier jour du mois en cours (également l'année en cours). Si vous avez besoin le premier jour d'une année **specific** et le premier jour du mois (mois en cours), mais dans la même année spécifique, vous pouvez utiliser la variation ci-dessous (par exemple pour 2008-Jan-01)

select 
    FirstDayOfYear =cast('20080101' as datetime), 
    FirstDayOfMonth =dateadd(m,month(getdate())-1,'20080101') 

Cette utilise les dates de pivotement et la séquence de numéros intégrée pour ajouter progressivement 1 an à chaque fois aux dates de pivot, en commençant par ajouter 0 (pour l'année en cours).

inner join master..spt_values v on v.type='P' 
    and v.number between 0 and 500 
    on jdt1.refdate >= DATEADD(year,v.number,D.FirstDayOfYear) 
    and jdt1.refdate < DATEADD(year,v.number,D.FirstDayOfMonth) 

Notez également qu'au lieu de

date between A and B 

Normalement, je préfère

date >= A and date < B+1 

qui fonctionne ou non B comprend des informations de temps. Cela n'a pas d'importance pour votre requête, mais c'est une bonne pratique pour la consistance.

+0

Impressionnant, merci! Cool pour voir comment vous gérez ce problème, et oui, 500 ans devraient suffire; – m7d

+0

Désolé je me suis trompé de pseudo, tellement habitué à voir le wiki au lieu du kiwi. – m7d

+0

@cyberkiwi - C'est une solution soignée et je peux en voir beaucoup d'utilisations, mais y a-t-il un moyen de garder l'année à laquelle elle commence à partir de toujours avancer avec chaque nouvelle année? Je veux vraiment partir de 2008. Il compare correctement la même période de chaque année, je n'ai tout simplement pas compris comment la modifier pour fixer la première année à 2008 et ensuite comparer toutes les années subséquentes à partir de là. En d'autres termes, la même période de données pour toutes les années à partir de 2008 indéfiniment doit être prédéfinie simultanément. Par exemple, du 1er janvier au 21 janvier 2008 aux côtés du 1er janvier au 21 janvier 2009, etc. Des idées? – m7d

2

Commencez avec une table de nombres pour générer datesets et rejoindre sur ce

Ce SO question le fait pour des séquences de jour

+0

Aussi: http: // stackoverflow.com/questions/1478951/tsql-générer-un-resultset-de-incrementing-dates –

+0

Merci pour le lien gbn! – m7d

1

Est-ce que somethign comme ce travail ?:

YEAR(jdt1.refdate) between 2008 and 2017 
    and 
MONTH(jdt1.refdate) < MONTH(getdate()) 
+0

c'est terrible pour l'utilisation de l'index – RichardTheKiwi

+0

Est-ce? Pourquoi? Et plus important, devrait-il être terrible? –

+0

car pour comprendre la première partie, il faudrait exécuter YEAR() à travers tous les enregistrements pour retourner la valeur int. Une plage de dates correctement construite à tester contre 'jdt1.refdate' n'a besoin de collecter que 1/12 des données. – RichardTheKiwi

1

Si vous utilisez SQL Server 2005+, vous pouvez simplement construire votre calendrier à la volée:

With MaxDate As 
    (
    Select Max(refdate) As [Date] 
    From jdt1 
    ) 
    , Calendar As 
    (
    Select Cast(Cast(Year(GetDate())As char(4)) + '0101' As datetime) As [StartDay] 
     , DateAdd(d, -1, Cast(Cast(Year(GetDate()) + 1 As char(4)) + '0101' As datetime))As [EndDay] 
    Union All 
    Select DateAdd(yyyy, 1, [StartDay]) 
     , DateAdd(yyyy, 1, [EndDay]) 
    From Calendar 
     Join MaxDate 
      On Year(DateAdd(yyyy, 1, [EndDay])) <= Year(MaxDate.[Date]) 
    ) 
Select ... 
From Calendar As C 
    Join jdt1 
     On jdt1.refdate Between C.StartDay And C.EndDay 
    Join oact 
     On oact.AcctCode = jdt1.account 
Where oct.fathernum Like '%1' 
Group By oact.fathernum, jdt1.refdate  
Option (MaxRecursion 0); 

Dans cette solution, j'ai commencé avec l'année d'aujourd'hui et élargi vers l'année du dernier refdate.

+0

Shoots, SO ne me laissera pas attribuer deux réponses. Eh bien, c'est aussi une bonne solution. Merci! – m7d