Compute "Amount" using values from tables - sql

I have the following tables already in my DB
EMP
E_N E_NAM E_RATE E_DEP
--- ----- ---------- -----
1 A 400
2 B 200 1
3 C 150 2
4 D 150 3
5 E 120 1
6 F 100 1
7 G 100 2
8 H 50 2
9 I 50 3
10 J 50 3
11 K 150 3
WORKS
E_NO PR_NO HRS
--- --- ----------
2 1 10
3 2 20
5 1 20
5 2 20
5 3 20
6 1 10
6 2 10
I have to compute the amount billed to each project as AMOUNT, and that is the sum of the amount billed to the project by all employees who work on said project. The amount billed being E_RATE*HRS (product of HRS and E_RATE).
There are only 3 PR_NO: 1, 2 and 3.
I've tried this multiple times with no avail, I know that it has to be a nested query and the calculation to be shown AS AMOUNT, but no clue on how exactly to only display the 3 projects with the calculation already made.

Sounds like simple join and aggregation:
select w.pr_no,
sum(w.hrs * e.e_rate) as amount
from works w
join emp e on w.e_no = e.e_n
group by w.pr_no;

simple aggregate SUM() function after joining the tables
--test data
with EMP(e_no, e_name, e_rate, e_dep) as
(select 1, 'A', 400, null from dual union all
select 2, 'B', 200, 1 from dual union all
select 3, 'C', 150, 2 from dual union all
select 4, 'D', 150, 3 from dual union all
select 5, 'E', 120, 1 from dual union all
select 6, 'F', 100, 1 from dual union all
select 7, 'G', 100, 2 from dual union all
select 8, 'H', 50, 2 from dual union all
select 9, 'I', 50, 3 from dual union all
select 10, 'J', 50, 3 from dual union all
select 11, 'K', 150, 3 from dual),
WORKS(e_no, pr_no, hrs) as
(select 2, 1, 10 from dual union all
select 3, 2, 20 from dual union all
select 5, 1, 20 from dual union all
select 5, 2, 20 from dual union all
select 5, 3, 20 from dual union all
select 6, 1, 10 from dual union all
select 6, 2, 10 from dual)
-- actual query starts here
select w.pr_no, sum(w.hrs*e.e_rate) as amount
from works w
inner join emp e on (w.e_no = e.e_no)
group by w.pr_no;
"PR_NO"|"AMOUNT"
1|5400
2|6400
3|2400

Related

sql function to create sequential for repeated item- [duplicate]

This question already has an answer here:
Add a sequence column in a query
(1 answer)
Closed 7 days ago.
I would like to know if there is an easy sql function to do a sequential classification for each duplicate record as shown in the image:
enter image description here
Looks like one of analytic functions, such as row_number (or perhaps rank).
Sample data:
SQL> with test (product, value) as
2 (select 'A', 100 from dual union all
3 select 'B', 100 from dual union all
4 select 'C', 100 from dual union all
5 select 'A', 100 from dual union all
6 select 'B', 100 from dual union all
7 select 'A', 100 from dual union all
8 select 'D', 100 from dual union all
9 select 'D', 100 from dual union all
10 select 'F', 100 from dual union all
11 select 'F', 100 from dual union all
12 select 'F', 100 from dual
13 )
Query:
14 select product, value,
15 row_number() over (partition by product order by null) classification
16 from test
17 order by product;
PRODUCT VALUE CLASSIFICATION
---------- ---------- --------------
A 100 1
A 100 2
A 100 3
B 100 1
B 100 2
C 100 1
D 100 1
D 100 2
F 100 1
F 100 2
F 100 3
11 rows selected.
SQL>

Calculate Total Runs For Each Over and I Also Want Sum of First and Second Over. Table Structure is Given Below

**Source Table: - Cricket_Scores
**
Over
Balls
Runs
1
1
1
1
2
2
1
3
4
1
4
0
1
5
1
1
6
2
2
1
3
2
2
1
2
3
1
2
4
4
2
5
6
2
6
0
And I Want Output Should be Like Below: -
Over
Runs
1
10
2
25
10 Runs: - (Total Runs of first 6 balls which makes an One over) and
25 Runs: - (Total Runs of first 6 balls + Second 6 balls)
OR
Something Like Below: -
Over
Runs
1
10
2
15
10 Runs: - (Total Runs of first 6 balls which makes an One over) and
15 Runs: - (Total Runs of Second 6 balls)
How to Write a Query In Oracle/SQL Developer.
GROUP BY the overs and SUM the runs to get the runs-per-over:
SELECT over,
SUM(runs) AS runs_per_over
FROM cricket_scores
GROUP BY over
Then use an analytic function to get the running total:
SELECT over,
SUM(SUM(runs)) OVER (ORDER BY over) AS total_runs
FROM cricket_scores
GROUP BY over
Which, for the sample data:
CREATE TABLE cricket_scores( Over, Balls, Runs ) AS
SELECT 1, 1, 1 FROM DUAL UNION ALL
SELECT 1, 2, 2 FROM DUAL UNION ALL
SELECT 1, 3, 4 FROM DUAL UNION ALL
SELECT 1, 4, 0 FROM DUAL UNION ALL
SELECT 1, 5, 1 FROM DUAL UNION ALL
SELECT 1, 6, 2 FROM DUAL UNION ALL
SELECT 2, 1, 3 FROM DUAL UNION ALL
SELECT 2, 2, 1 FROM DUAL UNION ALL
SELECT 2, 3, 1 FROM DUAL UNION ALL
SELECT 2, 4, 4 FROM DUAL UNION ALL
SELECT 2, 5, 6 FROM DUAL UNION ALL
SELECT 2, 6, 0 FROM DUAL;
Outputs:
OVER
TOTAL_RUNS
1
10
2
25
fiddle

