Query to pick departments - sql

I have a table like below:
Dept:
DeptName State Country
D1 Paris France
D2 Lyon France
D3 Lille France
Employee:
EmployeeId EmployeeName DeptName
1 John D1
2 Diesel D2
3 Rock D2
4 John D1
5 Diesel D2
6 Karn D2
7 Triple D1
8 Nancy D3
9 Greg D3
Output:
DeptName CountOfDifferentEmployeeName
D3 2
Get the department name and count of unique employee names for department using following conditions:
Pick only those department which have unique employee names in their country.There should not be same employee name in department in same country
While counting, avoid counting repeated employee names for the same department
But I am confused with whether I should do group by country and zoo name and how do I pick zoo name satisfying condition 1.
select * from Dept d
inner join Employee e on d.DeptName = e.DeptName
group by

If I get your requirement correct, this following script will help you to achieve your desired output-
DEMO HERE
SELECT D.DeptName,D.Country,
COUNT(E.EmployeeName) CountOfDifferentEmployeeName
FROM Employee E
iNNER JOIN Dept D ON E.DeptName = D.DeptName
GROUP BY D.DeptName,D.Country
HAVING COUNT(E.EmployeeName) = COUNT(DISTINCT E.EmployeeName)

I gather you want the names of department that don't have repeated employee name. If that is the case the query below is one way you can do it:
--1 Get per country all department that have repeated employee names
--2 Get the count per country and department of unique employee excluding the county department list in the cte
with Dept as
(
select 'D1' as DeptName, 'Paris' as State , 'France' as Country
union select 'D2' , 'Lyon' , 'France'
union select 'D3' , 'Lille' , 'France'
),
Employee as
(
select 1 as EmployeeId , 'John' as EmployeeName , 'D1' as DeptName
union select 2 , 'Diesel' , 'D2'
union select 3 , 'Rock' , 'D2'
union select 4 , 'John' , 'D1'
union select 5 , 'Diesel' , 'D2'
union select 6 , 'Karn' , 'D2'
union select 7 , 'Triple' , 'D1'
union select 8 , 'Nancy' , 'D3'
union select 9 , 'Greg' , 'D3'
)
select d.DeptName, d.Country, count(*) as unique_employee_names
from Dept d
inner join Employee e on d.DeptName = e.DeptName
where not exists
(
select 1
from (
select d.DeptName, d.Country
from Dept d
inner join Employee e on d.DeptName = e.DeptName
group by d.DeptName, d.Country, e.EmployeeName
having count(*) > 1
) c where d.DeptName = c.DeptName and d.Country = c.Country
)
group by d.DeptName, d.Country
Ouput is:
DeptName Country unique_employee_names
D3 France 2

Related

Using SQL to print data from two different tables but using a third table as a link between the two

I have 3 Tables. EMPLOYEES, LOCATIONS and DEPARTMENTS.
I need to write a SQL statement to display the first_name from EMPLOYEES and the city they live in.
The way that is linked though works like this:
Each employee has an Employee_ID which is found in the EMPLOYEES table and the DEPARTMENTS table
The DEPARTMENTS table has a Location_ID which is also present in the LOCATIONS Table.
I will add a picture to explain it better, but I dont know how the statement should look. Thanks in advance.
You can join:
select e.first_name, l.city
from employees e
inner join departments d on d.employee_id = e.employee_id
inner join locations l on l.location_id = d.location_id
You said it all, just join those tables. Sample data first; query you need begins at line #18.
SQL> with
2 employees (first_name, employee_id) as
3 (select 'Jack', 1 from dual union all
4 select 'Jill', 2 from dual union all
5 select 'Paul', 3 from dual
6 ),
7 departments (employee_id, location_id) as
8 (select 1, 10 from dual union all
9 select 2, 20 from dual union all
10 select 3, 30 from dual
11 ),
12 locations (location_id, city) as
13 (select 10, 'Narnia' from dual union all
14 select 20, 'Valhalla' from dual union all
15 select 30, 'Atlantis' from dual
16 )
17 --
18 select e.first_name, l.city
19 from employees e join departments d on e.employee_id = d.employee_id
20 join locations l on l.location_id = d.location_id
21 order by e.employee_id;
FIRS CITY
---- --------
Jack Narnia
Jill Valhalla
Paul Atlantis
SQL>

