ORACLE DB query - maximum stipend in faculty - sql

So, i have an task in uni to get max stipend in each faculty from a table with stipends.
Faculty table is:
ID_FACULTY FACULTY_NAME DEAN TELEPHON
---------- ------------------------------ -------------------- --------
10 Informacijas tehnologiju Vitols 63023095
11 Lauksaimniecibas Gaile 63022584
12 Tehniska Dukulis 53020762
13 Partikas tehnologijas Sabovics 63021075
Money table is:
ID_PAYOUT STUDENT_ID PAYOUT_DA STIPEND COMPENSATION
---------- ---------- --------- ---------- ------------
100 1 24-SEP-20 45.25 15
101 7 20-SEP-20 149.99 0
102 3 18-SEP-20 100 0
103 17 02-SEP-20 90.85 20
104 9 03-SEP-20 85 20
105 19 09-SEP-20 70.75 0
106 25 15-SEP-20 55 15
107 17 17-SEP-20 105.54 0
108 15 22-SEP-20 94 0
109 27 28-SEP-20 100 20
And the student table is:
ID_STUDENT SURNAME NAME COURSE_YEAR FACULTY_ID BIRTHDATE
---------- ------------------------- -------------------- ----------- ---------- ---------
1 Lapa Juris 4 13 27-SEP-96
3 Vilkauss Fredis 2 10 17-MAY-99
5 Karlsone Rasa 1 11 13-MAR-00
7 Grozitis Guntars 3 12 16-APR-97
9 Sonciks Jurgis 2 10 17-MAR-99
11 Berzajs Olafs 3 10 14-FEB-97
13 Vike Ilvija 2 13 14-MAY-99
15 Baure Inga 3 11 12-APR-97
17 Viskers Zigmunds 2 13 15-AUG-99
19 Talmanis Harijs 3 13 15-JUL-97
21 Livmanis Indulis 1 10 19-JAN-00
23 Shaveja Uva 2 13 18-FEB-98
25 Lacis Guntis 4 10 17-SEP-96
27 Liepa Guna 4 11 18-AUG-96
29 Klava Juris 2 10 19-MAY-98
I have tried many variations of queries, i think that I even tried all the possible combinations of joins, but i cannot achieve the neccessary result.
One of my queries looked like this:
SQL> SELECT ROW_NUMBER() OVER (ORDER BY surname) "Nr.",
f.faculty_name,
s.surname,
s.name,
MAX(m.stipend)
FROM faculty f, student s INNER JOIN money m ON s.id_student = m.student_id
WHERE s.faculty_id = f.id_faculty
GROUP BY f.faculty_name, s.surname, s.name
ORDER BY s.surname;
Which returned me the following result:
Nr. FACULTY_NAME SURNAME NAME MAX(M.STIPEND)
---------- ------------------------------ ------------------------- -------------------- --------------
1 Lauksaimniecibas Baure Inga 94
2 Tehniska Grozitis Guntars 149.99
3 Informacijas tehnologiju Lacis Guntis 55
4 Partikas tehnologijas Lapa Juris 45.25
5 Lauksaimniecibas Liepa Guna 100
6 Informacijas tehnologiju Sonciks Jurgis 85
7 Partikas tehnologijas Talmanis Harijs 70.75
8 Informacijas tehnologiju Vilkauss Fredis 100
9 Partikas tehnologijas Viskers Zigmunds 105.54
9 rows selected.
So the goal of this task is to retrieve the maximum amount of stipend granted to a student in a certain faculty.
Can someone please tell what am I doing wrong here?

Just max amount per faculty:
SELECT
f.faculty_name,
MAX(m.stipend)
FROM
faculty f
INNER JOIN student s ON s.faculty_id = f.id_faculty
INNER JOIN money m ON s.id_student = m.student_id
GROUP BY f.faculty_name
Max amount and all other details too:
SELECT * FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY f.faculty_name ORDER BY m.stipend desc) rn,
f.*,
s.*,
m.*
FROM
faculty f
INNER JOIN student s ON s.faculty_id = f.id_faculty
INNER JOIN money m ON s.id_student = m.student_id
) x
WHERE x.rn = 1
Points of note:
Do not use old style joins; if you ever write one table_name, other_table_name in a FROM block, you're using old style joins. Don't do it; they became bad news about 30 years ago
When you have a max-n-per-group, you specify how finely detailed the group is. If you GROUP BY s.first_name, s.last_name, f.faculty_name then your groups are "every unique combination of firstname/lastname/faculty, so the only way you'll get multiple items in your group is if there are two John Smiths in Mathematics. If the group is to be the whole of mathematics, then the faculty name (and anything else that is uniquely related 1:1 to it, like the faculty ID) is all that you can put in your group. Anything not in a group must be in an aggregation, like MAX
When you want other details too, you either group and max the data and then join this groupmaxed data back to the original data to use it as a filter, or you use an approach like here where you use a row_number or rank, with a partition (which is like an autojoined grouped summary). There is no group here; the row numbering acts like a group because it restarts from 1 every different faculty and proceeds incrementally as stipend decreses. This means that the highest stipend is always in row number 1.
Unlike using a groupmax that you join back to get the detail, the row_number route does not produce duplicate rows with tied-for-highest stipends