Oracle SQL Grouping In Ranges

I am looking for ideas on how to group numbers into low and high ranges in Oracle SQL. I looking to to avoid cursors...any ideas welcome
Example input
ID
LOW
HIGH
A
0
2
A
2
3
A
3
5
A
9
11
A
11
13
A
13
15
B
0
1
B
1
4
B
7
9
B
11
12
B
12
17
B
17
18
Which would result in the following grouping into ranges
ID
LOW
HIGH
A
0
5
A
9
15
B
0
4
B
7
9
B
11
18
This is a Gaps & Islands problem. You can use the traditional solution.
For example:
select max(id) as id, min(low) as low, max(high) as high
from (
select x.*, sum(i) over(order by id, low) as g
from (
select t.*,
case when low = lag(high) over(partition by id order by low)
and id = lag(id) over(partition by id order by low)
then 0 else 1 end as i
from t
) x
) y
group by g
Result:
ID LOW HIGH
--- ---- ----
A 0 5
A 9 15
B 0 4
B 7 9
B 11 18
See running example at db<>fiddle.
From Oracle 12, you should use MATCH_RECOGNIZE for row-by-row pattern matching:
SELECT *
FROM table_name
MATCH_RECOGNIZE(
PARTITION BY id
ORDER BY low, high
MEASURES
FIRST(low) AS low,
MAX(high) AS high
PATTERN (overlapping* last_row)
DEFINE
overlapping AS NEXT(low) <= MAX(high)
)
Which, for the sample data:
CREATE TABLE table_name (id, low, high) AS
SELECT 'A', 0, 2 FROM DUAL UNION ALL
SELECT 'A', 2, 3 FROM DUAL UNION ALL
SELECT 'A', 3, 5 FROM DUAL UNION ALL
SELECT 'A', 9, 11 FROM DUAL UNION ALL
SELECT 'A', 11, 13 FROM DUAL UNION ALL
SELECT 'A', 13, 15 FROM DUAL UNION ALL
SELECT 'B', 0, 1 FROM DUAL UNION ALL
SELECT 'B', 1, 4 FROM DUAL UNION ALL
SELECT 'B', 7, 9 FROM DUAL UNION ALL
SELECT 'B', 11, 12 FROM DUAL UNION ALL
SELECT 'B', 12, 17 FROM DUAL UNION ALL
SELECT 'B', 17, 18 FROM DUAL UNION ALL
SELECT 'C', 0, 10 FROM DUAL UNION ALL
SELECT 'C', 1, 3 FROM DUAL UNION ALL
SELECT 'C', 5, 8 FROM DUAL UNION ALL
SELECT 'C', 9, 15 FROM DUAL UNION ALL
SELECT 'C', 10, 14 FROM DUAL UNION ALL
SELECT 'C', 11, 13 FROM DUAL;
Outputs:
ID
LOW
HIGH
A
0
5
A
9
15
B
0
4
B
7
9
B
11
18
C
0
15
fiddle

Recursive query - Oracle

