Reputation: 2378
My original query is to count by month how many items have never been ordered.
I have created a sql fiddle here http://sqlfiddle.com/#!18/e89a7/2
And put together the query below
SELECT COUNT(ItemNo)
FROM Item
WHERE ItemNo NOT IN
(SELECT ItemNo
FROM Order1)
However this doesn't do what I want and I wonder if I'm going about it the wrong way. I need this to be grouped by ordered month and year but the NOT IN clause doesn't allow me to do this.
Sample data:
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');
desired output:
| ItemsNotOrdered | Month | Year |
|------------------|--------------|-------------|
| 3 | Nov | 2017 |
| 4 | Dec | 2017 |
Upvotes: 1
Views: 86
Reputation: 45096
I don't like your desired output as it misses months where nothing is sold. I also don't like that months don't sort.
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;
If you don't care about blank months then just:
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
Upvotes: 0
Reputation: 13949
Another option using 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
Upvotes: 0
Reputation: 1270723
You can use cross join
to generate all the item/year/month combinations. Then left join
and group by
to get the results you want:
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;
Here is the SQL Fiddle.
Upvotes: 1
Reputation: 263843
You need to get the cartesian product using CROSS JOIN
between all unique ItemNo
to all unique ordered months and years. The resulting product will then be outer join to table Items
on three conditions. The inexisting ItemNo
are simply the one you are looking for.
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
Here's a Demo.
If ItemNo
is unique, eliminate the subquery and use the table directly.
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
Here's a Demo.
Upvotes: 2