Группа SQL Server по месяцам - PullRequest
       7

Группа SQL Server по месяцам

45 голосов
/ 01 декабря 2011

У меня есть таблица с этой схемой

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

Я пытаюсь получить рабочий запрос, который показывает итоги за каждый месяц.До сих пор я пробовал DateDiff и вложенные выборки, но ни один не дает мне то, что я хочу.Думаю, это самое близкое, что у меня есть:

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 

Но я просто получаю нулевые значения, когда мне нужно получить значения.Я что-то упустил?

Ответы [ 7 ]

97 голосов
/ 01 декабря 2011
SELECT CONVERT(NVARCHAR(10), PaymentDate, 120) [Month], SUM(Amount) [TotalAmount]
FROM Payments
GROUP BY CONVERT(NVARCHAR(10), PaymentDate, 120)
ORDER BY [Month]

Вы также можете попробовать:

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
19 голосов
/ 21 июня 2012

Ограничьте размер NVARCHAR до 7, поставляемого в CONVERT, чтобы показывать только «ГГГГ-ММ»

SELECT CONVERT(NVARCHAR(7),PaymentDate,120) [Month], SUM(Amount) [TotalAmount]
FROM Payments
GROUP BY CONVERT(NVARCHAR(7),PaymentDate,120)
ORDER BY [Month]
5 голосов
/ 25 мая 2017

Я предпочитаю комбинировать функции DATEADD и DATEDIFF следующим образом:

GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Created),0)

Вместе эти две функции обнуляют компонент даты меньше , чем указанная часть даты (т.е. MONTH в этом примере).

Вы можете изменить бит datepart на YEAR, WEEK, DAY и т. Д., Что очень удобно.

Ваш исходный SQL-запрос будет выглядеть примерно так (я не могу проверить его, поскольку у меня нет набора данных, но он должен поставить вас на правильный путь).

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

Еще одна вещь: столбец Month имеет тип DateTime, что также является хорошим преимуществом, если вам необходимо дополнительно обработать эти данные или сопоставить их, например, объект .NET.

3 голосов
/ 01 декабря 2011

Если вам нужно делать это часто, я, вероятно, добавлю вычисляемый столбец PaymentMonth в таблицу:

ALTER TABLE dbo.Payments ADD PaymentMonth AS MONTH(PaymentDate) PERSISTED

Он сохраняется и хранится в таблице - так что на самом деле нет никаких проблем с производительностью, запрашивающих его,Это 4-байтовое значение INT - таким образом, пространство на диске также минимально.

Как только вы это сделаете, вы можете упростить свой запрос, чтобы он выглядел примерно так:

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 голосов
/ 01 декабря 2011
DECLARE @start [datetime] = 2010/4/1;

Должно быть ...

DECLARE @start [datetime] = '2010-04-01';

Тот, который у вас есть, делит 2010 на 4, затем на 1, а затем конвертирует в дату. Который является 57,5-м днем ​​с 1900-01-01.

Попробуйте SELECT @start после инициализации, чтобы проверить, правильно ли это.

1 голос
/ 23 июля 2016

Другой подход, который не включает добавление столбцов к результату, заключается в простом обнулении компонента day даты, поэтому 2016-07-13 и 2016-07-16 оба будут 2016-07-01 - таким образом, делая их равны по месяцам.

Если у вас есть значение date (не datetime), вы можете обнулить его напрямую:

SELECT
    DATEADD( day, 1 - DATEPART( day, [Date] ), [Date] ),
    COUNT(*)
FROM
    [Table]
GROUP BY
    DATEADD( day, 1 - DATEPART( day, [Date] ), [Date] )

Если у вас есть значения datetime, вам нужно будет использовать CONVERT для удаления части времени:

SELECT
    DATEADD( day, 1 - DATEPART( day, [Date] ),  CONVERT( date, [Date] ) ),
    COUNT(*)
FROM
    [Table]
GROUP BY
    DATEADD( day, 1 - DATEPART( day, [Date] ),  CONVERT( date, [Date] ) )
0 голосов
/ 01 декабря 2011

Теперь ваш запрос явно рассматривает только платежи за год = 2010, однако я думаю, что вы хотели, чтобы ваш январь / февраль / март фактически представляли 2009 год. Если это так, вам нужно будет немного скорректировать это для этого случая,Не продолжайте запрашивать значения сумм для каждого столбца, только условие разницы дат в месяцах.Остальное поместите в предложение 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
...