Ranking functions and Joins - sql

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

Related

How can distribute one table data to another table

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;

Transform rows to columns(probably pivot)

Here is my requirement:
create table #TEMP
(
KEY_VALUE VARCHAR(100)
,NAME VARCHAR(100)
,AMOUNT INT
,QUANTITY INT
)
INSERT INTO #TEMP
VALUES
('K1','ABC',100,10000),
('K2','XYZ',200,20000),
('K1','ABC',50,5000),
('K2','XYZ',300,30000),
('K3','MNO',50,500)
select * from #TEMP
Because the KEY_VALUE COLUMN matches for 2 rows(K1 and K2), I want to transform it to something as below:
KEY_VALUE NAME AMOUNT_1 AMOUNT_2 QUANTITY_1 QUANTITY_2
K1 ABC 100 50 10000 5000
K2 XYZ 200 300 20000 30000
K3 MNO 50 NULL 500 NULL
What/How do I do that? Please let me know if my question is not clear.
You can use ROW_NUMBER() & do conditional aggregation :
SELECT KEY_VALUE, NAME,
MAX(CASE WHEN seq = 1 THEN AMOUNT END) AS AMOUNT_1,
MAX(CASE WHEN seq = 2 THEN AMOUNT END) AS AMOUNT_2,
MAX(CASE WHEN seq = 1 THEN QUANTITY END) AS QUANTITY_1,
MAX(CASE WHEN seq = 2 THEN QUANTITY END) AS QUANTITY_2
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY KEY_VALUE ORDER BY AMOUNT) AS seq
FROM #TEMP t
) t
GROUP BY KEY_VALUE, NAME;
EDIT : If you want to do further calculation then you can use CTE :
WITH CTE AS (
<query>
)
SELECT C.*,
C.AMOUNT_1 - C.AMOUNT_1 AS Diff_Amt
FROM CTE C;
Prepare data
CREATE TABLE #t (
key_value varchar(10),
name varchar(10),
amount int,
quantity int
);
INSERT INTO #t
VALUES
('K1', 'ABC', 100, 10000),
('K2', 'XYZ', 200, 20000),
('K1', 'ABC', 50, 5000),
('K2', 'XYZ', 300, 30000),
('K3', 'MNO', 50, 500);
Querying
WITH t1 (id, key_value, name, amount, quantity)
AS (
SELECT ROW_NUMBER() OVER (ORDER BY key_value), key_value, name, amount, quantity FROM #t
),
t2
AS (
SELECT MIN(id) AS min_id, MAX(id) AS max_id, key_value, name
FROM t1
GROUP BY key_value, name
),
t3
AS (
SELECT t2.key_value, t2.name,
t11.amount AS amount_1, t11.quantity AS quantity_1,
t12.amount AS amount_2, t12.quantity AS quantity_2
FROM t2
INNER JOIN
t1 t11 ON t11.key_value = t2.key_value AND t11.name = t2.name
AND t11.id = t2.min_id
LEFT JOIN
t1 t12 ON t12.key_value = t2.key_value AND t12.name = t2.name
AND t12.id = t2.max_id AND t12.id <> t2.min_id
)
SELECT * FROM t3
Result
key_value name amount_1 quantity_1 amount_2 quantity_2
---------- ---------- ----------- ----------- ----------- -----------
K1 ABC 100 10000 50 5000
K2 XYZ 300 30000 200 20000
K3 MNO 50 500 NULL NULL

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

select based on specific values

I have this table:
ID NO.
111 6
222 7
333 9
111 8
333 4
222 3
111 7
222 5
333 2
I want to select only 2 ID numbers from table where NO. column equal specific values.
For example i tried this query but i didn't get the expected result:
SELECT top 2 * FROM mytable where NO. in
(select NO. from mytable )
Expected result:
111 6
111 8
222 7
222 3
333 9
333 3
You seem to want to select two rows in the table for each id, based on a condition on the No column. For this, one method uses row_number():
select t.*
from (select t.*, row_number() over (partition by id order by id) as seqnum
from mytable t
where <condition goes here>
) t
where seqnum <= 2;
I'm guessing (333,3) is a mistake and you expect (333,2). If not I have no idea.
SELECT
ua.ID
, ua.[NO.]
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY t.[NO.] ASC) AS RowNum
, t.ID
, t.[NO.]
FROM dbo.t1 AS t
UNION ALL
SELECT
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY t.[NO.] DESC)
, ID
, t.[NO.]
FROM dbo.t1 AS t
) ua
WHERE ua.RowNum = 1
ORDER BY ID, ua.[NO.] DESC
If you're just trying to get top 2 values for each group, you need something to define the order, ie. a third column. Then you don't need UNION ALL, just use WHERE ua.RowNum < 3.
/*Select 2 random rows per id where the number of rows per id can vary between 1 and infinity
A good article for this:-*/
--https://www.mssqltips.com/sqlservertip/3157/different-ways-to-get-random-data-for-sql-server-data-sampling/
DECLARE #TABLE TABLE(ID INT,NO INT)
INSERT INTO #TABLE
VALUES
(111, 6),
(222, 7),
(333 , 9),
(111 , 8),
(333 , 4),
(222 , 3),
(111 , 7),
(222 , 5),
(333 , 2)
select t.* from
(
Select s.* ,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY randomnumber) ROWNUMBER
from
(
SELECT ID,NO,
(ABS(CHECKSUM(NEWID())) % 100001) + ((ABS(CHECKSUM(NEWID())) % 100001) * 0.00001) [randomnumber]
FROM #TABLE
) s
) t
where t.rownumber < 3

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