Getting department names that have maximum staff count

I have 2 tables, one table storing details of staff (columns are staff_id, staff_name, department_id) and another table storing details of department (columns are department_id, department_name, department_block_num).
I need to write a query to display names of department that has maximum staff count order by department_name. It is also given that multiple department can also have staff count same as maximum staff count. Another condition is group function is not allowed.
Here is code
SELECT department_name
FROM department
WHERE department_id IN (SELECT department_id
FROM ( SELECT department_id, COUNT (*) AS cnt1
FROM staff
WHERE COUNT (*) =
(SELECT cnt
FROM ( SELECT department_id,
COUNT (*) AS cnt
FROM staff
GROUP BY department_id
ORDER BY cnt DESC)
WHERE ROWNUM = 1)
GROUP BY department_id));
You can use the RANK analytic function to find the rows with the maximum counts:
SELECT department_id,
COUNT(*) AS cnt,
RANK() OVER ( ORDER BY COUNT(*) DESC ) AS rnk
FROM staff
GROUP BY department_id
Will rank the rows in order of DESCending count of members of staff and then you can just filter on rows where the rank is 1 (and have the highest count).
Oracle Setup:
CREATE SEQUENCE staff__id__seq;
CREATE TABLE departments (
id INT PRIMARY KEY,
department_name VARCHAR2(20)
);
INSERT INTO departments ( id, department_name )
SELECT 1, 'Aaa' FROM DUAL UNION ALL
SELECT 2, 'Bbb' FROM DUAL UNION ALL
SELECT 3, 'Ccc' FROM DUAL UNION ALL
SELECT 4, 'Ddd' FROM DUAL;
CREATE TABLE staff (
id INT PRIMARY KEY,
department_id INT REFERENCES departments( id )
);
INSERT INTO staff ( id, department_id )
SELECT staff__id__seq.NEXTVAL, department_id
FROM (
SELECT 1 AS department_id FROM DUAL CONNECT BY LEVEL <= 3 UNION ALL
SELECT 2 FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 3 FROM DUAL CONNECT BY LEVEL <= 2 UNION ALL
SELECT 4 FROM DUAL CONNECT BY LEVEL <= 5
);
Query to count staff:
SELECT department_id,
COUNT(*) AS cnt,
RANK() OVER ( ORDER BY COUNT(*) DESC ) AS rnk
FROM staff
GROUP BY department_id
DEPARTMENT_ID | CNT | RNK
------------: | --: | --:
2 | 5 | 1
4 | 5 | 1
1 | 3 | 3
3 | 2 | 4
Query 2 - Get the department name for the highest counts:
Just join the previous query to the departments table and filter to return the rows when the rank is 1 (the highest count).
SELECT d.department_name
FROM (
SELECT department_id,
RANK() OVER ( ORDER BY COUNT(*) DESC ) AS rnk
FROM staff
GROUP BY department_id
) s
INNER JOIN departments d
ON ( d.id = s.department_id )
WHERE s.rnk = 1
Output:
| DEPARTMENT_NAME |
| :-------------- |
| Bbb |
| Ddd |
db<>fiddle here
If you are trying to avoid group by, then use subqueries:
select d.*,
(select count(*)
from staff s
where s.department_id = d.department_id
) as staff_cnt
from department d;
If you then want the top departments, with ties, use subqueries and window functions:
select . . . -- whatever columns you want
from (select d.*,
rank() over (order by staff_cnt desc) as seqnum
from (select d.*,
(select count(*)
from staff s
where s.department_id = d.department_id
) as staff_cnt
from department d
) d
) d
where seqnum = 1;
Assuming this is homework or similar and that windows function are not allowed here is a solution using more basic sql
SELECT department_name
FROM department d
JOIN staff s ON d.department_id = s.department_id
GROUP BY department_name
HAVING COUNT(s.department_id) = (SELECT COUNT(*) as stat
FROM staff
GROUP BY department_id
ORDER BY stat DESC
FETCH FIRST ROW ONLY)
ORDER BY department_name

Get column values separated by semi colon

