2
votes

Comment obtenir toutes les dates entre le mois en cours et les deux derniers mois

J'essaie d'obtenir toutes les dates existant entre le mois en cours et les deux derniers mois.

Par exemple: aujourd'hui 10-01-2019 Avec un script sql, j'obtiendrai toutes les dates entre le 01/10/2018 et le 31/01/2019.

with cte as
  (
  select getdate() as   n
  union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) < month(DATEADD(month, -3, getdate())) --and month(DATEADD(month, 0, getdate()))
   union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) > month(DATEADD(month, 0, getdate()))
  )
  select * from cte

J'obtiens

message d'erreur 530, niveau 16, état 1, ligne 1 La déclaration s'est terminée. La récursivité maximale 100 a été épuisée avant la fin de l'instruction.


6 Réponses :


2
votes

Vous atteignez la limite de maxrecursion. Augmentez-le en option:

with cte as
  (
  select getdate() as   n
  union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) < month(DATEADD(month, -3, getdate())) --and month(DATEADD(month, 0, getdate()))
   union all
  select  dateadd(DAY,-1,n) from cte where month(dateadd(dd,-1,n)) > month(DATEADD(month, 0, getdate()))
  )
select * from cte
OPTION (MAXRECURSION 1000)


1 commentaires

Cela atteint toujours la limite de récursivité ... et la récursivité n'est pas la meilleure approche ici. Cela crie pour un tableau de pointage ou de calendrier.




4
votes

Cela fonctionnera en fonction de votre version de SQL Server.

with cte as
  (
     select cast(getdate() as date) as   n
     union all
     select  dateadd(DAY,-1,n) from cte where dateadd(DAY,-1,n) > (select eomonth(cast(dateadd(month,-4,getdate()) as date)))
  )
  select * 
  from cte
  order by n desc
  option (maxrecursion 200)


0 commentaires

2
votes
with cte as
  (
  select dateadd(day,-1,dateadd(month,1,dateadd(day, 1 - day(getdate()) , cast(getdate() as date)))) n
  union all
  select  dateadd(day,-1,n) from cte where n > dateadd(month,-3,dateadd(day , 1 - day(getdate()),cast(getdate() as date))) 
  )
select * from cte order by n
OPTION (MAXRECURSION 1000)

0 commentaires

5
votes

La récursivité n'est pas une bonne approche pour cela. En termes de performances, l'utilisation d'un cte récursif pour incrémenter un compteur est la même chose qu'un curseur. http://www.sqlservercentral.com/articles/T-SQL/74118/

Une bien meilleure approche est de faire cet ensemble basé. Pour cette tâche, un tableau de pointage est idéal. Voici un excellent article sur le sujet.

Je garde un tableau de pointage comme vue dans mon système. Il est ultra-rapide avec zéro lecture.

select dateadd(day, t.N - 1, dateadd(month, -3, dateadd(month, datediff(month, 0, getdate()), 0)))
from cteTally t
where t.N - 1 < datediff(day,dateadd(month, -3, dateadd(month, datediff(month, 0, getdate()), 0)), dateadd(month, datediff(month, 0, getdate()) + 1, 0))

Ensuite, pour quelque chose comme ce problème, il est super simple à utiliser. Cela produira les mêmes résultats sans boucle.

declare @endDate datetime = '2019-01-31'
    , @tmpDate datetime = '2018-10-01'

select dateadd(day, t.N - 1, @tmpDate)
from cteTally t
where t.N - 1 <= DATEDIFF(day, @tmpDate, @endDate)

--EDIT--

Si vous avez besoin que cela soit dynamique, vous pouvez utiliser un peu de date math. Cela obtiendra les données du début d'il y a 3 mois à la fin du mois en cours, quel que soit le moment où vous l'exécutez. La logique de la date peut être un peu difficile à déchiffrer si vous n'avez jamais vu ce genre de chose auparavant. Lynn Pettis a un excellent article sur ce sujet. http://www.sqlservercentral.com / blogs / lynnpettis / 2009/03/25 / certaines-routines-de-date-communes /

create View [dbo].[cteTally] as

WITH
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS 
    (
        SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )
select N from cteTally
GO


6 commentaires

La seule réponse (au moment du commentaire) qui n'utilise pas de rCTE, ou (pire encore) une boucle WHILE . C'est de loin la bonne réponse ici.


Excellente réponse avec un bon exemple de tableau de pointage. Merci d'avoir partagé ceci avec nous.


Je ne pense pas que sa plage de dates devrait être codée en dur, c'était un exemple qu'il a donné, le mois en cours et 3 mois avant cela. Cela lui fera perdre son temps à devoir changer cela le premier de chaque mois. C'est une mauvaise approche pour faire cela.


@Cato et c'était aussi un exemple. Il est trivial d'ajuster cela pour tenir compte du mois en cours et des 3. Le même concept a juste besoin de rendre dynamiques les dates de début et de fin, ce qui n'est pas si difficile. Je mettrai à jour ceci dans une seconde pour démontrer.


En fait, c'était la partie qui ne fonctionnait pas vraiment dans son code, donc ce n'était pas du tout trivial. Vous ne lui avez pas vraiment réécrit qui répondait à ses exigences.


@Cato Je n'ai pas saisi les exigences dynamiques de l'OP, mais en relisant je pense que vous avez peut-être raison. Voir ma modification car elle répond à ce besoin et n'a toujours pas besoin de boucles ou de récursivité.



2
votes

Si vous utilisez SQL 2012+

SELECT 
    cast(dateadd(dd, number, dateadd(MM, -3, dateadd(dd, -day(getdate())+1, getdate()))) as date)
FROM 
    master..spt_values m1
WHERE 
    type = 'P' 
AND dateadd(dd, number, dateadd(MM, -3, dateadd(dd, -day(getdate())+1, getdate()))) <= dateadd(MM, 1, dateadd(dd, -day(getdate()) , getdate()))

Et pour les versions antérieures de SQL:

SELECT 
    dateadd(dd, number, (dateadd(dd, 1, dateadd(MM, -4, eomonth(getdate()))))) as TheDate
FROM 
    master..spt_values m1
WHERE 
    type = 'P' 
AND dateadd(dd, number, (dateadd(dd, 1, dateadd(MM, -4, eomonth(getdate()))))  ) <= eomonth(getdate())


0 commentaires