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 :
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)
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.
Vous pouvez utiliser une table temporaire à cet effet. Avec une boucle, ajoutez simplement les dates dont vous avez besoin à la table temporaire. Vérifiez la requête ci-dessous:
create table #temp (thedate date) declare @i int = 1 declare @endDate datetime = '2019-01-31' declare @tmpDate datetime = '2018-10-01' while @tmpDate<=@endDate begin insert into #temp values (@tmpDate) set @tmpDate = dateadd(day,1,@tmpDate) end select * from #temp
EDIT: Basé sur le commentaire de OP, nouvelle requête:
create table #temp (thedate date) declare @i int = 1 declare @tmpDate datetime = dateadd(month,-2,getdate()) while @tmpDate<=getdate() begin insert into #temp values (@tmpDate) set @tmpDate = dateadd(day,1,@tmpDate) end select * from #temp
Le bouclage ici n'est pas l'approche la plus efficace.
@SeanLange Vous avez raison, il n'a pas les meilleures performances mais pour deux mois de processus, c'est assez rapide et utile.
Un tableau de pointage serait beaucoup plus simple à utiliser. :)
J'obtiens des valeurs du 2018-11-10 au 2019-01-10 mais l'exigence est d'obtenir les dates du 2018-10-01 (début du mois) et 2019-01-31 (fin du mois en cours).
@NewbieSQL puis définissez votre date de fin sur 2019-01-31 au lieu d'utiliser getdate () et définissez la première valeur de tmpdate sur 2018-10-01.
Essayez cet article. sqlservercentral.com/articles/T-SQL/62867
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)
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)
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. Ensuite, pour quelque chose comme ce problème, il est super simple à utiliser. Cela produira les mêmes résultats sans boucle. --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 / 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))
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)
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
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é.
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())
Double possible de Obtenir toutes les dates entre deux dates dans SQL Server a>