Мой оригинальный запрос - посчитать по месяцам, сколько предметов никогда не заказывалось.

Я создал скрипту SQL здесь http://sqlfiddle.com/#!18/e89a7/2

И собрать запрос ниже

SELECT COUNT(ItemNo)
FROM Item
WHERE ItemNo NOT IN 
(SELECT ItemNo
FROM Order1)

Однако это не делает то, что я хочу, и мне интересно, если я поступаю неправильно. Мне нужно, чтобы это было сгруппировано по упорядоченным месяцам и годам, но предложение NOT IN не позволяет мне это делать.

Пример данных:

CREATE TABLE Item (
    ItemNo varchar(10),
    MonthStocked varchar(10),
    YearStocked varchar(10)
);

CREATE TABLE Order1 (
    OrderNo int,
    ItemNo varchar(10),
    MonthOrdered varchar(10),
    YearOrdered varchar(10)
);

INSERT INTO Item (ItemNo, MonthStocked,YearStocked)
VALUES ('111','Feb', 2017),
('222','Jan',  2018),
('333','Feb', 2017),
('444','Feb', 2017),
('555','Jan', 2017),
('666','Jan', 2017);

INSERT INTO Order1 (OrderNo, ItemNo,MonthOrdered,YearOrdered)
VALUES ('897', '111', 'Dec', '2017'),
('657', '222', 'Nov', '2017'),
('896', '333', 'Nov' , '2017'),
('867', '333', 'Dec' , '2017'),
('234', '444', 'Nov' , '2017');

желаемый результат:

| ItemsNotOrdered  |     Month    |     Year    |
|------------------|--------------|-------------|
|                3 |          Nov |        2017 |
|                4 |          Dec |        2017 |
1
Ryan Gadsdon 22 Фев 2018 в 18:23

4 ответа

Лучший ответ

Вам нужно получить декартово произведение, используя CROSS JOIN между всеми уникальными ItemNo всеми уникальными заказанными месяцами и годами. Полученный продукт будет тогда внешним соединением с таблицей Items при трех условиях. Существующие ItemNo просто те, кого вы ищете.

SELECT  b.MonthOrdered, b.YearOrdered,
        COUNT(CASE WHEN c.ItemNo IS NULL THEN 1 END)
FROM    (SELECT DISTINCT ItemNo FROM Item) a
        CROSS JOIN (SELECT  DISTINCT MonthOrdered, YearOrdered FROM Order1) b
        LEFT JOIN Order1 c
            ON a.ItemNo = c.ItemNo
                AND b.MonthOrdered = c.MonthOrdered 
                AND b.YearOrdered = c.YearOrdered 
 GROUP BY b.MonthOrdered, b.YearOrdered

Вот демонстрационная версия.

Если ItemNo уникален, исключите подзапрос и используйте таблицу напрямую.

SELECT  b.MonthOrdered, b.YearOrdered,
        COUNT(CASE WHEN c.ItemNo IS NULL THEN 1 END)
FROM    Item a
        CROSS JOIN (SELECT  DISTINCT MonthOrdered, YearOrdered FROM Order1) b
        LEFT JOIN Order1 c
            ON a.ItemNo = c.ItemNo
                AND b.MonthOrdered = c.MonthOrdered 
                AND b.YearOrdered = c.YearOrdered 
 GROUP BY b.MonthOrdered, b.YearOrdered

Вот демо-версия.

2
John Woo 22 Фев 2018 в 15:39

Вы можете использовать cross join для генерации всех комбинаций элемент / год / месяц. Затем left join и group by, чтобы получить желаемые результаты:

select ym.mon, ym.mon, count(*)
from item i cross join
     (select distinct MonthOrdered as mon, YearOrdered as yr from order1
     ) ym left join
     order1 o
     on i.itemno = o.itemno and ym.mon = o.MonthOrdered and ym.yr = o.YearOrdered 
where o.itemno is null
group by ym.yr, ym.mon;

Здесь - скрипта SQL.

1
Gordon Linoff 22 Фев 2018 в 15:45

Мне не нравится ваш желаемый результат, так как он пропускает месяцы, когда ничего не продается. Мне также не нравится, что месяцы не сортируют.

declare @item table (ItemNo varchar(10), MonthStocked varchar(10), YearStocked varchar(10))
INSERT INTO @Item (ItemNo, MonthStocked,YearStocked)
VALUES ('111','Feb', 2017),
       ('222','Jan', 2018),
       ('333','Feb', 2017),
       ('444','Feb', 2017),
       ('555','Jan', 2017),
       ('666','Jan', 2017);

declare @Order1 table (OrderNo int, ItemNo varchar(10), MonthOrdered varchar(10), YearOrdered varchar(10)) 
INSERT INTO @Order1 (OrderNo, ItemNo,MonthOrdered,YearOrdered)
VALUES ('897', '111', 'Dec', '2017'),
       ('657', '222', 'Nov', '2017'),
       ('896', '333', 'Nov', '2017'),
       ('867', '333', 'Dec', '2017'),
       ('234', '444', 'Nov', '2017');

declare @itemCount int = (select count(distinct ItemNo) from @item);

with CTEyymm as 
( select MonthStocked as mm, YearStocked as yy
    from @item 
  union
  select MonthOrdered, YearOrdered
    from @Order1
)

select yymm.yy, yymm.mm
     , (@itemCount - count(distinct(o.ItemNo))) as cnt  
  from CTEyymm yymm 
  left join @order1 o 
    on o.YearOrdered  = yymm.yy 
   and o.MonthOrdered = yymm.mm 
 group by yymm.yy, yymm.mm
 order by yymm.yy, yymm.mm;

Если вам не нужны пустые месяцы, просто:

select o.YearOrdered, o.MonthOrdered
     , (@itemCount - count(distinct(o.ItemNo))) as cnt  
  from @order1 o 
 group by o.YearOrdered, o.MonthOrdered
 order by o.YearOrdered, o.MonthOrdered  
0
paparazzo 22 Фев 2018 в 17:49

Еще один вариант использования OUTER APPLY

SELECT   DISTINCT 
         MonthOrdered,
         YearOrdered,
         ItemsNotOrdered
FROM     Order1 o
OUTER APPLY (
         SELECT COUNT(ItemNo) ItemsNotOrdered
         FROM   Item i
         WHERE  NOT EXISTS (
            SELECT  ItemNo 
            FROM    Order1 o2
            WHERE   i.ItemNo = o2.ItemNo
            AND     o2.MonthOrdered = o.MonthOrdered
            AND     o2.YearOrdered = o.YearOrdered
         ) 
) ino
0
JamieD77 22 Фев 2018 в 15:51