How to allocate amount using Oracle SQL in ascending order - sql

I'm not able to build Oracle SQL to Allocate budget amounts to require to projects. I have Budget table and Project tables. I want load into Allocation table as given below.
Budget table:
Category
Budget_Amt
Cat_A
110
Cat_B
20
Cat_C
30
Cat_D
80
Cat_E
100
Project table:
Project
Cost
Proj_1
80
Proj_2
60
Proj_3
50
Proj_4
20
Proj_5
140
Proj_6
30
allocate budget to project in ascending order
Allocation table:
Category
Project
Budget
Cost
Allocation
Cat_A
Proj_1
110
80
80
Cat_A
Proj_2
110
60
30
Cat_B
Proj_2
20
60
20
Cat_C
Proj_2
30
60
10
Cat_C
Proj_3
30
50
20
Cat_D
Proj_3
80
50
30
Cat_D
Proj_4
80
20
20
Cat_D
Proj_5
80
140
30
Cat_E
Proj_5
100
140
100

This is quite a tricky problem. The key idea is to calculate the running budget and running costs -- and then treat this as an overlap between the running amounts.
The following takes this approach:
select p.*, b.*,
least(running_budget_amt + budget_amt, running_cost + cost) - greatest(running_budget_amt, running_cost) as amt
from (select b.*, sum(budget_amt) over (order by category) - budget_amt as running_budget_amt
from budget b
) b join
(select p.*, sum(cost) over (order by project) - cost as running_cost
from project p
) p
on p.running_cost < running_budget_amt + budget_amt and
p.running_cost + cost > running_budget_amt
order by p.project, b.category;
Here is a db<>fiddle.

Related

How to show total sum, running sum and quarterly sum from a table in oracle sql

Data:
NAME AGE SAL MONTH
A 21 100 JAN
B 22 150 FEB
C 21 200 SEP
D 22 100 OCT
EMP 21 150 DEC
Required Output::
1> Normal Sum
NAME AGE SAL SUM
A 21 100 700
B 22 150 700
C 21 200 700
D 22 100 700
EMP 21 150 700
2> Running Sum
NAME AGE SAL RUNNING_SUM
A 21 100 100
B 22 150 250
C 21 200 450
D 22 100 550
EMP 21 150 700
3> Quarterly Sum
NAME AGE SAL MONTH SUM
A 21 100 JAN 250
B 22 150 FEB 250
C 21 200 SEP 200
D 22 100 OCT 250
EMP 21 150 DEC 250
Could someone please help me with the code to achieve the above results. I will be glad for the same.
All of your results can be achieved by using the SUM aggregate function along with a window function.
--Normal Sum
SELECT s.*, SUM (sal) OVER () AS SUM
FROM sum_data s;
--Running Sum
SELECT s.*, SUM (sal) OVER (ORDER BY name) AS SUM
FROM sum_data s;
--Quarterly Sum
SELECT s.*,
SUM (sal)
OVER (
PARTITION BY TO_CHAR (TO_DATE ('2022-' || s.month || '-1', 'YYYY-MON-DD'), 'Q')) AS quarterly_SUM
FROM sum_data s;

Finding Max Price and displaying multiple columns SQL

I have a table that looks like this:
customer_id item price cost
1 Shoe 120 36
1 Bag 180 50
1 Shirt 30 9
2 Shoe 150 40
3 Shirt 30 9
4 Shoe 120 36
5 Shorts 65 14
I am trying to find the most expensive item each customer bought along with the cost of item and the item name.
I'm able to do the first part:
SELECT customer_id, max(price)
FROM sales
GROUP BY customer_id;
Which gives me:
customer_id price
1 180
2 150
3 30
4 120
5 65
How do I get this output to also show me the item and it's cost in the output? So output should look like this...
customer_id price item cost
1 180 Bag 50
2 150 Shoe 40
3 30 Shirt 9
4 120 Shoe 36
5 65 Shorts 14
I'm assuming its a Select statement within a Select? I would appreciate the help as I'm fairly new to SQL.
One method that usually has good performance is a correlated subquery:
select s.*
from sales s
where s.price = (select max(s2.price)
from sales s2
where s2.customer_id = s.customer_id
);

Enumerate records in table on multiple columns

