TSQL Finding Incomplete courses - sql-server-2005

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

Related

Query to pick departments

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

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

SQL query over 2 different tables for some common value grouping

I have 2 tables, one for student/class & the other for student/interests as below, for example;
studentID class
---------------------
4134 1
4135 1
4136 1
4137 1
4138 2
4139 2
4140 2
4141 2
studentID interests
---------------------
4134 basketball
4134 football
4135 basketball
4136 basketball
4137 football
4138 swimming
4138 football
4139 running
4140 tennis
4141 tennis
What will be the best way to query about which students have same interests with his classmates ONLY? The hardest part is the requirement of ONLY.
The query should not result in 4135, 4136, 4140 & 4141. As even 4134 has same interest with 4137, both of their interests are same as 4138, who is not in class 1.
I have created two table for your data -
CREATE TABLE #temp (id INT , class INT)
CREATE TABLE #temp2 (id INT, activity VARCHAR(500))
INSERT INTO #temp
SELECT 4134 , 1 UNION ALL
SELECT 4135 , 1 UNION ALL
SELECT 4136 , 1 UNION ALL
SELECT 4137 , 1 UNION ALL
SELECT 4138 , 2 UNION ALL
SELECT 4139 , 2 UNION ALL
SELECT 4140 , 2 UNION ALL
SELECT 4141 , 2
INSERT INTO #temp2
SELECT 4134 , 'basketball' UNION ALL
SELECT 4134 , 'football' UNION ALL
SELECT 4135 , 'basketball' UNION ALL
SELECT 4136 , 'basketball' UNION ALL
SELECT 4137 , 'football' UNION ALL
SELECT 4138 , 'swimming' UNION ALL
SELECT 4138 , 'football' UNION ALL
SELECT 4139 , 'running' UNION ALL
SELECT 4140 , 'tennis' UNION ALL
SELECT 4141 , 'tennis'
AND use select statement
SELECT DISTINCT t.id id
FROM #temp2 t
INNER JOIN #temp2 t1 ON t.activity = t1.activity AND t.id <> t1.id
INNER JOIN #temp t3 ON t.id = t3.id
INNER JOIN #temp t4 ON t1.id = t4.id
WHERE t3.class = t4.class AND t.id NOT IN (SELECT DISTINCT te.id id
FROM #temp2 te
INNER JOIN #temp2 te1 ON te.activity = te1.activity AND te.id <> te1.id
INNER JOIN #temp te3 ON te.id = te3.id
INNER JOIN #temp te4 ON te1.id = te4.id
WHERE te3.class <> te4.class)
This will return -
id
4135
4136
4140
4141
I have tried writing query using joins and partition by just have a look at it
SELECT * FROM
(SELECT STUD_ID,CLASS,INTREST,COUNT(INTREST) OVER(partition BY INTREST) AS RR FROM
(SELECT COUNT(*) OVER( PARTITION BY A.STUD_ID ) CN, B.STUD_ID,A.CLASS,B.INTREST FROM
STUDENTS A
JOIN
INTREST B
ON
A.STUD_ID = B.STUD_ID )
WHERE CN =1 )WHERE RR >1 ;

select multiple columns from mutiple tables

Example.
I have two tables
CAR
id name
1 bmw
2 fiat
Car_info
car_id field_name country
1 year 2000
1 country germany
2 year 1988
2 country italy
How in one select query I can get this?
id name year country
1 bmw 2000 germany
2 fiat 1988 italy
Ideally, you should have a column in the car_info table specifying what type of information is being displayed on that line. i.e. Type 1 is the year, type 2 is the country.
You can get round this by joining on to the car_info table twice specifying an 'AND' condition on the join, like so:
SELECT car.ID, car.Name, ci1.country as [YEAR], ci2.country as COUNTRY
FROM car
INNER JOIN car_info AS ci1 ON Car.ID = ci1.car_id AND ci1.field_name = 'year'
INNER JOIN car_info AS ci2 ON Car.ID = ci2.car_id AND ci2.field_name = 'country'
Try this :
You can use PIVOT and then join with the Car table
with cte as
(
Select car_id,[year],[country]
from
(Select car_id,field_name,countryName
from car_info
)pv
pivot
(
max(countryName)
FOR field_name IN ([year],[country])
)pvt
)
Select c.name,ct.year,ct.country
from car c inner join cte ct
on ct.car_id=c.id
Demo in SQL Fiddle
WITH car AS (
SELECT 1 AS id, 'BMW' AS name FROM dual
UNION ALL
SELECT 2, 'Fiat' FROM dual
)
, car_info AS (
SELECT 1 AS car_id, 'Year' AS field_name, '2000' AS field_val FROM dual
UNION ALL
SELECT 1, 'Country', 'Germany' FROM dual
UNION ALL
SELECT 2, 'Year', '1988' FROM dual
UNION ALL
SELECT 2, 'Country', 'Italy' FROM dual
)
SELECT c.*, car_year.field_val AS yr, car_country.field_val AS country
FROM car c
JOIN car_info car_year ON c.id = car_year.car_id AND car_year.field_name = 'Year'
JOIN car_info car_country ON c.id = car_country.car_id AND car_country.field_name = 'Country'
;

Find Missing Pairs in SQL

Assume there's a relational database with 3 tables:
Courses {name, id},
Students {name, id},
Student_Course {student_id, course_id}
I want to write an SQL that gives me the student-course pairs that do NOT exist. If that is not feasible, at least it'd be good to know if there are missing pairs or not.
Also, since this is a small part of a larger problem I'd like to automate, seeing many different ways of doing it would be useful.
1st find all pairs and then remove pairs present (either by left join/not null or not exists)
select s.id as student_id, c.id as course_id
from Courses as c
cross join Students as s
left join Student_Course as sc on sc.student_id = s.id and sc.course_id = c.id
where sc.course_id is null -- any sc field defined as "not null"
with Courses as(
select 1 as id,'Math' as name union all
select 2 as id,'English' as name union all
select 3 as id,'Physics' as name union all
select 4 as id,'Chemistry' as name),
Students as(
select 1 as id,'John' as name union all
select 2 as id,'Joseph' as name union all
select 3 as id,'George' as name union all
select 4 as id,'Michael' as name
),
studcrse as(
select 1 as studid, 1 as crseid union all
select 1 as studid, 2 as crseid union all
select 1 as studid, 3 as crseid union all
select 2 as studid, 3 as crseid union all
select 2 as studid, 4 as crseid union all
select 3 as studid, 1 as crseid union all
select 3 as studid, 2 as crseid union all
select 3 as studid, 4 as crseid union all
select 3 as studid, 3 as crseid union all
select 4 as studid, 4 as crseid )
SELECT A.ID AS studentId,a.name as studentname,b.id as crseid,b.name as crsename
from Students as a
cross join
Courses as b
where not exists
(
select 1 from studcrse as c
where c.studid=a.id
and c.crseid=b.id)