Windowed Functions in SQL Server - sql

I have a table called Orders in which the data looks like this:
EMpID OrderValue OrderID
1 100 1
2 167 89
....
There are multiple orders for each empID.
What I want is to get output in this form
EMPID RANK VALUETOTAL VALUETHISEMPID
1 1 300 100
4 2 300 50\
.....
If there are multiple EmpID(s) With same ValueThisEmpID then it should get same rank.
I tried
SELECT EmpID,SUM(val) OVER() as VALUETOTAL,SUM(val) OVER(PARTITION BY EmpID)
How can I obtain rank and order it by ValueThisEmpID?

First some test data:
insert into #t values (1, 10, 100)
insert into #t values (1, 20, 101)
insert into #t values (2, 30, 120)
insert into #t values (3, 10, 130)
insert into #t values (3, 10.5, 131)
insert into #t values (4, 100, 140)
You need two steps, one to get empIds and their summed order value. Step two will be to get the total total and rank:
; with Step1 (EmpId, ValueThisEmpId) as
(select empId, sum(OrderValue)
from #t
group by empId)
select EmpId,
rank() over(order by ValueThisEmpId desc) as "rank",
sum(ValueTHisEmpId) over() as ValueTotal,
ValueThisEmpId
from Step1
This will give output of:
4 1 180.50 100.00
1 2 180.50 30.00
2 2 180.50 30.00
3 4 180.50 20.50
If you don't want gaps in the ranking, use dense rank:
; with Step1 (EmpId, ValueThisEmpId) as
(select empId, sum(OrderValue)
from #t
group by empId)
select EmpId,
dense_rank() over(order by ValueThisEmpId desc) as "rank",
sum(ValueTHisEmpId) over() as TotalValue,
ValueThisEmpId
from Step1

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;

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

SELECT TOP 20 Percent SQL

I have a query which can select TOP 20 percent of TOP highest with GrandTotal. But there is something is not fair. For example, in between the Top 20 out of 10 People is 2. So the out put is show this:
EmpName GrandTotal
Kelvin 50
Gem 40
But the grand total of the 3rd and 4th people also having 40 as Grand Total. I need some idea and advice, how i going to do solve this problem?
SELECT TOP 20 PERCENT
EmpName,
SUM(Scoring) AS GrandTotal
FROM
[masterView]
GROUP BY
EmpName
ORDER BY
GrandTotal DESC, EmpName ASC
On SQL server you can use WITH TIES in order to include ties
SELECT TOP 20 PERCENT WITH TIES Id, sum(Score) as GrandTotal
FROM myTable GROUP BY Id
ORDER BY GrandTotal DESC
SQL Fiddle Demo
Test Data
CREATE TABLE Table1
([ID] int, [Score] int)
;
INSERT INTO Table1
([ID], [Score])
VALUES
(1, 10), (2, 20),
(3, 30), (4, 20),
(5, 10), (6, 40),
(7, 40), (8, 50),
(9, 10), (10, 5);
Query
with ranked as (
select
id,
rank() over (order by Score desc) as rnk
from Table1
),
total as (
select count(*) as total
from Table1
)
SELECT *
FROM ranked
CROSS JOIN total
WHERE ranked.rnk <= 0.2 * total.total
OUTPUT
| id | rnk | total |
|----|-----|-------|
| 8 | 1 | 10 |
| 6 | 2 | 10 |
| 7 | 2 | 10 |

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