2012-02-26 5 views
2

Мне нужно найти список из трех продуктов и их количества, сгруппированные по дате, за последние пять дней. Для этого я использовал TSQL ниже.Нулевой подсчет для категории нет категории

SELECT CONVERT(VARCHAR(10),WM_data,103) as Date, 
     Product = CASE 
        WHEN WM_Product = 1 THEN 'Product A' 
        WHEN WM_Product = 2 THEN 'Product B' 
        ELSE 'Product C' 
       END, 
     WM_qtd as Quantity 
    FROM Tb_InfoProduct 
GROUP BY CONVERT(VARCHAR(10),WM_data,103), WM_Product 
    HAVING CONVERT(VARCHAR(10),WM_data,103) > convert(VARCHAR(10),dateadd(dd, -5, getdate()),103) 
ORDER BY CONVERT(VARCHAR(10),WM_data,103), WM_Product 

в результате я получаю:

Date   Product  Quantity 
------------------------------------------------- 
16/02/2012 Product A 132 
16/02/2012 Product C  10 
17/02/2012 Product A 163 
17/02/2012 Product B  61 
17/02/2012 Product C  58 
18/02/2012 Product A  7 
18/02/2012 Product B  2 

Но мне нужно:

Date  Product  Quantity 
------------------------------------------------- 
16/02/2012 Product A 132 
16/02/2012 Product B  0 -- This 
16/02/2012 Product C 10 
17/02/2012 Product A 163 
17/02/2012 Product B 61 
17/02/2012 Product C 58 
18/02/2012 Product A  7 
18/02/2012 Product B  2 
18/02/2012 Product C  0 -- This 

Как я могу добиться этого?

+0

Вы должны иметь (или генерировать) список дат, которые вы затем OUTER присоединиться к на значении даты. –

+0

Как в стороне, вы должны сделать фильтрацию в разделе 'WHERE' (используя объекты даты, а не строки) – Leigh

ответ

1

Прежде всего, есть некоторые проблемы с вашим запросом:

  1. Ваш пункт SELECT содержит столбец, который не является ни в GROUP BY ни агрегируются - WM_qtd. Вы имели в виду SUM(WM_qtd), не так ли?

  2. В целом, даты, отформатированные с CONVERT(103), не могут быть осмысленно сопоставлены или отсортированы. Я бы посоветовал вам использовать другой формат (например, 112) или использовать другой метод удаления временной части вообще и применить форматирование позже (только в предложении SELECT или, возможно, вообще не в SQL).

  3. Вы фильтруете исходный набор данных с помощью предложения HAVING, что является неэффективным, поскольку все данные должны быть вытащены, сгруппированы и только затем отфильтрованы. В этом конкретном случае было бы лучше отфильтровать данные с помощью предложения WHERE.

Теперь за ваш вопрос ... Результирующий набор вы после в основном кросс-произведение всех соответствующих дат и всех соответствующих продуктов (ой, простите за каламбур). Он просто содержит некоторую дополнительную информацию, например «Количество».

Таким образом, вам необходимо получить два списка, даты и продукты, перекрестия их, затем вы можете подключить внешний результирующий набор к Tb_InfoProduct и группировать по дате и продукту, чтобы получить номера - то же, что вы уже делаете в вашем запросе будут указаны только столбцы даты и продукта, которые должны быть указаны в статьях GROUP BY и SELECT, в соответствующих производных списках, а не в таблице Tb_InfoProduct.

Для моего ответа я предполагаю, что подмножество Tb_InfoProduct, которое вы запрашиваете, содержит все даты и все продукты, которые должны присутствовать на выходе, поэтому для получения двух списков я просто выбор отдельных дат и отдельных продуктов из подмножества Tb_InfoProduct. Вот приближенное решение в полном объеме:

SELECT 
    Date = CONVERT(VARCHAR(10), d.Date, 103), 
    Product = CASE p.WM_Product 
    WHEN 1 THEN 'Product A' 
    WHEN 2 THEN 'Product B' 
    ELSE  'Product C' 
    END, 
    Quantity = SUM(WM_qtd) 
FROM (
    SELECT DISTINCT Date = DATEADD(DAY, DATEDIFF(DAY, 0, WM_data), 0) 
    FROM Tb_InfoProduct 
    WHERE WM_data >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 4, 0) 
) d 
CROSS JOIN (
    SELECT DISTINCT WM_Product 
    FROM Tb_InfoProduct 
    WHERE WM_data >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 4, 0) 
) p 
LEFT JOIN (
    SELECT 
    Date = DATEADD(DAY, DATEDIFF(DAY, 0, WM_data), 0), 
    WM_Product 
    FROM Tb_InfoProduct 
    WHERE WM_data >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 4, 0) 
) i ON d.Date = i.Date AND p.WM_Product = i.WM_Product 
GROUP BY 
    d.Date, 
    p.WM_Product 
ORDER BY 
    d.Date, 
    p.WM_Product 

Если запрашиваемое подмножество может пропустить некоторые даты и/или продукты, которые вы предпочли бы включить в выводе, вы должны создать список (ов) иначе. Например, вы можете использовать таблицу ссылок Products для списка продуктов, если она у вас есть, конечно. Что касается дат, вам может понадобиться сгенерировать список дат для данного диапазона, как предложил @OMG Ponies (и этот вопрос мог бы помочь вам с этим: Get a list of dates between two dates using a function).

0

при использовании SQL Server 2008:

SELECT table1.date, table2.productid, isnull(table3.total,0) total FROM 
--get the last 5 days 
(
select distinct date from mytable1 where date > getdate() - 5 
) table1 
--get the 3 products 
CROSS JOIN 
(
select productid from product where productid in (1,2,3) 
) table2 
OUTER APPLY (SELECT SUM(1) Total from YourTable 
    where yourtable.date = table1.date 
    and yourtable.productid = table2.productid) Table3 
Смежные вопросы