2012-07-14 10 views
10

creo que tengo una pregunta difícil aquí ... :(Incluir meses que faltan en consulta GROUP BY

Estoy tratando de obtener un recuento ordenado por mes, aún cuando es cero Aquí está la consulta problema:.

SELECT datename(month, OrderDate) as Month, COUNT(OrderNumber) AS Orders 
FROM OrderTable 
WHERE OrderDate >= '2012-01-01' and OrderDate <= '2012-06-30' 
GROUP BY year(OrderDate), month(OrderDate), datename(month, OrderDate) 

Lo que estoy buscando a conseguir es algo como esto:.

Month   Orders 
-----   ------ 
January   10 
February   7 
March   0 
April   12 
May    0 
June    5 

... pero mi consulta se salta una fila de marzo y mayo he intentado COALESCE(COUNT(OrderNumber), 0) y ISNULL(COUNT(OrderNumber), 0) pero estoy bastante seguro la agrupación es ca usando eso para no trabajar.

+3

Parece que 'OrderTable' no tiene registros para marzo o mayo, por lo que la selección de esta tabla por sí sola no puede devolver un resultado para esos meses. Posibles soluciones alternativas: si su base de datos tiene una tabla maestra de fechas, puede aprovechar eso, o puede crear su propia tabla de fechas sobre la marcha. –

+1

Como dijo @andyholaday: crea una tabla de búsqueda con enero ... diciembre y haz una combinación a la izquierda, y obtendrás los meses faltantes. – kd7

Respuesta

21

Esta solución no requiere que codifique la lista de meses que desee, todo lo que necesita hacer es proporcionar cualquier fecha de inicio y cualquier fecha de finalización, y calculará los límites de los meses para usted. Incluye año en el resultado para que soporte más de 12 meses y para que las fechas de inicio y finalización puedan cruzar el límite de un año y seguir ordenando correctamente y mostrar el mes correcto y año.

DECLARE @StartDate SMALLDATETIME, @EndDate SMALLDATETIME; 

SELECT @StartDate = '20120101', @EndDate = '20120630'; 

;WITH d(d) AS 
(
    SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, @StartDate), 0)) 
    FROM (SELECT TOP (DATEDIFF(MONTH, @StartDate, @EndDate) + 1) 
    n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1 
    FROM sys.all_objects ORDER BY [object_id]) AS n 
) 
SELECT 
    [Month] = DATENAME(MONTH, d.d), 
    [Year]  = YEAR(d.d), 
    OrderCount = COUNT(o.OrderNumber) 
FROM d LEFT OUTER JOIN dbo.OrderTable AS o 
    ON o.OrderDate >= d.d 
    AND o.OrderDate < DATEADD(MONTH, 1, d.d) 
GROUP BY d.d 
ORDER BY d.d; 
+0

¡Guau, impresionante! Su SELECT no devolvió el recuento correcto de pedidos, pero su parte WITH funcionó de maravilla ... así que simplemente dejé de juntar los resultados del WITH con mi SELECT como subconsulta y funciona muy bien, año y todo! –

+0

Muchas gracias, esto es justo lo que necesitaba es una gran pieza de código. – Enzero

+0

¿cómo se ve esta consulta en LINQ? esto es realmente una pesadilla para mí T_T – Baby

1

Dado que su consulta simplemente No puede adivinar los meses que desea, deberá tener los meses que desee almacenados en algún lugar, únelos con su tabla y luego agrúpelos. Algo así como:

;With Months (Month) 
AS 
(

    select 'January' as Month 
    UNION 
    select 'February' as Month 
    UNION 
    select 'March' as Month 
    UNION 
    select 'April' as Month 
    UNION 
    select 'May' as Month 
    UNION 
    select 'June' as Month 
    UNION 
    select 'July' as Month 
    UNION 
    select 'August' as Month 
    UNION 
    select 'September' as Month 
    UNION 
    select 'October' as Month 
    UNION 
    select 'November' as Month 
    UNION 
    select 'December' as Month 

) 
--Also you could have them in a "Months" Table 

A continuación, sólo JOIN esta tabla con la tabla:

Select 
    SELECT datename(month, OrderDate) as Month, COUNT(OrderNumber) 
    FROM Months T1 
    LEFT JOIN OrderTable T2 on datename(month, T2.OrderDate) = T2.Month 
    WHERE (T2.OrderDate >= '2012-01-01' and T2.OrderDate <= '2012-06-30') 
OR T2.OrderDate IS NULL --So will show you the months with no rows 
    GROUP BY year(T2.OrderDate), month(T2.OrderDate), datename(month, T2.OrderDate) 

espero que funcione!

+0

Gracias por su esfuerzo, pero esto no funcionó para mí. Estaba pensando en cambiar T1 y T2, pero no pude conseguirlo. No veo cómo podría funcionar de todos modos porque aún no hay resultados desde OrderTable para unirme a la tabla de meses. Si entiendo el concepto, en el mejor de los casos recibiría los 12 meses, que tampoco es lo que estoy buscando. –

1

Aquí es uno usando CTE recursiva:

declare @StartDate datetime = '2015-04-01'; 
declare @EndDate datetime = '2015-06-01'; 

-- sample data 
declare @orders table (OrderNumber int, OrderDate datetime); 
insert into @orders 
select 11, '2015-04-02' 
union all 
select 12, '2015-04-03' 
union all 
select 13, '2015-05-03' 
; 

-- recursive CTE 
with dates 
as (
    select @StartDate as reportMonth 
    union all 
    select dateadd(m, 1, reportMonth) 
    from dates 
    where reportMonth < @EndDate 
    ) 
select 
    reportMonth, 
    Count = count(o.OrderNumber) 
from dates 
left outer join @orders as o 
    on o.OrderDate >= reportMonth 
    and o.OrderDate < dateadd(MONTH, 1, reportMonth) 
group by 
    reportMonth 
option (maxrecursion 0); 
;