How can distribute one table data to another table - sql

I have two tables Orderapproval and stock:
Table 1: Orderapproval
itemcode
approvalqty
1
25
2
10
Table 2: stock
itemcode
stockqty
batch
date
1
5
aa
2021-02-01
1
10
bb
2021-02-10
1
15
cc
2021-02-02
2
5
dd
2021-02-01
2
20
aa
2021-02-05
we have stock batch-wise.
when we get an order request for an item
we are trying to pick up items from the old batch first & so on
How can I get a result set like this:
itemcode
qty
batch
date
1
5
aa
2021-02-01
1
15
cc
2021-02-02
1
5
bb
2021-02-10
2
5
dd
2021-02-01
I am trying as......
Declare #StockTable table
(ItemCode int,BranchCode int,Qty int)
Insert Into #StockTable values
(1, 101, 5),
(1, 102, 10),
(1, 101, 15),
(2, 102, 5),
(2, 103, 20)
Declare #Orderapproval table (ItemCode int,Qty int)
Insert Into #Orderapproval values
(1,25),
(2,10)
;with T1 as (
Select A.*
,ToDist = cast(D.Qty as int),
running_total = SUM(D.Qty) OVER (PARTITION BY A.ItemCode,BranchCode ORDER BY BranchCode DESC
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
From #StockTable A
Join #Orderapproval D on A.ItemCode=D.ItemCode )
, T2 as (
Select *, prev_running_total = LAG(Qty,1,0) OVER (PARTITION BY ItemCode ORDER BY BranchCode DESC) From T1
)
select *,
CASE WHEN prev_running_total >= ToDist THEN 0
WHEN running_total > ToDist THEN ToDist - prev_running_total
ELSE qty
END 'qty'
from T2;
I didn't get the right output.
Please suggest a better way

Try this:
SELECT s.itemcode
, s.stockqty AS qty
, s.batch
, s.date
FROM stock AS s
INNER JOIN Orderapproval AS oa
ON s.stockqty <= oa.approvalqty
If required, insert resultset into new table

I try it on SQLite 3.30
Schema
CREATE TABLE stock (
id INT,
itemcode INT,
stockqty INT,
batch CHAR,
date DATE
);
INSERT INTO stock (id, itemcode, stockqty, batch, date) VALUES (1, 1, 5, 'aa', '2021-02-01');
INSERT INTO stock (id, itemcode, stockqty, batch, date) VALUES (2, 1, 10, 'bb', '2021-02-10');
INSERT INTO stock (id, itemcode, stockqty, batch, date) VALUES (3, 1, 15, 'cc', '2021-02-02');
INSERT INTO stock (id, itemcode, stockqty, batch, date) VALUES (4, 2, 5, 'dd', '2021-02-01');
INSERT INTO stock (id, itemcode, stockqty, batch, date) VALUES (5, 2, 20, 'aa', '2021-02-05');
CREATE TABLE orderapproval (
id INT,
itemcode INT,
approvalqty INT
);
INSERT INTO orderapproval (id, itemcode, approvalqty) VALUES (1, 1, 25);
INSERT INTO orderapproval (id, itemcode, approvalqty) VALUES (2, 2, 10);
Query
with a as ( SELECT *, sum(stockqty) over (partition by itemcode order by date ) as acc FROM stock)
select a.itemcode, case when acc<=approvalqty then stockqty else approvalqty-(acc-stockqty) end as qty, batch, date from a
inner join orderapproval o
on a.itemcode = o.itemcode
where qty > 0
order by a.itemcode, date
Result
itemcode
qty
batch
date
1
5
aa
2021-02-01
1
15
cc
2021-02-02
1
5
bb
2021-02-10
2
5
dd
2021-02-01
2
5
aa
2021-02-05
db-fiddle

SQL CODE---(Tested)
Select stock.itemcode
,stock.stockqty AS qty
,stock.batch,stock.date
from stock
join orderapproval on stock.stockqty <= orderapproval.approvalgty
group by stock.batch;

Related

Ranking functions and Joins

I have the following tables.
Order_det
Ord_num
item_code
Unit_sales_price
1111
1
50
1111
2
40
1111
3
30
1111
4
20
1111
5
10
2222
3
30
Pick_det
Ord_num
Shipment_num
Item_code
Qty_to_pick
Qty_picked
1111
1
1
100
100
1111
2
1
100
100
1111
3
2
100
100
2222
3
3
200
200
I want the table as follows,
Ord_num
Shipment_num
Item_code
Qty_to_pick
Qty_picked
Unit_sales_price
Total_price (Unit_sales_price*Qty_picked)
1111
3
2
100
100
40
4000
2222
3
3
200
200
30
6000
With the help of this community, I found a very similar answer i.e,
Link to that answer, Similar question
select *
from
(
select t1.*, max(shipment_num) over (partition by ord_num) as orders_max_ship_num
from pick_det t1
) with_max
where shipment_num = orders_max_ship_num
order by ord_num, item_code;
My question is,
where do I join the Order_det table to get the Unit_sales_price value to the already retrieved max shipment_num rows from pick_det table?
Im late to the party, as I typed everything up and made sure it ran correctly
if OBJECT_ID('tempdb..#Order_det') IS NOT NULL DROP TABLE #Order_det;
if OBJECT_ID('tempdb..#Pick_det') IS NOT NULL DROP TABLE #Pick_det;
CREATE TABLE #Order_det (Ord_num INT, item_code INT, Unit_sales_price INT);
CREATE TABLE #Pick_det(Ord_num INT, Shipment_num INT, Item_code INT, Qty_to_pick INT, Qty_picked INT);
INSERT INTO #Order_det (Ord_num, item_code, Unit_sales_price)
VALUES
(1111, 1, 50),
(1111, 2, 40),
(1111, 3, 30),
(1111, 4, 20),
(1111, 5, 10),
(2222, 3, 30)
INSERT INTO #Pick_det (Ord_num, Shipment_num, Item_code, Qty_to_pick, Qty_picked)
VALUES
(1111, 1, 1, 100, 100),
(1111, 2, 1, 100, 100),
(1111, 3, 2, 100, 100),
(2222, 3, 3, 200, 200)
SELECT
OrderDet.Ord_num
, PickList.Shipment_Num
, OrderDet.Item_code
, PickList.Qty_to_pick
, PickList.Qty_picked
, OrderDet.Unit_sales_price
, OrderDet.Unit_sales_price * PickList.Qty_picked AS Total_price
FROM
(
SELECT
Ord_num
, MAX(Shipment_num) OVER (PARTITION BY ord_num) AS MaxShipment_Num--
, Shipment_num
, Item_code
, Qty_to_pick
, Qty_picked
FROM #Pick_det
) AS PickList
INNER JOIN #Order_det AS OrderDet
ON OrderDet.Ord_num = PickList.Ord_num
AND OrderDet.item_code = PickList.Item_code
and PickList.Shipment_num = PickList.MaxShipment_Num
You can simply base a query on yours:
with s as
(
select *
from
(
select t1.*, max(shipment_num) over (partition by ord_num) as orders_max_ship_num
from pick_det t1
) with_max
where shipment_num = orders_max_ship_num
)
select
s.ord_num, s.shipment_num, s.item_code, s.qty_to_pick, s.qty_picked,
od.unit_sales_price, od.unit_sales_price * s.qty_picked as total_price
from s
join order_det od on od.ord_num = s.ord_num and od.item_code = s.item_code
order by s.ord_num, s.item_code;
Or you apply the join right away:
select
s.ord_num, s.shipment_num, s.item_code, s.qty_to_pick, s.qty_picked,
od.unit_sales_price, od.unit_sales_price * s.qty_picked as total_price
from
(
select t1.*, max(shipment_num) over (partition by ord_num) as orders_max_ship_num
from pick_det t1
) s
join order_det od on od.ord_num = s.ord_num and od.item_code = s.item_code
where s.shipment_num = s.orders_max_ship_num
order by s.ord_num, s.item_code;
Another simple technique you could use is by applying analytic function - Dense_Rank which is a window function that assigns a rank to each row within a partition of a result set
I am trying to rank by shipment_num in descending order and then just JOINing on the common column. I have used CTE and sub-query as well:
With temp AS(Select * from (SELECT a.*, dense_rank() OVER (PARTITION BY ord_num order by shipment_num desc) rnk
from Pick_det a) t where rnk = 1)
Select temp.Ord_num, temp.Shipment_num, temp.item_code, temp.Qty_to_pick, temp.Qty_picked,
temp.qty_to_pick * Order_det.Unit_sales_price total_price
from temp JOIN Order_det on temp.ord_num = Order_det.ord_num
AND temp.item_code = Order_det.item_code;
Your Sub-query would look something like this:
Select temp.Ord_num, temp.Shipment_num, temp.Item_code, temp.Qty_to_Pick, temp.Qty_picked,
temp.qty_to_pick * Order_det.Unit_sales_price total_price
from
(SELECT a.*, dense_rank() OVER (PARTITION BY ord_num order by shipment_num desc) rnk
from Pick_det a) temp INNER JOIN Order_det on temp.ord_num = Order_det.ord_num
AND temp.item_code = Order_det.item_code
AND temp.rnk = 1;
If you want to learn about dense_rank function, here is the link
Here is the SQL Fiddle

SQL query for date intervals comparing non-adjacent rows?

I want to flag the first date in every window of at least 31 days for each ID unit in my data.
ROW ID INDEX_DATE
1 ABC 1/1/2019
2 ABC 1/7/2019
3 ABC 1/21/2019
4 ABC 2/2/2019
5 ABC 2/9/2019
6 ABC 3/6/2019
7 DEF 1/5/2019
8 DEF 2/1/2019
9 DEF 2/8/2019
The desired rows are 1, 4, 6, 7 and 9; these are either the first INDEX_DATE for the given ID, or they occur at least 31 days after the previously flagged INDEX_DATE. Every suggestion I have found uses LAG() or LEAD with window functions, but I could only get these to compare adjacent rows. Row 4, for example, needs to be compared to Row 1 in order to be identified as the first after a 31-day window has completed.
I tried the following:
Data
DROP TABLE tTest IF EXISTS;
CREATE TEMP TABLE tTest
(
ROWN INT,
ID VARCHAR(3),
INDEX_DATE DATE
) ;
GO
INSERT INTO tTEST VALUES (1, 'ABC', '1/1/2019');
INSERT INTO tTEST VALUES (2, 'ABC', '1/7/2019');
INSERT INTO tTEST VALUES (3, 'ABC', '1/21/2019');
INSERT INTO tTEST VALUES (4, 'ABC', '2/2/2019');
INSERT INTO tTEST VALUES (5, 'ABC', '2/9/2019');
INSERT INTO tTEST VALUES (6, 'ABC', '3/6/2019');
INSERT INTO tTEST VALUES (7, 'DEF', '1/5/2019');
INSERT INTO tTEST VALUES (8, 'DEF', '2/1/2019');
INSERT INTO tTEST VALUES (9, 'DEF', '2/8/2019');
GO
Query:
DROP TABLE TTEST2 IF EXISTS;
CREATE TEMP TABLE TTEST2 AS (
WITH
RN_CTE(ROWN, ID, INDEX_DATE, RN) AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY INDEX_DATE)
FROM tTEST),
MIN_CTE(ROWN, ID, INDEX_DATE, RN) AS (SELECT * FROM RN_CTE WHERE RN=1),
DIFF_CTE(ROWN,ID, INDEX_DATE, RN, DAY_DIFF) AS (
SELECT RN.*, DATE(RN.INDEX_DATE + INTERVAL '30 DAYS')
FROM RN_CTE AS RN
JOIN MIN_CTE AS MC ON RN.ID=MC.ID
WHERE RN.RN=1
OR RN.INDEX_DATE > MC.INDEX_DATE + INTERVAL '30 DAYS' ),
MIN_DIFF_CTE AS (
SELECT ID, DAY_DIFF, MIN(ROWN) AS MIN_ROW
FROM DIFF_CTE
GROUP BY ID, DAY_DIFF)
SELECT T.*
FROM MIN_DIFF_CTE AS MDC
JOIN tTEST AS T ON MDC.MIN_ROW = T.ROWN
ORDER BY ID, INDEX_DATE
);
Result:
SELECT * FROM TTEST2 ORDER BY ID, INDEX_DATE;
ROWN ID INDEX_DATE
1 ABC 2019-01-01
4 ABC 2019-02-02
5 ABC 2019-02-09
6 ABC 2019-03-06
7 DEF 2019-01-05
9 DEF 2019-02-08
Row 5 with INDEX_DATE = 2019-02-09 should not be in the output because it is less than 31 days after Row 4's INDEX_DATE.
Something like this. The CTE's locate the unique window of the minimum ROW value for each ID transition and 31 days rolling too.
Data
drop table if exists #tTEST;
go
select * INTO #tTEST from (values
(1, 'abc', '1/1/2019'),
(2, 'abc', '1/7/2019'),
(3, 'abc', '1/21/2019'),
(4, 'abc', '2/2/2019'),
(5, 'abc', '2/9/2019'),
(6, 'abc', '3/6/2019'),
(7, 'def', '1/5/2019'),
(8, 'def', '2/1/2019'),
(9, 'def', '2/8/2019')) V([ROW], ID, INDEX_DATE);
Query
;with
rn_cte([ROW], ID, INDEX_DATE, rn) as (
select *, row_number() over (partition by ID order by INDEX_DATE)
from #tTEST),
min_cte([ROW], ID, INDEX_DATE, rn) as (select * from rn_cte where rn=1),
diff_cte([ROW], ID, INDEX_DATE, rn, day_diff) as (
select rn.*, datediff(d, mc.INDEX_DATE, rn.INDEX_DATE)/31
from rn_cte rn
join min_cte mc on rn.ID=mc.ID
where rn.rn=1
or datediff(d, mc.INDEX_DATE, rn.INDEX_DATE)/31>0),
min_diff_cte as (
select ID, day_diff, min([ROW]) min_row
from diff_cte
group by ID, day_diff)
select t.*
from min_diff_cte mdc
join #tTEST t on mdc.min_row=t.ROW
order by 1;
Output
ROW ID INDEX_DATE
1 abc 1/1/2019
4 abc 2/2/2019
6 abc 3/6/2019
7 def 1/5/2019
9 def 2/8/2019

Accumulating previous rows with grouping

I have this table on MS SQL Server
Customer Month Amount
-----------------------------
Tom 1 10
Kate 1 60
Ali 1 70
Tom 2 50
Kate 2 40
Tom 3 80
Ali 3 20
I want the select to get accumulation of the customer for each month
Customer Month Amount
-----------------------------
Tom 1 10
Kate 1 60
Ali 1 70
Tom 2 60
Kate 2 100
Ali 2 70
Tom 3 140
Kate 3 100
Ali 3 90
Noticing that Ali has no data for the month of 2
and Kate has no data for the month of 3
I have done it but the problem is that for the missing month for each customer no data shows
i.e. Kate has to be in month 3 with 100 amount
and Ali has to be in Month 2 with 70 amount
declare #myTable as TABLE (Customer varchar(50), Month int, Amount int)
;
INSERT INTO #myTable
(Customer, Month, Amount)
VALUES
('Tom', 1, 10),
('Kate', 1, 60),
('Ali', 1, 70),
('Tom', 2, 50),
('Kate', 2, 40),
('Tom', 3, 80),
('Ali', 3, 20);
select * from #myTable
select
SUM(b.Amount),a.Customer, a.Month
from
#myTable a
inner join
#myTable b
on a.Customer = b.Customer and
a.Month >= b.Month
group by
a.Customer, a.Month
Use window function
select Customer, Month,
sum(Amount) over (partition by customer order by month) Amount
from table t
So, you want some kind of look up tables which has possible months with customers.
with cte as
(
select * from (
select Customer from table
group by Customer)c
cross join (values (1),(2),(3))a(Months)
) -- look-up table
select c.Customer, c.Months,
sum(t.Amount) over (partition by c.Customer order by c.Months) Amount
from cte c left join table t
on t.Month = c.Months and t.Customer = c.Customer
Result :
Customer Months Amount
Tom 1 10
Kate 1 60
Ali 1 70
Tom 2 60
Ali 2 70
Kate 2 100
Ali 3 90
Kate 3 100
Tom 3 140
with cte as
(select *
from
(select distinct customer from myTable ) c
cross join ( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) t(month))
select cte.customer, cte.month,
sum(myTable.amount) over (partition by cte.customer order by cte.month) as cumamount
from cte left join myTable
on cte.customer = myTable.customer and cte.month = myTable.month
order by cte.month, cte.customer desc
to be clear(in answer Amount and AmountSum)
DECLARE #myTable TABLE(Customer varchar(50), Month int, Amount int);
INSERT INTO #myTable(Customer, Month, Amount)
VALUES
('Tom', 1, 10),
('Kate', 1, 60),
('Ali', 1, 70),
('Tom', 2, 50),
('Kate', 2, 40),
('Tom', 3, 80),
('Ali', 3, 20);
DECLARE #FullTable TABLE(Customer varchar(50), Month int, Amount int);
INSERT INTO #FullTable(Customer, Month, Amount)
SELECT c.Customer, m.Month, ISNULL(mt.Amount, 0)
FROM (SELECT DISTINCT [Month] FROM #myTable) AS m
CROSS JOIN (SELECT DISTINCT Customer FROM #myTable) AS c
LEFT JOIN #myTable AS mt ON m.Month = mt.Month AND c.Customer = mt.Customer
SELECT t1.Customer, t1.Month, t1.Amount, (t1.Amount + ISNULL(t2.sm, 0)) AS AmountSum
FROM #FullTable AS t1
CROSS APPLY (SELECT SUM(Amount) AS sm FROM #FullTable AS t WHERE t.Customer = t1.Customer AND t.Month < t1.Month) AS t2
ORDER BY Month, Customer
Do you want get the sum amount every month for each customer , what ever the customer has transaction in that month?
In following script, If you have a customer table, you can join the customer table, do not need use (SELECT DISTINCT Customer FROM #myTable)
declare #myTable as TABLE (Customer varchar(50), Month int, Amount int);
INSERT INTO #myTable(Customer, Month, Amount)
VALUES
('Tom', 1, 10),
('Kate', 1, 60),
('Ali', 1, 70),
('Tom', 2, 50),
('Kate', 2, 40),
('Tom', 3, 80),
('Ali', 3, 20),
('Jack', 3, 90);
SELECT c.Customer,sv.number AS Month ,SUM(CASE WHEN t.Month<=sv.number THEN t.Amount ELSE 0 END ) AS Amount
FROM master.dbo.spt_values AS sv
INNER JOIN (SELECT DISTINCT Customer FROM #myTable) AS c ON 1=1
LEFT JOIN #myTable AS t ON t.Customer=c.Customer
WHERE sv.type='P' AND sv.number BETWEEN 1 AND MONTH(GETDATE())
GROUP BY sv.number,c.Customer
ORDER BY c.Customer,sv.number
----------
Customer Month Amount
-------------------------------------------------- ----------- -----------
Ali 1 70
Ali 2 70
Ali 3 90
Jack 1 0
Jack 2 0
Jack 3 90
Kate 1 60
Kate 2 100
Kate 3 100
Tom 1 10
Tom 2 60
Tom 3 140
Try this the table name is "a".
Using a combination of Cte and sub query. Tried it out in MSSQL2008R2
with cte as
(
select * from (
select Customer from a
group by Customer)c
cross join (values (1),(2),(3),(4),(5),(6),(7),(8),(9), (10),(11),(12))a(Months)
)
select Customer,Months,
(select SUM(total) from
(select customer , month , sum(amount)as total from a group by customer,
month) as GroupedTable
where GroupedTable.customer= cte.customer and GroupedTable.month<= cte.Months) as total
from cte
Group by Customer,Months
order by Customer,Months
try this:
create table #tmp (Customer VARCHAR(10), [month] INT ,Amount INT)
INSERT INTO #tmp
SELECT 'Tom',1,10
union all
SELECT 'Kate',1,60
union all
SELECT 'Ali',1,70
union all
SELECT 'Tom',2,50
union all
SELECT 'Kate',2,40
union all
SELECT 'Tom',3,80
union all
SELECT 'Ali',3,20
;WITH cte1 AS (
SELECT [month], ROW_NUMBER() OVER(order by [month] desc) rn
FROM (SELECT DISTINCT [month] as [month] FROM #tmp) a
)
, cte2 AS (
SELECT customer, ROW_NUMBER() OVER(order by customer desc) rn
FROM (SELECT DISTINCT customer as customer FROM #tmp) b
)
SELECT t2.Customer,t2.[month],ISNULL(t1.Amount,0) As Amount
into #tmp2
from #tmp t1
RIGHT JOIN
(select [month],customer from cte1
cross apply
cte2) t2 ON t1.customer=t2.customer and t1.[month]=t2.[month]
order by t2.[month]
SELECT Customer,[Month] ,SUM (Amount) OVER(partition by customer order by customer ROWS UNBOUNDED PRECEDING ) as Amount
FROM #tmp2
order by [month]
drop table #tmp
drop table #tmp2
I think this does what you want
declare #myTable as TABLE (Customer varchar(50), Month int, Amount int);
INSERT INTO #myTable (Customer, Month, Amount)
VALUES
('Tom', 1, 10),
('Kate', 1, 60),
('Ali', 1, 70),
('Tom', 2, 50),
('Kate', 2, 40),
('Tom', 3, 80),
('Ali', 3, 20);
select dts.Month, cts.Customer, isnull(t.Amount, 0) as Amount
, sum(isnull(t.Amount, 0)) over(partition by cts.Customer order by dts.Month) as CumAmt
from ( select distinct customer
from #myTable
) cts
cross join ( select distinct Month
from #myTable
) dts
left join #myTable t
on t.Customer = cts.Customer
and t.Month = dts.Month
order by dts.Month, cts.Customer;
Month Customer Amount CumAmt
----------- -------------------------------------------------- ----------- -----------
1 Ali 70 70
1 Kate 60 60
1 Tom 10 10
2 Ali 0 70
2 Kate 40 100
2 Tom 50 60
3 Ali 20 90
3 Kate 0 100
3 Tom 80 140
Try Sum Over Partition By
https://learn.microsoft.com/en-us/sql/t-sql/functions/sum-transact-sql
This will help you get the idea how to accumulate. If the code i use in postgresql like this
Select sum(amount) over(partition by customer, month)
This should do it for you. Also here is a link to the Microsoft docs regarding aggregation functions.
https://learn.microsoft.com/en-us/sql/t-sql/functions/aggregate-functions-transact-sql
Example:
SELECT
Customer, Month, SUM(Amount) as Amount
FROM myTable
GROUP BY Customer, Month
ORDER BY Customer, Month

How to filter a query with one column have multiple matches in another column?

Having a hard time thinking through how I can filter my query. I have a query result, and with the results I only want to show products that were ordered as an "EACH" and as a "CASE" or "INNER" for a given week.
Example output at the moment:
Weekno Itemno UnitofMeasure ConvQty #Orders
1 31111 EACH 1 5
1 31111 CASE 144 10
1 31112 EACH 1 10
1 31113 CASE 144 2
2 31111 EACH 1 10
2 31112 EACH 1 20
2 31112 CASE 144 15
2 31113 EACH 1 5
2 31113 INNER 12 20
So with this sample output I would only want to show as a result:
1 31111 EACH 1 5
1 31111 CASE 144 10
2 31112 EACH 1 20
2 31112 CASE 144 15
2 31113 EACH 1 5
2 31113 INNER 12 20
Here is my current query on how I get those results:
SELECT datepart(ww,[Promised Ship Date]) as weekno
,[Item ID]
,[UNITOFMEASURE]
,[UNITOFMEASURECONV] as ConvQTY
,count(*) as Orders
FROM SalesHistory vw
inner join ITEMTABLE IT on IT.itemcode = vw.[Item Id]
where [Promised Ship Date] >= '2016-01-01' and [Promised Ship Date] < '2016-02-01'
and [UDF_MIN_SALES_UNIT] = 'Each'
group by datepart(ww,[Promised Ship Date]),[item id],[unitofmeasure],[UNITOFMEASURECONV]
order by weekno,[item id]
You would count 'EACH' and 'INNER'/'CASE' per week and item and only show those week/item pairs that have a match in both.
select weekno, itemno, unitofmeasure, convqty, #orders
from
(
select weekno, itemno, unitofmeasure, convqty, #orders
, count(case when unitofmeasure = 'EACH' then 1 end)
over (partition by weekno, itemno) as cnt_each
, count(case when unitofmeasure in ('INNER','CASE') then 1 end)
over (partition by weekno, itemno) as cnt_inner_or_case
from mytable
) counted
where cnt_each > 0 and cnt_inner_or_case > 0;
declare #T TABLE(
Weekno INT,
Itemno INT,
UnitofMeasure VARCHAR(32),
ConvQty INT,
Orders INT)
INSERT #T
VALUES
(1, 31111, 'EACH', 1, 5),
(1, 31111, 'CASE', 144, 10),
(1, 31111, 'CASE', 144, 11),
(1, 31112, 'EACH', 1 , 10),
(1, 31113, 'CASE', 144 , 2),
(2, 31111, 'EACH', 1 , 10),
(2, 31112, 'EACH', 1 , 20),
(2, 31112, 'CASE', 144, 15),
(2, 31113, 'CASE', 144, 5)
SELECT a.*
FROM #T a
JOIN (
SELECT DISTINCT t.Weekno, t.Itemno
FROM #T t
CROSS APPLY (SELECT Itemno FROM #T WHERE UnitofMeasure IN ('CASE', 'INNER') AND Weekno = t.Weekno AND Itemno = t.Itemno) c
WHERE t.UnitofMeasure = 'EACH'
) q ON q.Itemno = a.Itemno AND q.Weekno = a.Weekno
If I understood correctly, the following query should do the trick. It considers all UnitMeasures INNER as valid and all pairs of EACH and CASE as valid. :
DECLARE #t TABLE (Weekno int, Itemno int, UnitofMeasure varchar(10), ConvQty int, NumOrders int);
INSERT INTO #t VALUES
(1, 31111, 'EACH', 1 , 5 )
,(1, 31111, 'CASE', 144 , 10 )
,(1, 31112, 'EACH', 1 , 10 )
,(1, 31113, 'CASE', 144 , 2 )
,(2, 31111, 'EACH', 1 , 10 )
,(2, 31112, 'EACH', 1 , 20 )
,(2, 31112, 'CASE', 144 , 15 )
,(2, 31113, 'CASE', 144 , 5 );
WITH cte AS(
SELECT Weekno, Itemno, UnitofMeasure, ConvQty, NumOrders, CASE WHEN UnitofMeasure IN ('CASE', 'EACH') THEN 0.5 WHEN UnitofMeasure = 'INNER' THEN 1 END AS x
FROM #t
),
cteFilter AS(
SELECT Weekno, Itemno, sum(x) x
FROM cte
GROUP BY Weekno, Itemno
HAVING sum(x) = 1
)
SELECT t.*
FROM #t AS t
JOIN cteFilter AS f ON f.Weekno = t.Weekno AND f.Itemno = t.Itemno

Partition OVER field when grouping by another field

I have this table
ID UPC Sales Date
1 333 10 1/1/2015
1 222 20 1/1/2015
1 111 30 1/1/2015
1 444 10 2/1/2015
1 555 20 2/1/2015
2 333 20 1/1/2015
2 222 50 1/1/2015
2 111 30 1/1/2015
2 444 20 2/1/2015
2 555 20 2/1/2015
And want this output
ID SUM(Sales) Avg(Sales) COUNT(DISTINCT DATE)
1 90 45 2
2 140 70 2
I have tried this
SELECT ID, AVG(Sales) OVER (Partition BY Date) as basket_size
FROM Transactions
GROUP BY ID
But I get this error:
Column 'Transactions.Date' is invalid in the select list because it is
not contained in either an aggregate function or the GROUP BY clause.
Thoughts?
The following would give you the desired result:
DECLARE #t TABLE(ID INT,UPC INT, Sales INT, [Date] DATE);
INSERT INTO #t(ID,UPC,Sales,[Date])
VALUES(1,333,10,'2015-01-01'),
(1,222,20,'2015-01-01'),
(1,111,30,'2015-01-01'),
(1,444,10,'2015-01-02'),
(1,555,20,'2015-01-02'),
(2,333,20,'2015-01-01'),
(2,222,50,'2015-01-01'),
(2,111,30,'2015-01-01'),
(2,444,20,'2015-01-02'),
(2,555,20,'2015-01-02');
SELECT
t1.ID,
t1.[SUM(Sales)],
t2.basket_size AS [AVG(Sales)],
t1.[COUNT(DISTINCT Date)]
FROM
(
SELECT
ID,
SUM(Sales) AS [SUM(Sales)],
COUNT(DISTINCT [Date]) AS [COUNT(DISTINCT Date)]
FROM
#t
GROUP BY
ID
) AS t1
INNER JOIN(
SELECT
ID,
AVG([SUM(Sales)]) AS basket_size
FROM
(
SELECT
ID,
[Date],
SUM(Sales) AS [SUM(Sales)]
FROM
#t
GROUP BY
ID,
[Date]
) AS tt
GROUP BY
ID
) AS t2 ON
t1.ID=t2.ID
This can be done using OUTER APPLY. Try this,
CREATE TABLE Transactions(ID INT, UPC INT, Sales INT, [Date] Date)
INSERT INTO Transactions(ID, UPC, Sales, [Date])
VALUES
(1, 333, 10, '1/1/2015'),
(1, 222, 20, '1/1/2015'),
(1, 111, 30, '1/1/2015'),
(1, 444, 10, '2/1/2015'),
(1, 555, 20, '2/1/2015'),
(2, 333, 20, '1/1/2015'),
(2, 222, 50, '1/1/2015'),
(2, 111, 30, '1/1/2015'),
(2, 444, 20, '2/1/2015'),
(2, 555, 20, '2/1/2015')
SELECT ID, SalesSum AS [Sum], SalesSum/[COUNT] AS AVG, [COUNT] AS [Count] FROM (
SELECT ID, Sum(Sales) OVER (PARTITION BY ID) AS SalesSum, [COUNT],
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) RN
FROM Transactions
OUTER APPLY
(
SELECT COUNT(DISTINCT [Date]) [COUNT] FROM Transactions
) AS OUTR
) TMP WHERE RN = 1
Sql Fiddle Demo