J'ai une table qui a ce schéma
ItemID UserID Year IsPaid PaymentDate Amount
1 1 2009 0 2009-11-01 300
2 1 2009 0 2009-12-01 342
3 1 2010 0 2010-01-01 243
4 1 2010 0 2010-02-01 2543
5 1 2010 0 2010-03-01 475
J'essaie de faire fonctionner une requête qui affiche les totaux pour chaque mois. Jusqu'à présent, j'ai essayé DateDiff et des sélections imbriquées, mais aucune ne m'a donné ce que je voulais. C'est le plus proche que j'ai, je pense:
DECLARE @start [datetime] = 2010/4/1;
SELECT ItemID, IsPaid,
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 And DateDiff(m, PaymentDate, @start) = 0 AND UserID = 100) AS "Apr",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =1 AND UserID = 100) AS "May",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =2 AND UserID = 100) AS "Jun",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =3 AND UserID = 100) AS "Jul",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =4 AND UserID = 100) AS "Aug",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =5 AND UserID = 100) AS "Sep",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =6 AND UserID = 100) AS "Oct",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =7 AND UserID = 100) AS "Nov",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =8 AND UserID = 100) AS "Dec",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =9 AND UserID = 100) AS "Jan",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =10 AND UserID = 100) AS "Feb",
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =11 AND UserID = 100) AS "Mar"
FROM LIVE L INNER JOIN Payments I ON I.LiveID = L.RECORD_KEY
WHERE UserID = 16178
Mais je n’obtiens que des valeurs nulles alors que je devrais recevoir des valeurs. Est-ce que je manque quelque chose?
SELECT CONVERT(NVARCHAR(10), PaymentDate, 120) [Month], SUM(Amount) [TotalAmount]
FROM Payments
GROUP BY CONVERT(NVARCHAR(10), PaymentDate, 120)
ORDER BY [Month]
Vous pouvez aussi essayer:
SELECT DATEPART(Year, PaymentDate) Year, DATEPART(Month, PaymentDate) Month, SUM(Amount) [TotalAmount]
FROM Payments
GROUP BY DATEPART(Year, PaymentDate), DATEPART(Month, PaymentDate)
ORDER BY Year, Month
Limitez la dimension de NVARCHAR à 7, fournie à CONVERT pour afficher uniquement "AAAA-MM".
SELECT CONVERT(NVARCHAR(7),PaymentDate,120) [Month], SUM(Amount) [TotalAmount]
FROM Payments
GROUP BY CONVERT(NVARCHAR(7),PaymentDate,120)
ORDER BY [Month]
Je préfère combiner les fonctions DATEADD
et DATEDIFF
comme ceci:
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Created),0)
Ensemble, ces deux fonctions mettent à zéro le composant de date plus petit que le spécifié datepart (ie MONTH
dans cet exemple).
Vous pouvez remplacer le bit datepart par YEAR
, WEEK
, DAY
, etc ..., ce qui est très pratique.
Votre requête SQL d'origine ressemblerait alors à quelque chose comme ceci (je ne peux pas le tester car je n'ai pas votre jeu de données, mais cela devrait vous mettre sur la bonne voie).
DECLARE @start [datetime] = '2010-04-01';
SELECT
ItemID,
UserID,
DATEADD(MONTH, DATEDIFF(MONTH, 0, Created),0) [Month],
IsPaid,
SUM(Amount)
FROM LIVE L
INNER JOIN Payments I ON I.LiveID = L.RECORD_KEY
WHERE UserID = 16178
AND PaymentDate > @start
Une dernière chose: la colonne Month
est saisie sous la forme d'un DateTime
, ce qui constitue également un avantage appréciable si vous devez traiter davantage ces données ou mapper cet objet .NET, par exemple.
DECLARE @start [datetime] = 2010/4/1;
Devrait être...
DECLARE @start [datetime] = '2010-04-01';
Celui que vous avez divise 2010 par 4, puis par 1, puis convertit en date. Quel est le 57.5ème jour du 1900-01-01.
Essayez SELECT @start
après votre initialisation pour vérifier si cela est correct.
Si vous devez le faire fréquemment, j'ajouterais probablement une colonne calculée PaymentMonth
à la table:
ALTER TABLE dbo.Payments ADD PaymentMonth AS MONTH(PaymentDate) PERSISTED
Elle est persistante et stockée dans la table - il n'y a donc vraiment pas de frais de performance à interroger. Il s'agit d'une valeur INT de 4 octets. L'espace disque est donc minime également.
Une fois que vous avez cela, vous pouvez simplifier votre requête pour qu’elle ressemble à:
SELECT ItemID, IsPaid,
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 And PaymentMonth = 1 AND UserID = 100) AS 'Jan',
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 And PaymentMonth = 2 AND UserID = 100) AS 'Feb',
.... and so on .....
FROM LIVE L
INNER JOIN Payments I ON I.LiveID = L.RECORD_KEY
WHERE UserID = 16178
Une autre approche, qui n’implique pas l’ajout de colonnes au résultat, consiste simplement à supprimer le composant day
de la date, de sorte que 2016-07-13
et 2016-07-16
_ seraient tous deux 2016-07-01
- les rendant ainsi égaux par mois.
Si vous avez une valeur date
(et non datetime
), vous pouvez la mettre à zéro directement:
SELECT
DATEADD( day, 1 - DATEPART( day, [Date] ), [Date] ),
COUNT(*)
FROM
[Table]
GROUP BY
DATEADD( day, 1 - DATEPART( day, [Date] ), [Date] )
Si vous avez les valeurs datetime
, vous devrez utiliser CONVERT
pour supprimer la portion d'heure:
SELECT
DATEADD( day, 1 - DATEPART( day, [Date] ), CONVERT( date, [Date] ) ),
COUNT(*)
FROM
[Table]
GROUP BY
DATEADD( day, 1 - DATEPART( day, [Date] ), CONVERT( date, [Date] ) )
Maintenant, votre requête porte explicitement sur les paiements pour l'année = 2010, cependant, je pense que vous vouliez que votre janvier/février/mars représente réellement 2009. Si tel est le cas, vous devrez ajuster cela un peu pour cette affaire. Ne continuez pas à interroger les valeurs de somme pour chaque colonne, mais simplement la condition de la différence de date en mois. Mettez le reste dans la clause WHERE.
SELECT
SUM( case when DateDiff(m, PaymentDate, @start) = 0
then Amount else 0 end ) AS "Apr",
SUM( case when DateDiff(m, PaymentDate, @start) = 1
then Amount else 0 end ) AS "May",
SUM( case when DateDiff(m, PaymentDate, @start) = 2
then Amount else 0 end ) AS "June",
SUM( case when DateDiff(m, PaymentDate, @start) = 3
then Amount else 0 end ) AS "July",
SUM( case when DateDiff(m, PaymentDate, @start) = 4
then Amount else 0 end ) AS "Aug",
SUM( case when DateDiff(m, PaymentDate, @start) = 5
then Amount else 0 end ) AS "Sep",
SUM( case when DateDiff(m, PaymentDate, @start) = 6
then Amount else 0 end ) AS "Oct",
SUM( case when DateDiff(m, PaymentDate, @start) = 7
then Amount else 0 end ) AS "Nov",
SUM( case when DateDiff(m, PaymentDate, @start) = 8
then Amount else 0 end ) AS "Dec",
SUM( case when DateDiff(m, PaymentDate, @start) = 9
then Amount else 0 end ) AS "Jan",
SUM( case when DateDiff(m, PaymentDate, @start) = 10
then Amount else 0 end ) AS "Feb",
SUM( case when DateDiff(m, PaymentDate, @start) = 11
then Amount else 0 end ) AS "Mar"
FROM
Payments I
JOIN Live L
on I.LiveID = L.Record_Key
WHERE
Year = 2010
AND UserID = 100