Related

Count values separately until certain amount of duplicates SQL

I need a Statement that selects all patients and the amount of their appointments and when there are 3 or more appointments that are taking place on the same date they should be counted as one appointment
That is what my Statement looks so far
SELECT PATSuchname, Count(DISTINCT AKTDATUM) AS AKTAnz
FROM tblAktivitaeten
LEFT OUTER JOIN tblPatienten ON (tblPatienten.PATID=tblAktivitaeten.PATID)
WHERE (AKTDeleted<>'J' OR AKTDeleted IS Null)
GROUP BY PATSuchname
ORDER BY AKTAnz DESC
The result should look like this
PATSuchname Appointments
----------------------------------------
Joey Patner 13
Billy Jean 15
Example Name 13
As you can see Joey Patner has 13 Appointments, in the real table though he has 15 appointments but three of them have the same Date and because of that they are only counted as 1
So how can i write a Statement that does exactly that?
(I am new to Stack Overflow, sorry if the format I use is wrong and tell me if it is.
In the table it looks like this.
tblPatienten
----------
PATSuchname PATID
------------------------
Joey Patner 1
Billy Jean 2
Example Name 3
tblAktivitaeten
----------
AKTDatum PATID AKTID
-----------------------------------------
08.02.2021 1 1000 ----
08.02.2021 1 1001 ---- So these 3 should counted as 1
08.02.2021 1 1002 ----
09.05.2021 1 1003
09.07.2021 2 1004 -- these 2 shouldn't be counted as 1
09.07.2021 2 1005 --
Two GROUP BY should do it:
SELECT
x.PATID, PATSuchname, SUM(ApptCount)
FROM (
SELECT
PATID, AKTDatum, CASE WHEN COUNT(*) < 3 THEN COUNT(*) ELSE 1 END AS ApptCount
FROM tblAktivitaeten
GROUP BY
PATID, AKTDatum
) AS x
LEFT JOIN tblPatienten ON tblPatienten.PATID = x.PATID
GROUP BY
x.PATID, PATSuchname

Select max date for each register, null if does not exists

I have these tables: Employee (id, name, number), Configuration (id, years, licence_days), Periods (id, start_date, end_date, configuration_id, employee_id, period_type):
Employee table:
id name number
---- ----- -------
1 Bob 355
2 John 467
3 Maria 568
4 Josh 871
configuration table:
id years licence_days
---- ----- ------------
1 1 8
2 3 16
3 5 24
Periods table:
id start_date end_date configuration_id employee_id period_type
---- ---------- ------- ---------------- ----------- -----------
1 2021-05-23 2021-05-31 1 1 vaccation
2 2021-05-24 2021-06-01 1 2 vaccation
3 2021-03-01 2021-03-17 2 2 vaccation
4 2021-05-05 2021-05-21 2 2 vaccation
5 2021-01-01 2021-01-17 2 4 vaccation
I want this result:
Result:
employee_id years licence_days max(end_date)
1 1 8 2021-05-31
1 3 16 null
1 5 24 null
2 1 8 2021-06-01
2 3 16 2021-05-21
2 5 24 null
3 1 8 null
3 3 16 null
3 5 24 null
4 1 8 null
4 3 16 2021-01-17
4 5 24 null
i.e., I want to select all Employees with all configuration, and for each one of that, the max end_date of the "vaccation" type (or null if it does not exists).
How can I do that
Oracle supports cross joins, right? So may be something like that?
SELECT e.employee_id, c.years, c.licence_days, max(p.end_date)
FROM Employee e
CROSS JOIN configuration c
LEFT JOIN Periods p
ON e.employee_id = p.employee_id
AND c.configuration_id = p.configuration_id
GROUP BY e.employee_id, c.years, c.licence_days
ORDER BY e.employee_id, c.years
#umberto-petrov chooses wisely with the ANSI CROSS JOIN syntax for a cartesian join. However, in the very weak probability that your requires output of configurations even where there is no employees, you can go with something like :
EDIT: Filtering the Periods join with 'vaccation' as asked in the comments.
If you have to filter for some employee ids, change ON 1 = 1 by ON Employee.id IN (id1, id2, ...). It still keeps every configurations but only takes employees that match the ids.
SELECT Employee.employee_id,
Configuration.years,
Configuration.licence_days,
MAX(Configuration.end_date) max_end_date
FROM Configuration LEFT JOIN Employee ON 1 = 1
LEFT JOIN Periods ON Periods.configuration_id = Configuration.id
AND Periods.employee_id = Employee.id
AND Periods.period_type = 'vaccation'
GROUP BY Employee.employee_id,
Configuration.years,
Configuration.licence_days
ORDER BY Employee.employee_id,
Configuration.years,
Configuration.licence_days
We start from configuration to take every records from this one at least, then made a LEFT CARTESIAN JOIN with Employee and finally a full LET JOIN on Periods for both. That way , if there is no employees, this will output configuration_id and NULL for years, licence_days and max end_date.

joining 2 tables in sql which has no dependency on each other

I have 2 tables in the following way
Table 1:
e_id e_name e_salary e_age e_gender e_dept
---------------------------------------------------
1 sam 95000 45 male operations
2 bob 80000 21 male support
3 ann 125000 25 female analyst
Table 2:
d_salary d_age d_gender e_dept
----------------------------------
34000 25 male Admin
56000 41 female Tech
77000 35 female HR
I want the output something like this:
e_id e_name e_salary e_age e_gender e_dept d_salary d_age d_gender e_dept
1 sam 95000 45 male operations 34000 25 male Admin
2 bob 80000 21 male support 56000 41 female Tech
3 ann 125000 25 female analysts 77000 35 female HR
There is no dependency between the tables. No common columns. No primary or foreign key.
I tried using cross join that results in duplicate rows because it works on M X N
I am new to this SQL thing. Can someone help me, please? Thanks in advance
Though I didn't get the reason behind your desired output but you can get that with below query:
select a.e_id ,a.e_name ,a.e_salary ,a.e_age ,a.e_gender ,a.e_dept,b.d_salary ,b.d_age ,b.d_gender ,b.e_dept
from
(select e_id ,e_name ,e_salary ,e_age ,e_gender ,e_dept, row_number()over(order by e_id)rn
from table1)a
inner join
(select d_salary d_age d_gender e_dept,row_number()over(order by d_salary) rn
from table 2) b
on a.rn=b.rn
Generally you can create a row count using the row_number() window function on both tables and use this as join criterion. But this requires a certain order for both tables, which means that you have explicitly tell the query why is the Admin record ordered first and must be joined on the first record of table 1:
SELECT
*
FROM (
SELECT
*,
row_number() OVER (ORDER BY e_id) as row_count -- assuming e_id is your order criterion
FROM table1
) t1
JOIN (
SELECT
*,
row_number() OVER (ORDER BY /*whatever you expect to be ordered*/) as row_count
FROM table2
) t2
ON t1.row_count = t2.row_count

Write a query to display the names of the department and the staff count in each department, if staff not exist display the count as 0,

The code is running properly giving necessary output but one of the test cases fails.
select department_name,count(*) as staff_count
from department d,staff s
where d.department_id=s.staff_id
group by department_name
order by department_name;
Proposed grade: 50 / 100
Result Description
Failed Test
Test Case 2
Summary of tests
+------------------------------+
| 2 tests run / 1 test passed |
+------------------------------+
Sample output with necessary input data:
DEPARTMENT_NAME STAFF_COUNT
------------------------------ -----------
CSE 2
ECE 1
EEE 2
IT 2
SE 4
DEPARTMENT_ID DEPARTMENT_NAME DEPARTMENT_BLOCK_NUMBER
------------- ------------------------------ -----------------------
1 CSE 3
2 IT 3
3 SE 3
4 ECE 2
5 EEE 2
STAFF_ID STAFF_NAME DEPARTMENT_ID
---------- ------------------------------ -------------
1 Lakshmi 1
2 Venky 1
3 Senthil 2
4 Sandhya 2
5 Geetha 3
6 Tom 3
7 Rekha 3
8 Viji 3
9 Laya 4
10 Nisha 5
11 Venki 5
You need an outer join for this, not an inner join (which your old and outdated implicit join in the WHERE clause does). You also need to do the join between the two department_id columns.
select department_name, count(s.staff_id) as staff_count
from department d
left join staff s on d.department_id = s.department_id
group by department_name
order by department_name;
The left join (=outer join) will return NUL for s.staff_id for departments that do not have staff. Aggregate functions simply ignore NULL values and that's how you get the 0 for the staff count.
However your sample data does not have any department without staff.
Online example with an additional empty department: https://rextester.com/EEXE2322

how to get the unique max data

I have below table
userid comp_dd coursecode qualification course_id course passyear totalmarks func stream
----------- ----------- ----------- --------------- ----------- -------------------------------------------------- ----------- -----------------
60 26 1 High School 15 Class 10 26 67 2 All Subject
60 26 2 Senior Secondry 15 Class 12 26 85 2 Commerce
60 2010 3 Graduates 4 B.Tech/B.E 2010 54 1 IT/Computers
60 2013 4 Post Graduates 9 M.com 2013 98.5 2 Commerce
i wanted to get the unique record of max coursecode, the output should be
userid comp_dd coursecode qualification course_id course passyear totalmarks func stream
----------- ----------- ----------- --------------- ----------- -------------------------------------------------- ----------- -----------------
60 2013 4 Post Graduates 9 M.com 2013 98.5 2 Commerce
There will be many records of the different userids
I think you want:
select t.*
from t
where t.coursecode = (select max(t2.coursecode)
from t t2
where t2.userid = t.userid
);
You can also do this with window functions, but the correlated subquery is often faster with the right index, which would be on (userid, coursecode).
Sort the table by coursecode descending and get the 1st row:
select top 1 *
from tablename
order by coursecode desc
This will work only if there are no duplicates in the column coursecode since it will fetch only 1 row, but I guess this is the case because you say:
i wanted to get the unique record of max coursecode