I have this data and expected result:
Data Expected Result
No A B A B
1 10 500 10 500
2 10 c=20 20 400
3 20 400 30 600
4 30 600 30 700
5 30 c=40 30 800
6 30 c=50 40 700
7 40 700 50 900
8 50 c=60 60 900
9 60 c=70 70 900
10 70 900 10 400
I need to perform a self join and get the result.
For line number 1 the expected result is same as the row.
For line number 2, I need to take the substring of column B (c=20) as 20 and join with column B and get the result as 400.
Lines number 5 and 6 need to substring column B and get the result from column A.
I tried a recursive query, but still am not getting the expected result.
with rec(A, B, nested) as
(
select A, B, case when instr(B, 'C=') != 0 then substr(B, instr(B, 'C=')) as nested
from table
union all
select A, rec.B from table
inner join rec
on (table.A = rec.nested)
)
select A, B, nested from rec;
Answer for the initial version of the question
You do not need a recursive query. To get your desired output you just need to exclude the rows where B starts with c=:
SELECT a, b
FROM table_name
WHERE b NOT LIKE 'c=%';
Which, for the sample data:
CREATE TABLE table_name (no, a, b) AS
SELECT 1, 10, '500' FROM DUAL UNION ALL
SELECT 2, 10, 'c=20' FROM DUAL UNION ALL
SELECT 3, 20, '400' FROM DUAL UNION ALL
SELECT 4, 30, '600' FROM DUAL UNION ALL
SELECT 5, 30, 'c=40' FROM DUAL UNION ALL
SELECT 6, 30, 'c=50' FROM DUAL UNION ALL
SELECT 7, 40, '700' FROM DUAL UNION ALL
SELECT 8, 50, '800' FROM DUAL;
Outputs your desired output:
A
B
10
500
20
400
30
600
40
700
50
800
fiddle
Answer for the 3rd edit of the question
You can use a hierarchical query:
SELECT DISTINCT
CONNECT_BY_ROOT a AS a,
b
FROM table_name
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY
PRIOR b LIKE 'c=%'
AND PRIOR SUBSTR(b, 3) = a
ORDER BY a, b;
Which, for the sample data:
CREATE TABLE table_name (no, a, b) AS
SELECT 1, 10, '500' FROM DUAL UNION ALL
SELECT 2, 10, 'c=20' FROM DUAL UNION ALL
SELECT 3, 20, '400' FROM DUAL UNION ALL
SELECT 4, 30, '600' FROM DUAL UNION ALL
SELECT 5, 30, 'c=40' FROM DUAL UNION ALL
SELECT 6, 30, 'c=50' FROM DUAL UNION ALL
SELECT 7, 40, '700' FROM DUAL UNION ALL
SELECT 8, 50, 'c=60' FROM DUAL UNION ALL
SELECT 9, 60, 'c=70' FROM DUAL UNION ALL
SELECT 10, 70, '900' FROM DUAL;
Outputs:
A
B
10
400
10
500
20
400
30
600
30
700
30
900
40
700
50
900
60
900
70
900
fiddle

How to sum two different fields from two tables with one field is common

I have two tables Sales and Charges.
Tables having data as:
'Sales' 'Charges'
SID F_AMT SID C_AMT
1 100 1 10
1 100 1 10
1 100 1 20
1 200 2 20
2 200 2 10
2 300 3 20
4 300 3 30
4 300 3 10
4 300 5 20
4 200 5 10
I want the output as below:
SID Total_Fees Total_charges
1 500 40
2 500 30
3 0 60
4 1100 0
5 0 30
Assuming you want to do it for the whole tables this is the simplest approach:
Select Sid
, Sum(f_amt) as total_fees
, Sum(c_amt) as total_charges
From ( select sid, f_amt, 0 as c_amt
From sales
Union all
select sid, 0 as f_amt, c_amt
From charges
)
Group by sid
Use full join and nvl():
select sid, nvl(sum(f_amt), 0) fees, nvl(sum(c_amt), 0) charges
from sales s
full join charges c using (sid)
group by sid
order by sid
Demo:
with sales(sid, f_amt) as (
select 1, 100 from dual union all select 1, 100 from dual union all
select 1, 100 from dual union all select 1, 200 from dual union all
select 2, 200 from dual union all select 2, 300 from dual union all
select 4, 300 from dual union all select 4, 300 from dual union all
select 4, 300 from dual union all select 4, 200 from dual ),
charges (sid, c_amt) as (
select 1, 10 from dual union all select 1, 10 from dual union all
select 1, 20 from dual union all select 2, 20 from dual union all
select 2, 10 from dual union all select 3, 20 from dual union all
select 3, 30 from dual union all select 3, 10 from dual union all
select 5, 20 from dual union all select 5, 10 from dual )
select sid, nvl(sum(f_amt), 0) fees, nvl(sum(c_amt), 0) charges
from sales s
full join charges c using (sid)
group by sid
order by sid
Output:
SID FEES CHARGES
------ ---------- ----------
1 1500 160
2 1000 60
3 0 60
4 1100 0
5 0 30
You could use conditional aggregation:
SELECT SID,
COALESCE(SUM(CASE WHEN t=1 THEN AMT END),0) AS Total_Fees,
COALESCE(SUM(CASE WHEN t=2 THEN AMT END),0) AS Total_Charges
FROM (SELECT SID, F_AMT AS AMT, 1 AS t
FROM Sales
UNION ALL
SELECT SID, C_AMT AS AMT, 2 AS t
FROM Charges) sub
GROUP BY SID
ORDER BY SID;
DB Fiddle Demo