ID descr points
----------------------
1000 24 100
1000 24 40
1000 25 100
1000 25 40
2000 24 100
2000 25 100
2000 26 100
Above is my table. I want to add/update column enumerating records on basis of ID and descr. How can I do that?
Below is the result I am looking for.
ID descr points order#
-------------------------------
1000 24 100 1
1000 24 40 2
1000 25 100 1
1000 25 40 2
2000 24 100 1
2000 25 100 2
2000 26 100 3
You can use the ANSI standard function `row_number():
select id, descr, points,
row_number() over (partition by id, descr order by points desc) as ordernum
from t;

Calculate fixed Cost/day for multiple services on same date

Desired Output table T with Calculated Cost column:
SvcID Code ID Date Mins Units Cost
1 3000 15 4/4/2016 60 10 70
2 3000 17 4/4/2016 45 10 0
3 3000 15 5/2/2016 30 10 70
4 3000 18 5/2/2016 60 10 0
5 3000 10 5/2/2016 30 10 0
6 4200 16 2/1/2016 60 4 60
7 4200 9 2/1/2016 30 2 30
Query for calculating and displaying:
SELECT
...
,CASE
WHEN Code=4200 THEN Units*15
WHEN Code=3000 THEN ?
END AS Cost
FROM ...
WHERE Code IN ('3000','4200')
GROUP BY ....;
Cost should be a total of 70 for all services offered on same date for Code 3000, irrespective of number of services offered. No relation between Minutes and Units for this Code for calculating Cost.
One way could be to calculate cost as 70 for any one service and make the remaining services cost 0 for same date. Can this be done in the CASE statement?
Any better way to achieve this?
You need to Investigate Window functions MSDN.
Your case would become something like this:
-- New select statament
SELECT
...
,CASE
WHEN Code=4200 THEN Units*15
WHEN Code=3000 THEN ( CASE WHEN DuplicateNum = 1 THEN 70 ELSE 0 END )?
END AS Cost
FROM(
-- Your current query (with case statement removed) and ROW_NUMBER() function added
SELECT
..., ROW_NUMBER() OVER( PARTITION BY Code, Date ORDER BY ID ) AS DuplicateNum
FROM ...
WHERE Code IN ('3000','4200')
GROUP BY ....
) AS YourCurrentQuery;

Sqlite substract sums (with group by) with JOIN and duplicates

I previously found the solution to my problem but unfortunately I lost files on my harddrive and I can't find the statement I managed to produce.
I have 2 tables T2REQ and T2STOCK, both have 2 columns (typeID and quantity) and my problem reside in the fact that I can have multiple occurences of SAME typeID in BOTH tables.
What I'm trying to do is SUM(QUANTITY) grouped by typeID and substract the values of T2STOCK from T2REQ but since I have multiple occurences of same typeID in both tables, the SUM I get is multiplied by the number of occurences of typeID.
Here's a sample of T2REQ (take typeID 11399 for example):
typeID quantity
---------- ----------
34 102900
35 10500
36 3220
37 840
11399 700
563 140
9848 140
11486 28
11688 700
11399 390
4393 130
9840 390
9842 390
11399 390
11483 19.5
11541 780
And this is a sample of T2STOCK table :
typeID quantity
---------- ----------
9842 1921
9848 2400
11399 1700
11475 165
11476 27
11478 28
11481 34
11483 122
11476 2
And this is where I'm at for now, I know that the SUM(t2stock.quantity) is affected (multiplied) because of the JOIN 1 = 1 but whatever I tried, I'm not doing it in the right order:
SELECT
t2req.typeID, sum(t2req.quantity), sum(t2stock.quantity),
sum(t2req.quantity) - sum(t2stock.quantity) as diff
FROM t2req JOIN t2stock ON t2req.typeID = t2stock.typeID
GROUP BY t2req.typeID
ORDER BY diff DESC;
typeID sum(t2req.quantity) sum(t2stock.quantity) diff
---------- ------------------- --------------------- ----------
563 140 30 110
11541 780 780 0
11486 28 40 -12
11483 19.5 122 -102.5
9840 390 1000 -610
40 260 940 -680
9842 390 1921 -1531
9848 140 2400 -2260
11399 1480 5100 -3620
39 650 7650 -7000
37 1230 116336 -115106
36 28570 967098 -938528
35 33770 2477820 -2444050
34 102900 2798355 -2695455
You can see that SUM(t2req) for typeID 11399 is correct : 1480
And you can see that the SUM(t2stock) for typeID 11399 is not correct : 5100 instead of 1700 (which is 5100 divided by 3, the number of occurences in t2req)
What would be the best way to avoid multiplications because of multiple typeIDs (in both tables) with the JOIN for my sum substract ?
Sorry for the wall of text, just trying to explain as best as I can since english is not my mother tongue.
Thanks a lot for your help.
You can aggregate before join:
SELECT
t2req.typeID,
t2req.quantity,
t2stock.quantity,
t2req.quantity - t2stock.quantity as diff
FROM
(SELECT TypeID, SUM(Quantity) Quantity FROM t2req GROUP BY TypeID) t2req JOIN
(SELECT TypeID, SUM(Quantity) Quantity FROM t2stock GROUP BY TypeID) t2stock
ON t2req.typeID = t2stock.typeID
ORDER BY diff DESC;
Fiddle sample: http://sqlfiddle.com/#!7/06711/5
You can't do this in a single aggregation:
SELECT
COALESCE(r.typeID, s.typeID) AS typeID,
COALESCE(r.quantity, 0) AS req_quantity,
COALESCE(s.quantity, 0) AS stock_quantity,
COALESCE(r.quantity, 0) - COALESCE(s.quantity, 0) AS diff
FROM (
SELECT rr.typeID, SUM(rr.quantity) AS quantity
FROM t2req rr
GROUP BY rr.typeID
) r
CROSS JOIN (
SELECT ss.typeID, SUM(ss.quantity) AS quantity
FROM t2stock ss
GROUP BY ss.typeID
) s ON r.typeID = s.typeID
ORDER BY 4 DESC;