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, - sql

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

Related

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.

ORACLE DB query - maximum stipend in faculty

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

SQL SELECT query to pull data from tables to calculate a sum

I have four tables in a database, what I want to do is essentially list the employee number, name department number of any projects they are working on and the total hours that they spend working on projects.
I'm trying to do this all as one single SELECT statement, but am having trouble getting it to work with Oracle SQL. My latest attempt has been
SELECT EMPLOYEE.E#, EMPLOYEE.NAME, SUM(WORKSON.HOURS)
FROM EMPLOYEE JOIN WORKSON ON EMPLOYEE.E#=WORKSON.E#
GROUP BY EMPLOYEE.E#;
Which isn't even the complete statement, and gives me the error:
ERROR at line 1:
ORA-00979: not a GROUP BY expression
What can I do to complete the statement to also include the department number and what should I be doing? Should I be using a join for this? or should I be incorporating another SELECT statement within a WHERE clause?
The tables are as follows:
EMPLOYEE:
SQL> SELECT * FROM EMPLOYEE;
E# NAME DOB S SALARY SUPER D#
----- ------------------ ----------- -- ------ -------- ---
00100 Albert 13-OCT-65 M 186.5
00110 Alvin 13-OCT-77 M 156.4 00100 1
00120 Alice 17-JUN-73 F 156.5 00100 2
00150 Bob 02-JUL-60 M 166.4 00100 3
00200 Carl 02-FEB-67 M 156.3 00100 4
00250 Douglass 14-APR-83 M 156.4 00100 5
00101 Peter 13-NOV-76 M 85.2 00110 1
00103 Ami 12-SEP-85 F 78.2 00110 1
00107 Wendy 12-SEP-88 F 68.2 00110 1
00109 Michael 12-SEP-90 M 58.2 00110 1
00125 Angela 20-NOV-90 F 56.4 00120 2
00105 Robert 15-JAN-86 M 66.2 00150 3
00136 Aban 15-JAN-90 M 55.3 00200 4
00187 Eadger 07-APR-86 M 76.5 00250 5
14 rows selected.
WORKSON:
SQL> SELECT * FROM WORKSON;
E# P# HOURS
----- ------- -----
00110 1001 10
00101 1001 20
00150 1002 10
00105 1002 10
00105 1003 20
00105 1004 20
00250 1004 15
00187 1004 25
00105 1005 15
DEPARTMENT:
SQL> SELECT * FROM DEPARTMENT;
D# DNAME MANAG MSDATE
-- ------------------ ------ ------------
1 SALES 00110 02-JAN-12
2 ACCOUNTING 00120 30-OCT-10
3 GAMES 00150 01-MAR-08
4 HUMAN RESOURCES 00200 02-JAN-13
5 SPORTS 00250 10-MAY-10
PROJECT:
SQL> SELECT * FROM PROJECT;
P# PTITLE SPONSOR D# BUDGET
------ --- -------------- ---------------------- --- -------
1001 Computation Microsoft 1 25000
1002 Study methods Education committee 3 15000
1003 Racing car Cloud Pty Ltd 3 225000
1004 Football Football club 5 35000
1005 Swimming Education committee 5 125000
Any help would be greatly appreciated. Thanks in advance!
You first find the sum of hours of each employee and then join the same with your main table. Inline view can be used for the same.
SELECT EMPLOYEE.E#, EMPLOYEE.NAME, NVL(WORKSON_AGGR.TOTAL_HOURS,0)
FROM EMPLOYEE
LEFT JOIN (SELECT E#, SUM(HOURS) as TOTAL_HOURS
FROM WORKSON
GROUP BY E# ) WORKSON_AGGR
ON (WORKSON_AGGR.E# = EMPLOYEE.E#)
Now, you can join your department table too easily.
SELECT
E.E#,
E.NAME,
NVL(WORKSON_AGGR.TOTAL_HOURS,0),
D.DNAME
FROM EMPLOYEE E
LEFT JOIN (SELECT E#, SUM(HOURS) as TOTAL_HOURS
FROM WORKSON
GROUP BY E# ) WORKSON_AGGR
ON (WORKSON_AGGR.E# = E.E#)
JOIN DEPARTMENT D ON (D.D# = E.D#)

Querying 100k records to 5 records

I have a requirement in such a way that it should join two tables with more than 100k records in one table and just 5 records in another table as shown below
Employee Dept Result
id Name deptid deptid Name Name deptid Name
1 Jane 1 1 Science Jane 1 Science
2 Jack 2 2 Maths Dane 1 Science
3 Dane 1 3 Biology Jack 2 Maths
4 Drack 3 4 Social Drack 3 Biology
5 Drim 5 Zoology Kery 4 Social
6 Drum 5 Drum 5 Zoology
7 Krack
8 Kery 4
.
.
100k
Which join need to be used to get the query in an better way to perform to get the result as shown.
I just want the query to join with other table from employee table only which has dept which i thought of below query but wanted to know is there any better way to do it.
Select e.name,d.deptid,d.Name from
(Select deptid,Name from Employee where deptid IS NOT NULL) A
and dept d where A.deptid=d.deptid;
Firstly not sure why you are performing your query the way you are. Should be more like
SELECT A.name, D.deptid,D.Name
FROM Employee A
INNER JOIN dept D
ON A.deptid = D.deptid
No need of the IS NOT NULL statement.
If this is a ONE TIME or OCCASIONAL thing and performance is key (not a permanent query in your DB) you can leave out the join altogether and do it using CASE:
SELECT
A.name, A.deptid,
CASE
WHEN A.deptid = 1 THEN "Science"
WHEN A.deptid = 2 THEN "Maths"
...[etc for the other 3 departments]...
END as Name
FROM Employee A
If this is to be permanent and performance is key, simply try applying an INDEX on the foreign key deptid in the Employee table and use my first query above.

How does select query works?

I having trouble with this query
it is executing quit well but I cannot make out
how is this select statement working.
Any help or explanation on this problem will be appreciated ..
thank you
these are my tables and query
here am looking for the employee who lives in same city as the the company for which they work
Table:-emp
eid name street city
----------- ---------------- ------------- ------------
1 yeman asd vasai
2 aksh adssd mumbai
3 chintan ghfgh mumbai
4 samual ghfdgh bandra
5 ddlj fghfgh andheri
6 jack fghnfg Bandra
7 bridge gfhfgh vasai
8 rahim ghfgh mumbai
9 chirag fghfghfg bandra
10 mistry hhhty bandra
11 ravi tytey andheri
Table:- company
cid companyname city
----------- ------------------- ------------
1 Vasai Industries vasai
2 Mumbai Pharmacy mumbai
3 bandra loft bandra
4 andheri tactics andheri
Table:= works
eid cid salary
----------- ----------- -----------
1 1 200
2 3 4831
3 4 4457
4 2 20001
5 1 32221
6 2 224
7 3 784
8 1 336
9 3 2489
10 2 4789
11 1 22541
Query
select * from emp
where eid
IN (select eid from works
where cid=(select cid from company
where city=emp.city))
why not use this query with joins and its easy to understand then a bunch of subqueries.
select * from emp
inner join works on works.eid = emp.eid
inner join company on company.city=emp.city
Explanation:
1.select cid from company where city=emp.city
Here you are getting city id regarding cities which are same in emp and company
2.
select eid from works
where cid=(select cid from company
where city=emp.city)
Here you getting collection of id's from works table which cid is same in emp and company
3.
select * from emp
where eid
IN (select eid from works
where cid=(select cid from company
where city=emp.city))
here you are getting all records based on emp id's whose cities are same in emp and city