SQL Server-Gruppierung nach Monat
-
27-10-2019 - |
Frage
Ich habe eine Tabelle mit diesem Schema
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
Ich versuche, eine Abfrage zum Laufen zu bringen, die die Gesamtsummen für jeden Monat anzeigt.Bisher habe ich DateDiff und verschachtelte Auswahlen ausprobiert, aber keines von beiden liefert mir das, was ich will.Das kommt meiner Meinung nach am nächsten:
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
Aber ich bekomme nur Nullen, wenn ich Werte bekommen sollte.Vermisse ich etwas?
Lösung
SELECT CONVERT(NVARCHAR(10), PaymentDate, 120) [Month], SUM(Amount) [TotalAmount]
FROM Payments
GROUP BY CONVERT(NVARCHAR(10), PaymentDate, 120)
ORDER BY [Month]
Sie können es auch versuchen:
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
Andere Tipps
Beschränken Sie die Dimension des Nvarchar auf 7, um nur "yyyy-mm" zu konvertieren, um nur "yyyy-mm" zu zeigen.
SELECT CONVERT(NVARCHAR(7),PaymentDate,120) [Month], SUM(Amount) [TotalAmount]
FROM Payments
GROUP BY CONVERT(NVARCHAR(7),PaymentDate,120)
ORDER BY [Month]
Ich kombiniere lieber DATEADD
Und DATEDIFF
funktioniert so:
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Created),0)
Zusammen setzen diese beiden Funktionen die Datumskomponente auf Null kleiner als die angegebene Datumsteil (d. h. MONTH
in diesem Beispiel).
Sie können das ändern Datumsteil bisschen zu YEAR
, WEEK
, DAY
, usw...Das ist super praktisch.
Ihre ursprüngliche SQL-Abfrage würde dann etwa so aussehen (ich kann sie nicht testen, da ich Ihren Datensatz nicht habe, aber sie sollte Sie auf den richtigen Weg bringen).
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
Eine Sache noch:Die Month
Die Spalte wird als a eingegeben DateTime
Dies ist auch ein schöner Vorteil, wenn Sie diese Daten weiter verarbeiten oder sie beispielsweise einem .NET-Objekt zuordnen müssen.
DECLARE @start [datetime] = 2010/4/1;
Sollte sein...
DECLARE @start [datetime] = '2010-04-01';
Die, die Sie haben, dividiert 2010 durch 4, dann um 1 und konvertiert dann auf ein Datum. Das ist der 57.5. Tag von 1900-01-01.
Versuchen SELECT @start
Nach Ihrer Initialisierung, um zu überprüfen, ob dies korrekt ist.
Wenn Sie dies häufig tun müssen, würde ich wahrscheinlich eine berechnete Spalte hinzufügen PaymentMonth
zum Tisch:
ALTER TABLE dbo.Payments ADD PaymentMonth AS MONTH(PaymentDate) PERSISTED
Es ist in dem Tisch bestehen und gespeichert - es gibt also wirklich keine Leistung, die es abfragt. Es ist ein 4 -Byte -Int -Wert - also ist auch der Space Overhead minimal.
Sobald Sie das haben, können Sie Ihre Abfrage so vereinfachen, dass Sie etwas in der Sicht haben:
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
Ein anderer Ansatz, der nicht das Hinzufügen von Spalten zum Ergebnis besteht day
Komponente des Datums so 2016-07-13
und 2016-07-16
wäre beides 2016-07-01
- damit sie nach Monat gleich machen.
Wenn Sie eine haben date
(kein datetime
) Wert, dann können Sie es direkt null:
SELECT
DATEADD( day, 1 - DATEPART( day, [Date] ), [Date] ),
COUNT(*)
FROM
[Table]
GROUP BY
DATEADD( day, 1 - DATEPART( day, [Date] ), [Date] )
Wenn Sie haben datetime
Werte, Sie müssen verwenden CONVERT
Um den Tagesabschnitt zu entfernen:
SELECT
DATEADD( day, 1 - DATEPART( day, [Date] ), CONVERT( date, [Date] ) ),
COUNT(*)
FROM
[Table]
GROUP BY
DATEADD( day, 1 - DATEPART( day, [Date] ), CONVERT( date, [Date] ) )
Jetzt prüft Ihre Abfrage ausdrücklich nur Zahlungen für das Jahr = 2010, aber ich denke, Sie wollten, dass Ihr Jan/Februar/Mar tatsächlich 2009 repräsentiert. Wenn ja, müssen Sie dies für diesen Fall ein wenig anpassen. Behalten Sie die Summenwerte für jede Spalte nicht weiter, sondern nur die Bedingung der Datumsdifferenz in Monaten. Legen Sie den Rest in die Where -Klausel.
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