I have two tables in plsql
tblStudent:-
StudentId Name .........
1 A
2 B
tblDept:-
DeptId DeptName StudentId
1 Dep Aero 1
2 IT 1
3 Dep Maths 1
4 Dep Chemistry 2
I want to get studentId, with all its departments which starts with 'Dep' separated by semi colon, If i pass where StudentId = 1 in SELECT result should look like
StudentId DeptName
1 Dep Aero;Dep Maths
any help please?
You may use LISTAGG to concat and LIKE to filter the records.
SELECT studentid,
LISTAGG(deptname,';') WITHIN GROUP(
ORDER BY deptid
) as deptname
FROM t
WHERE deptname LIKE 'Dep%'
GROUP BY studentid;
If you want an empty list of departments when a student has no entries then you can use an outer join between the two tables, e.g.:
select s.studentid,
listagg(d.deptname, ';') within group (order by d.deptname) as deptnames
from tblstudent s
left join tbldept d on d.studentid = s.studentid
and deptname like 'Dep%'
group by s.studentid;
Demo using CTEs for your sample data, including a third student ID with no matching departments:
-- CTEs for sample data
with tblstudent (studentid, name) as (
select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 3, 'C' from dual
),
tbldept (deptid, deptname, studentid) as (
select 1, 'Dep Aero', 1 from dual
union all select 2, 'IT', 1 from dual
union all select 3, 'Dep Maths', 1 from dual
union all select 4, 'Dep Chemistry', 2 from dual
)
-- actual query
select s.studentid,
listagg(d.deptname, ';') within group (order by d.deptname) as deptnames
from tblstudent s
left join tbldept d on d.studentid = s.studentid
and deptname like 'Dep%'
group by s.studentid;
STUDENTID DEPTNAMES
---------- ------------------------------
1 Dep Aero;Dep Maths
2 Dep Chemistry
3
Your data model looks odd though; you should probably have a department table which only has the department IDs and names, and then another tables that links each student to all of their departments - something like (in CTE form again):
-- CTEs for sample data
with tblstudent (studentid, name) as (
select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 3, 'C' from dual
),
tbldept (deptid, deptname) as (
select 1, 'Dep Aero' from dual
union all select 2, 'IT' from dual
union all select 3, 'Dep Maths' from dual
union all select 4, 'Dep Chemistry' from dual
),
tblstudentdept (studentid, deptid) as (
select 1, 1 from dual
union all select 1, 2 from dual
union all select 1, 3 from dual
union all select 2, 4 from dual
)
-- actual query
select s.studentid,
listagg(d.deptname, ';') within group (order by d.deptname) as deptnames
from tblstudent s
left join tblstudentdept sd on sd.studentid = s.studentid
left join tbldept d on d.deptid = sd.deptid
and deptname like 'Dep%'
group by s.studentid;
STUDENTID DEPTNAMES
---------- ------------------------------
1 Dep Aero;Dep Maths
2 Dep Chemistry
3
Either way, if you only want to see a single student's results when add that as a where clause, right before the group by:
...
where s.studentid = 1
group by s.studentid;
Try this GROUP_CONCAT -
SELECT stud2.studentId,
CAST((SELECT GROUP_CONCAT(CONCAT(dep.depName,'; ') FROM tblDept dep
INNER JOIN tblStudent stud ON (stud.DeptId = dep.DeptId)))
FROM tblStudent stud2
No join is necessary for your query. If you want to do this for a particular student id:
select listagg(d.DeptName, ';') within group (order by d.DeptName)
from tblDept d
where d.studentid = :studentid and
d.DeptName like 'Dep%';
you can alo use this:
select
st.studentid,
listagg(d.DeptName,';') within group( order by d.DeptName )
From tblStudent st
join tblDept d on d.studentid = st.studentid
where DeptName like 'Dep%'
group by st.studentid
sqlFiddle

Numbering of groups in select (Oracle)

I have a following example - table with name, department and country. I need to create a select statement that lists all records and assigns unique number to each group of department and country (column Group in the example):
Name Department Country Group
====== ============ ========= =====
James HR UK 1
John HR UK 1
Alice Finance UK 2
Bob Finance DE 3
Frank Finance DE 3
I thought of some select with analytic function but I found only row_number() over (partition by department, country) which numbers records inside the group and not groups themselves. Do you have any idea how to solve this problem? Thank you!
SELECT t.*, q.grp
FROM (
SELECT q.*, rownum AS grp
FROM (
SELECT DISTINCT department, country
FROM mytable
ORDER BY
department, country
) q
) q
JOIN mytable t
ON t.department = q.department
AND t.country = q.country
or
SELECT t.*, DENSE_RANK() OVER (ORDER BY department desc, country desc) AS grp
FROM mytable
Its a bit clunky, but you could do a a sub query ( or in this case using the with clause ) on the table to get the distinct department per country then get the rownum from that.
set echo on
DROP TABLE TESTXX
DROP TABLE TESTXX succeeded.
CREATE
TABLE TESTXX
(
NAME VARCHAR2 ( 10 )
, DEPARTMENT VARCHAR2 ( 15 )
, COUNTRY VARCHAR2 ( 2 )
)
CREATE succeeded.
INSERT INTO TESTXX VALUES
( 'James', 'HR', 'UK'
)
1 rows inserted
INSERT INTO TESTXX VALUES
( 'John', 'HR', 'UK'
)
1 rows inserted
INSERT INTO TESTXX VALUES
( 'Alice', 'FI', 'UK'
)
1 rows inserted
INSERT INTO TESTXX VALUES
( 'Bob', 'FI', 'DE'
)
1 rows inserted
INSERT INTO TESTXX VALUES
( 'Frank', 'FI', 'DE'
)
1 rows inserted
.
WITH
X AS
(SELECT
XX.*
, ROWNUM R
FROM
(SELECT
DEPARTMENT
, COUNTRY
FROM
TESTXX
GROUP BY
COUNTRY
, DEPARTMENT
ORDER BY
COUNTRY DESC
, DEPARTMENT DESC
) XX
)
SELECT
T.*
, X.R
FROM
TESTXX T
INNER JOIN X
ON
T.DEPARTMENT = X.DEPARTMENT
AND T.COUNTRY = X.COUNTRY
ORDER BY
T.COUNTRY DESC
, T.DEPARTMENT DESC
NAME DEPARTMENT COUNTRY R
---------- --------------- ------- ----------------------
James HR UK 1
John HR UK 1
Alice FI UK 2
Bob FI DE 3
Frank FI DE 3
5 rows selected

TSQL Finding Incomplete courses

From the StudentDetails Table
StudentDetails
SID Name CourseCompleted
1 Andrew CS001
1 Andrew CS002
1 Andrew CS003
2 Grey CS001
2 Grey CS005
2 Grey CS002
3 Jon CS002
3 Jon CS005
3 Jon CS008
How to generate the folowing output ( Course not completed by each student)
SID Name Course Not Completed
1 Andrew CS005
1 Andrew CS008
2 Grey CS003
2 Grey CS008
3 Jon CS001
3 Jon CS003
select distinct a.SID, a.Name, b.CourseCompleted as `Course Not Completed`
from StudentDetails a,
(select distinct CourseCompleted from StudentDetails) b
where not exists
(select 1 from StudentDetails where SID = a.SID and CourseCompleted = b.CourseCompleted)
order by a.SID
select s.SID, s.Name, c.Course as [Course Not Completed]
from (select distinct CourseCompleted [Course] from StudentDetails) c,
StudentDetails s
where not exists (
select * from StudentDetails where SID=s.SID and CourseCompleted=c.Course
)
Of course, if you have a table listing all possible courses, you could replace the subquery in the from clause with that table.
With StudentDetails As
(
SELECT 1 SID, 'Andrew' Name, 'CS001' CourseCompleted union all
SELECT 1, 'Andrew', 'CS002' union all
SELECT 1 , 'Andrew' , 'CS003' union all
SELECT 2 , 'Grey' , 'CS001' union all
SELECT 2 , 'Grey' , 'CS005' union all
SELECT 2 , 'Grey' , 'CS002' union all
SELECT 3 , 'Jon' , 'CS002' union all
SELECT 3 , 'Jon' , 'CS005' union all
SELECT 3 , 'Jon' , 'CS008'
),
Courses AS
(
SELECT DISTINCT CourseCompleted AS Course
FROM StudentDetails
),
Students As
(
SELECT DISTINCT SID, Name
FROM StudentDetails
)
SELECT s.SID, s.name, c.Course AS [Course not Completed] FROM Students s
CROSS JOIN Courses c
EXCEPT
SELECT SID, name, CourseCompleted
FROM StudentDetails
ORDER BY s.SID, c.Course