web-dev-qa-db-fra.com

Groupe SQL Server par mois

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?

45
Echilon
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
98
Dave Downs

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]
19
Martyn Davis

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.

5
bounav
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.

3
MatBailie

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 
3
marc_s

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] ) )
1
Dai

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
0
DRapp