J'ai besoin de calculer le nombre deCOMPLETmois en SQL, c.-à-d.
J'ai essayé d'utiliser DATEDIFF, c'est-à-dire.
SELECT DATEDIFF(MONTH, '2009-04-16', '2009-05-15')
mais au lieu de me donner des mois entiers entre les deux dates, cela me donne la différence de la partie mois, c.-à-d.
1
quelqu'un sait comment calculer le nombre de mois complets dans SQL Server?
Le message original contenait des bugs ... je l'ai donc réécrit et emballé sous forme de fichier UDF.
CREATE FUNCTION FullMonthsSeparation
(
@DateA DATETIME,
@DateB DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @Result INT
DECLARE @DateX DATETIME
DECLARE @DateY DATETIME
IF(@DateA < @DateB)
BEGIN
SET @DateX = @DateA
SET @DateY = @DateB
END
ELSE
BEGIN
SET @DateX = @DateB
SET @DateY = @DateA
END
SET @Result = (
SELECT
CASE
WHEN DATEPART(DAY, @DateX) > DATEPART(DAY, @DateY)
THEN DATEDIFF(MONTH, @DateX, @DateY) - 1
ELSE DATEDIFF(MONTH, @DateX, @DateY)
END
)
RETURN @Result
END
GO
SELECT dbo.FullMonthsSeparation('2009-04-16', '2009-05-15') as MonthSep -- =0
SELECT dbo.FullMonthsSeparation('2009-04-16', '2009-05-16') as MonthSep -- =1
SELECT dbo.FullMonthsSeparation('2009-04-16', '2009-06-16') as MonthSep -- =2
select case when DATEPART(D,End_dATE) >=DATEPART(D,sTAR_dATE)
THEN ( case when DATEPART(M,End_dATE) = DATEPART(M,sTAR_dATE) AND DATEPART(YYYY,End_dATE) = DATEPART(YYYY,sTAR_dATE)
THEN 0 ELSE DATEDIFF(M,sTAR_dATE,End_dATE)END )
ELSE DATEDIFF(M,sTAR_dATE,End_dATE)-1 END
La fonction dateadd peut être utilisée pour aller au début du mois. Si endDate a une partie de jour inférieure à startDate, elle sera reportée au mois précédent, ainsi datiff donnera le nombre correct de mois.
DATEDIFF(MONTH, DATEADD(DAY,-DAY(startDate)+1,startDate),DATEADD(DAY,-DAY(startDate)+1,endDate))
Ceci est pour Oracle seulement et pas pour SQL-Server:
months_between(to_date ('2009/05/15', 'yyyy/mm/dd'),
to_date ('2009/04/16', 'yyyy/mm/dd'))
Et pour un mois complet:
round(months_between(to_date ('2009/05/15', 'yyyy/mm/dd'),
to_date ('2009/04/16', 'yyyy/mm/dd')))
Peut être utilisé dans Oracle 8i et supérieur.
Quelle est votre définition d'un mois? Techniquement, un mois peut être de 28,29,30 ou 31 jours, selon le mois et les années bissextiles.
Il semble que vous envisagiez un mois comme étant 30 jours puisque, dans votre exemple, vous avez ignoré que le mois de mai avait 31 jours, alors pourquoi ne pas simplement procéder comme suit?
SELECT DATEDIFF(DAY, '2009-04-16', '2009-05-15')/30
, DATEDIFF(DAY, '2009-04-16', '2009-05-16')/30
, DATEDIFF(DAY, '2009-04-16', '2009-06-16')/30
Cette réponse suit le format T-SQL. Je conceptualise ce problème comme une distance linéaire temporelle entre deux points de date au format datetime, appelez-les Time1 et Time2; Time1 doit être aligné sur la valeur "plus ancien dans le temps" (par exemple, une date de naissance ou une date de création ou un début de voyage) et Time2 doit être aligné sur la valeur "plus récent dans le temps" (par exemple, une date instantanée). ou une date d'achèvement du widget ou une date atteinte par un point de contrôle de trajet).
DECLARE @Time1 DATETIME
SET @Time1 = '12/14/2015'
DECLARE @Time2 DATETIME
SET @Time2 = '12/15/2016'
La solution s'appuie sur des mesures, des conversions et des calculs simples des intersections en série de multiples cycles de différentes longueurs. ici: Siècle, Décennie, Année, Mois, Jour (Merci au Calendrier Maya pour le concept!). Un petit mot de remerciement: je remercie les autres contributeurs de Stack Overflow de m'avoir montré certaines des fonctions composant ce processus que j'ai cousues ensemble. J'ai évalué positivement ces derniers dans mon temps sur ce forum.
Commencez par construire un horizon représentant l’ensemble linéaire des intersections des cycles Siècle, Décennie, Année, Mois, incrémental par mois. Utilisez la fonction cartésienne de jointure croisée pour cela. (Pensez à cela comme à la création du tissu à partir duquel nous allons couper une longueur entre deux points "aaaa-mm" afin de mesurer la distance):
SELECT
Linear_YearMonths = (centuries.century + decades.decade + years.[year] + months.[Month]),
1 AS value
INTO #linear_months
FROM
(SELECT '18' [century] UNION ALL
SELECT '19' UNION ALL
SELECT '20') centuries
CROSS JOIN
(SELECT '0' [decade] 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') decades
CROSS JOIN
(SELECT '1' [year] 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' UNION ALL
SELECT '0') years
CROSS JOIN
(SELECT '-01' [month] UNION ALL
SELECT '-02' UNION ALL
SELECT '-03' UNION ALL
SELECT '-04' UNION ALL
SELECT '-05' UNION ALL
SELECT '-06' UNION ALL
SELECT '-07' UNION ALL
SELECT '-08' UNION ALL
SELECT '-09' UNION ALL
SELECT '-10' UNION ALL
SELECT '-11' UNION ALL
SELECT '-12') [months]
ORDER BY 1
Ensuite, convertissez vos points de date Time1 et Time2 au format "aaaa-mm" (considérez-les comme les points de coupe des coordonnées sur tout le tissu). Conservez également les versions originales datetime des points:
SELECT
Time1 = @Time1,
[YYYY-MM of Time1] = CASE
WHEN LEFT(MONTH(@Time1),1) <> '1' OR MONTH(@Time1) = '1'
THEN (CAST(YEAR(@Time1) AS VARCHAR) + '-' + '0' + CAST(MONTH(@Time1) AS VARCHAR))
ELSE (CAST(YEAR(@Time1) AS VARCHAR) + '-' + CAST(MONTH(@Time1) AS VARCHAR))
END,
Time2 = @Time2,
[YYYY-MM of Time2] = CASE
WHEN LEFT(MONTH(@Time2),1) <> '1' OR MONTH(@Time2) = '1'
THEN (CAST(YEAR(@Time2) AS VARCHAR) + '-' + '0' + CAST(MONTH(@Time2) AS VARCHAR))
ELSE (CAST(YEAR(@Time2) AS VARCHAR) + '-' + CAST(MONTH(@Time2) AS VARCHAR))
END
INTO #datepoints
Ensuite, sélectionnez la distance ordinale des unités "aaaa-mm", moins une pour la convertir en distance cardinale (c.-à-d. Coupez un morceau de tissu de tout le tissu aux points de coupe identifiés et obtenez sa mesure brute):
SELECT
d.*,
Months_Between = (SELECT (SUM(l.value) - 1) FROM #linear_months l
WHERE l.[Linear_YearMonths] BETWEEN d.[YYYY-MM of Time1] AND d.[YYYY-MM of Time2])
FROM #datepoints d
Sortie brute: J'appelle cela une «distance brute», car la composante mois de la distance cardinale «aaaa-mm» peut en être une de trop; Les composantes du cycle journalier du mois doivent être comparées pour déterminer si cette dernière valeur doit compter. Dans cet exemple en particulier, la distance de sortie brute est «12». Mais cette erreur, car 12/14 est antérieure à 12/15, 11 mois complets sont donc écoulés - il ne reste qu'un jour avant le 12ème mois. Nous devons donc intégrer le cycle de jours intra-mensuel pour obtenir une réponse finale. Insérez une comparaison de position 'mois, jour' entre le pour déterminer si le dernier mois de la date compte de façon nominale ou non:
SELECT
d.*,
Months_Between = (SELECT (SUM(l.value) - 1) FROM AZ_VBP.[MY].[edg_Linear_YearMonths] l
WHERE l.[Linear_YearMonths] BETWEEN d.[YYYY-MM of Time1] AND d.[YYYY-MM of Time2])
+ (CASE WHEN DAY(Time1) < DAY(Time2)
THEN -1
ELSE 0
END)
FROM #datepoints d
Résultat final: .__ La réponse correcte de «11» est maintenant notre résultat. Et alors, j'espère que cela aide. Merci!
Il n'est pas nécessaire de créer la fonction uniquement la partie @result. Par exemple:
Select Name,
(SELECT CASE WHEN
DATEPART(DAY, '2016-08-28') > DATEPART(DAY, '2016-09-29')
THEN DATEDIFF(MONTH, '2016-08-28', '2016-09-29') - 1
ELSE DATEDIFF(MONTH, '2016-08-28', '2016-09-29') END) as NumberOfMonths
FROM
tableExample;
declare @StartDate date = '2019-01-31' declare @EndDate date = '2019-02-28'
SÉLECTIONNER
DATEDIFF (MOIS, @StartDate, @EndDate) +
(
cas
quand format (@ StartDate, 'aaaa-MM')! = format (@ EndDate, 'aaaa-MM') ET DATEPART (JOUR, @ StartDate)> DATEPART (JOUR, @ EndDate) ET DATEPART (JOUR, @ EndDate) = DATEPART (JOUR, EOMONTH (@EndDate)) puis 0
quand format (@ StartDate, 'aaaa-MM')! = format (@ EndDate, 'aaaa-MM') ET DATEPART (DAY, @ StartDate)> DATEPART (DAY, @ EndDate) puis -1
sinon 0
fin
)
comme NumberOfMonths
Essayer:
trunc(Months_Between(date2, date1))
WITH
-- Count how many months must be added to @StartDate to exceed @DueDate
MONTHS_SINCE(n, [Month_hence], [IsFull], [RemainingDays] ) AS (
SELECT
1 as n,
DATEADD(Day, -1, DATEADD(Month, 1, @StartDate)) AS Month_hence
,CASE WHEN (DATEADD(Day, -1, DATEADD(Month, 1, @StartDate)) <= @LastDueDate)
THEN 1
ELSE 0
END AS [IsFull]
,DATEDIFF(day, @StartDate, @LastDueDate) as [RemainingDays]
UNION ALL
SELECT
n+1,
--DateAdd(Month, 1, Month_hence) as Month_hence -- No, causes propagation of short month discounted days
DATEADD(Day, -1, DATEADD(Month, n+1, @StartDate)) as Month_hence
,CASE WHEN (DATEADD(Day, -1, DATEADD(Month, n+1, @StartDate)) <= @LastDueDate)
THEN 1
ELSE 0
END AS [IsFull]
,DATEDIFF(day, DATEADD(Day, -1, DATEADD(Month, n, @StartDate)), @LastDueDate)
FROM MONTHS_SINCE
WHERE Month_hence<( @LastDueDate --WHERE Period= 1
)
), --SELECT * FROM MONTHS_SINCE
MONTH_TALLY (full_months_over_all_terms, months_over_all_terms, days_in_incomplete_month ) AS (
SELECT
COALESCE((SELECT MAX(n) FROM MONTHS_SINCE WHERE isFull = 1),1) as full_months_over_all_terms,
(SELECT MAX(n) FROM MONTHS_SINCE ) as months_over_all_terms,
COALESCE((SELECT [RemainingDays] FROM MONTHS_SINCE WHERE isFull = 0),0) as days_in_incomplete_month
) SELECT * FROM MONTH_TALLY;
select CAST(DATEDIFF(MONTH, StartDate, EndDate) AS float) -
(DATEPART(dd,StartDate) - 1.0) / DATEDIFF(DAY, StartDate, DATEADD(MONTH, 1, StartDate)) +
(DATEPART(dd,EndDate)*1.0 ) / DATEDIFF(DAY, EndDate, DATEADD(MONTH, 1, EndDate))
SELECT 12 * (YEAR(end_date) - YEAR(start_date)) +
((MONTH(end_date) - MONTH(start_date))) +
SIGN(DAY(end_date) / DAY(start_date));
Cela fonctionne bien pour moi sur SQL SERVER 2000.
Je sais que c’est un vieux message, mais j’ai créé cette solution intéressante qui, à mon avis, est facile à mettre en œuvre à l’aide d’une déclaration CASE.
Estimez la différence à l'aide de DATEDIFF, puis testez les mois avant et après l'utilisation de DATEADD pour trouver la meilleure date. Cela suppose que le 31 janvier au 28 février soit 1 mois (parce que c'est le cas).
DECLARE @First date = '2015-08-31'
DECLARE @Last date = '2016-02-28'
SELECT
@First as [First],
@Last as [Last],
DateDiff(Month, @First, @Last) as [DateDiff Thinks],
CASE
WHEN DATEADD(Month, DATEDIFF(Month, @First, @Last) +1, @First) <= @Last Then DATEDIFF(Month, @First, @Last) +1
WHEN DATEADD(Month, DATEDIFF(Month, @First, @Last) , @First) <= @Last Then DATEDIFF(Month, @First, @Last)
WHEN DATEADD(Month, DATEDIFF(Month, @First, @Last) -1, @First) <= @Last Then DATEDIFF(Month, @First, @Last) -1
END as [Actual Months Apart]