2011-12-01 25 views
38

Tengo una tabla que tiene este esquemaGrupo de SQL Server Por Mes

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 

que estoy tratando de conseguir un trabajo de consulta que muestra los totales para cada mes. Hasta ahora he probado DateDiff y selecciones anidadas, pero ninguno me da lo que quiero. Este es el más cercano que tengo que pienso:

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 

Pero apenas consigo nulos cuando debería estar recibiendo valores. ¿Me estoy perdiendo de algo?

+0

¿Está tratando de pivotar la tabla para mostrar columnas por año/mes con una suma de pagos por ID de usuario debajo de ella? – William

+0

¿Por qué su UserID = 16178 en la cláusula where diferente de UserID = 100 en su subconsulta where clause? También las últimas 3 subconsultas para enero, febrero y marzo, ¿son sus diferencias de mes de abril realmente 9, 10 y 11 respectivamente? –

+0

Posible duplicado de [Cómo agrupar por mes desde el campo Fecha usando sql] (http://stackoverflow.com/questions/14565788/how-to-group-by-month-from-date-field-using-sql) – kurast

Respuesta

84
SELECT CONVERT(NVARCHAR(10), PaymentDate, 120) [Month], SUM(Amount) [TotalAmount] 
FROM Payments 
GROUP BY CONVERT(NVARCHAR(10), PaymentDate, 120) 
ORDER BY [Month] 

También puede probar:

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 
+5

DatePart es lo que necesitaba. Gracias. – Echilon

+0

Simplemente use comas en MySql como este 'grupo por año (fecha), mes (fecha)' – simo

+0

Preferencia hacia la segunda respuesta para legibilidad. –

3
DECLARE @start [datetime] = 2010/4/1; 

tiene que ser ...

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

la que tiene está dividiendo 2010 por 4, luego por 1, entonces la conversión a una fecha. Que es el día 57.5 desde 1900-01-01.

Pruebe SELECT @start después de su inicialización para comprobar si esto es correcto.

3

Si necesita hacer esto con frecuencia, probablemente agregar una columna calculada PaymentMonth a la mesa:

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

Se persistió y se almacena en la tabla - por lo que en realidad no hay sobrecarga de rendimiento consulta de ella. Es un valor INT de 4 bytes, por lo que la sobrecarga de espacio también es mínima.

Una vez que tienes eso, se podría simplificar su consulta para ser algo en la línea de:

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

¿Hay una sobrecarga de rendimiento? Incluso en un cuadro de baja especificación, SQL Server puede hacer varios millones 'CONVERTIR (NVARCHAR (7), PaymentDate, 120)' por segundo. No estoy seguro de que el uso de una columna calculada proporcione algún beneficio (a menos que lo indexe tal vez) – NickG

0

Ahora su consulta está mirando de forma explícita a sólo pagos por año = 2010, sin embargo, yo creo que hayas querido tenga su Jan/Feb/Mar representando a 2009. Si es así, tendrá que ajustar esto un poco para ese caso. No siga consultando los valores de suma para cada columna, solo la condición de la diferencia de fecha en meses. Pon el resto en la cláusula 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 
11

Restringir la dimensión de la NVARCHAR a 7, suministrado para convertir para mostrar solamente "AAAA-MM"

SELECT CONVERT(NVARCHAR(7),PaymentDate,120) [Month], SUM(Amount) [TotalAmount] 
FROM Payments 
GROUP BY CONVERT(NVARCHAR(7),PaymentDate,120) 
ORDER BY [Month] 
1

Otro enfoque, que no implica la adición de columnas para el resultado, es simplemente ponga a cero el componente day de la fecha, por lo que 2016-07-13 y 2016-07-16 serían ambos 2016-07-01, lo que los hace iguales por mes.

Si usted tiene un (no un datetime) date valor, entonces usted puede concentrarse directamente:

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

Si tiene datetime valores, se tendrá que utilizar CONVERT para eliminar el tiempo-de- parte correspondiente al día:

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

yo prefiero combinar DATEADD y DATEDIFF funciones de la siguiente manera:

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

En conjunto, estas dos funciones cero el componente de fecha menor que el especificado datepart (es decir, MONTH en este ejemplo).

Puede cambiar el bit datepart-YEAR, WEEK, DAY, etc ... que es súper práctico.

Su consulta SQL original se vería así (no puedo probarlo ya que no tengo su conjunto de datos, pero debería ponerlo en el camino correcto).

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 

Una cosa más: la columna de la Month se escribe como DateTime que también es una buena ventaja si necesita avanzar en su proceso de datos o un mapa que se .NET objeto, por ejemplo.