Cross Join for Missing table (Select all and Select or Insert the Missing Row Only) - sql

I have two table and have to fill in a list of missing values in one of the table based on the other one. First table has student's information and the second table has Grade related info, Grade and Grade description.
Table One
ID Name yearWithUs Grade Course Level
1 Jim 2004 4 4
2 Jim 2004 4 1
2 Jim 2003 3 3
4 Jim 2002 2 3
4 Jim 2002 2 1
3 Jim 2001 1 2
3 Jim 2001 1 1
Table two -- logic is.. A Student in a higher Course Level can change to a lower Course Level at anytime during the semester. And It can only go downward 1 level at a time. Example: Jim in his second grade first was assigned to attend course in level 3. He need to attend course in level 2 first before he can attend course in level 1. Means. Row for course level 2 at jim's first grade is missing.
Table Two
ID Grade Grade_Desc Course Level Course Desc
1 1 First Grade 1 Basic
2 1 First Grade 2 Normal
3 1 First Grade 3 Hard
4 1 First Grade 4 Expert
5 2 Second Grade 1
6 2 Second Grade 2
7 2 Second Grade 3
8 2 Second Grade 4
. . .
. . .
. . .
Logic of Table Two
ID Grade Grade_Desc Course Level Possible Move
1 1 First Grade 1 Null
2 1 First Grade 2 1
3 1 First Grade 3 2
4 1 First Grade 4 3
Ouptput one ... how to use select statement to return Jim's Grade?
ID Name Grade_Desc Grade yerWithUs Course Level
1 Jim Fourth Grade 4 2004 4
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
4 Jim Fourth Grade 4 2004 1
5 Jim Third Grade 3 2003 3
6 Jim Second Grade 2 2002 3
7 Jim Second Grade 2 2002 2
8 Jim Second Grade 2 2002 1
9 Jim First Grade 2 2001 2
10 Jim First Grade 2 2001 1
Output Two..How to retrieve only the missing row into a new temp table?
ID Name Grade_Desc Grade yearWithUs Course Level
2 Jim Fourth Grade 4 2004 3
3 Jim Fourth Grade 4 2004 2
7 Jim Second Grade 2 2002 2
I am currently is using a messy Cursor Statement to do it. The structure looks really messy and hard to debug return errors. I did a lot of research, and saw people use Cross Join to fill the missing portion which looks really clean (See example below)... I have tried the script it myself in many different way by using the cross join example below...obviously, I failed. I found a similar question in stackoverflow..but I am not able to understand how does it work and why without looking at the data....I need help to understand how to use cross join to rerun missing row? and I am open to any other possible solution.
"SELECT calendar.Date,
Category.Cat,
Score = ISNULL(Scores.Score, 0)
FROM Calendar
CROSS JOIN Catogory
LEFT JOIN Scores
ON Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
WHERE Calendar.DayOfMonth = 1;"
Inserting missing rows with a join
Thank You

This will produce that output:
select distinct name, grade, Grade_Desc
from one
cross join two

If select is all you want then:
Select row_number() over(order by (select 1)) as id, * from
(Select distinct name from t1)t1
cross join t2
Here is fiddle http://sqlfiddle.com/#!6/a8a42/3

Try this out:
Create #Temp
DECLARE #Name VARCHAR(100) = 'Jim'
SELECT ROW_NUMBER() OVER (ORDER BY B.Grade DESC,B.CourseLevel DESC) ID,
A.Name,
B.Grade_Desc,
B.Grade,
A.YearWithUs,
B.[Course Level]
INTO #temp
FROM
(
SELECT DISTINCT Name,YearWithUs,Grade
FROM TableOne
WHERE Name = #Name
) A
INNER JOIN TableTwo B
ON A.Grade = B.Grade
Output One
SELECT *
FROM #temp
Output Two into #OutputTwo(temp table)
SELECT A.* INTO #OutputTwo
FROM #temp A
LEFT JOIN TableOne B
ON A.Grade = B.Grade
AND A.[Course Level] = B.[Course Level]
WHERE A.Grade IS NULL AND A.[Course Level] IS NULL

Related

How to select values from table based on field with the same id?

Let's say I have following sql tables:
Table "student"
id
first_name
last_name
1
Lillian
Nees
2
William
Lorenz
3
Mary
Moore
4
Giselle
Collins
5
James
Moultrie
6
John
Rodriguez
Table "exam_result":
exam_result_id
subject_id
student_id
mark
1
2
1
49
2
2
2
21
3
1
3
81
4
4
1
33
5
4
2
19
6
3
2
46
7
1
5
55
8
3
5
75
9
2
5
60
11
1
6
86
12
2
6
92
13
3
6
48
14
4
6
78
I need to select all students, who have all their exam marks <50 or haven't taken any exam at all.
So, in this case I need students with id 1, 2 and 4.
Closest thing I came up with is the following query, but it gives students with id 1, 2, 4 and 6. I don't need a student with id 6, since he has only 1 failed exam, not all of them.
SELECT DISTINCT s.id, s.first_name, s.last_name
FROM university.student s
LEFT JOIN
university.exam_result er ON
s.id = er.student_id
WHERE er.mark < 50 OR er.mark IS NULL;
I need it to work in both PostgreSQL and MariaDB, so I don't want anything specific to them.
The query that works is:
SELECT DISTINCT s.id, s.first_name, s.last_name
FROM university.student s
WHERE NOT EXISTS(SELECT 1 FROM university.exam_result er WHERE s.id = er.student_id AND er.mark > 49);
Thanks, #jarlh for your comment.
I would use a NOT EXISTS condition combined with a check for the marks
select s.*
from student s
where not exists (select *
from exam_result er
where er.student_id = s.id)
or 50 >= all (select er.mark
from exam_result er
where er.student_id = s.id)
One option is doing this with aggregation, by ensuring the count of marks less than 50 is equal to all the count of their marks. When the student has 0 marks, the condition will still be satisfied.
SELECT s.id, s.first_name, s.last_name
FROM student s
LEFT JOIN exam_result er ON s.id = er.student_id
GROUP BY s.id, s.first_name, s.last_name
HAVING COUNT(CASE WHEN er.mark < 50 THEN 1 END) = COUNT(er.mark)
Check the MariaDB demo and PostgreSQL demo.

create pivot table using sql

I have a database called player.db
These database has two tables.
The tables called person and the other is called match.
person table is
Player_ID
Player
Country
1
Lionel Messi
Argentina
2
Luis Suarez
Uruguay
3
Neymar
Brazil
match table is
Match _ID
Game
Player_ID
Date
Season
1
Uruguay-Paraguay
2
5/3/2019
1
2
Uruguay-Chile
2
19/3/2019
1
3
Argentina-Chile
1
22/3/2019
1
4
Brazil-Guyana
3
3/4/2019
1
5
Brazil-USA
3
1/6/2020
2
6
Brazil-Belize
3
3/7/2020
2
7
Brazil-Suriname
3
5/7/2020
2
8
Argentina-USA
1
8/8/2020
2
9
Argentina-Canada
1
3/3/2021
3
10
Argentina-Grenada
1
8/3/2021
3
11
Uruguay-Suriname
2
7/4/2021
3
12
Uruguay-Mexico
2
2/2/2022
4
13
Uruguay-Jamaica
2
4/2/2022
4
14
Brazil-Ecuador
3
5/2/2022
4
My pivot table should look like these:
Season
Player
1
Luis Suarez
2
Neymar
3
Lionel Messi
4
Luis Suarez
I want a sql code which create a pivot table which shows which player played most with topscore in which season year. For example Luis Suarez occured most in season 1.
I started coding in sql, but got not the desired solution
SELECT Player_ID, COUNT(*)FROM match GROUP BY Player_ID HAVING COUNT(*) max
The problem is I got an error and it doesn't create a pivot table which show which player played most in which season.
Join the tables, group by season and player to get the number of matches for each player and use FIRST_VALUE() window function to pick the top player of each season:
SELECT DISTINCT m.Season,
FIRST_VALUE(p.Player) OVER (PARTITION BY m.Season ORDER BY COUNT(*) DESC) Player
FROM match m INNER JOIN person p
ON p.Player_ID = m.Player_ID
GROUP BY m.Season, m.Player_ID;
See the demo.
Your clause HAVING count(*) max may cause the error.
The solution may depend on the relational data base you use. Here is a solution with postgresql :
SELECT DISTINCT ON (Season)
m.season, p.player, count(*) AS count
FROM match AS m
INNER JOIN player AS p ON p.player_ID = m.Player_ID
GROUP BY m.Season, p.Player_ID, p.player
ORDER BY m.Season ASC, count DESC
see dbfiddle

how can I convert this SQL statement with nested IN clauses to a JOIN format

Here are the very simplified versions of my three tables.
Schools_Subjects Table
SchoolId
SubjectId
1
2
5
1
5
6
The above table contains only parent subject IDs.
Subjects Table
SubjectId
Name
ParentSubjectId
1
Science
{NULL}
2
Mathematics
{NULL}
3
Biology
1
4
Physics
1
5
Chemistry
1
6
History
{NULL}
7
Elementary Math
2
8
Calculus
2
Questions Table
QuestionId
Text
SubjectId
1
What is 2 + 2
7
2
Tell me the fastest animal name
3
3
Salt is composed of which two elements
5
4
How to divide 6 apples among 3 students
7
I want to fetch all the questions given a (or multiple) school ID. For example for schoolId:5, I have the below SQL query:
SELECT *
FROM Questions
WHERE SubjectId IN (
SELECT SubjectId
FROM Subjects
WHERE ParentSubjectId IN (
SELECT SubjectId
FROM Schools_Subjects
WHERE SchoolId = 5
)
)
My above query works but I want to change it into a JOIN format query.
I work on SQL Server, but a ANSI-SQL query will be highly appreciated.
If using Mysql:
SELECT Q.*
FROM Questions Q
JOIN (
SELECT S.SubjectId
FROM Subjects S
JOIN Schools_Subjects SS
ON S.ParentSubjectId = SS.SubjectId AND SS.SchoolId = 5
) t1
ON Q.SubjectID = t1.SubjectId
QuestionId Text SubjectId X
1 2 Tell me the fastest animal name 3 NA
2 3 Salt is composed of which two elements 5 NA
Which is the same results produced by your code

Delete Group of Records based on Group Statistic

I have the following two example tables
Teams
Team ID
Team Name
1
Bears
2
Tigers
3
Lions
4
Sharks
Players
Player ID
Name
Team ID
Playtime
1
John
1
5
2
Adam
1
4
3
Smith
1
5
4
Michelle
2
5
5
Stephanie
2
10
6
David
2
10
7
Courtney
2
2
8
Frank
2
7
9
Teresa
2
1
10
Michael
3
3
11
May
4
1
12
Daniel
4
1
13
Lisa
4
4
I need a select statement with the following criteria
Select all teams with less than 4 players
Figure out the total playtime for all players on those teams and sort the resulting table based on this in descending order
Based on the example given, I would want a table that looks like this:
Teams
Team Name
Num Players
Total Playtime
Bears
3
14
Sharks
3
6
Lions
1
3
Finally, I want to cut all even rows from the previous table, so the final table would look like:
Team Name
Num Players
Total Playtime
Bears
3
14
Lions
1
3
You may try the following:
Query #1
SELECT
t."Team Name",
COUNT(p."Player ID") as "Num Players",
SUM(p."Playtime") as "Total Playtime"
FROM
teams t
LEFT JOIN
players p ON t."Team ID"=p."Team ID"
GROUP BY
t."Team Name"
HAVING
COUNT(p."Player ID") < 4
ORDER BY
SUM(p."Playtime") DESC;
Team Name
Num Players
Total Playtime
Bears
3
14
Sharks
3
6
Lions
1
3
Query #2
SELECT
t1."Team Name",
t1."Num Players",
t1."Total Playtime"
FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY SUM(p."Playtime") DESC) as rn,
t."Team Name",
COUNT(p."Player ID") as "Num Players",
SUM(p."Playtime") as "Total Playtime"
FROM
teams t
LEFT JOIN
players p ON t."Team ID"=p."Team ID"
GROUP BY
t."Team Name"
HAVING
COUNT(p."Player ID") < 4
) t1
WHERE MOD(rn,2)=1
ORDER BY rn;
Team Name
Num Players
Total Playtime
Bears
3
14
Lions
1
3
View on DB Fiddle
Let me know if this works for you.

Find missing dates from multi table query

I'm looking to write a query that can count missing entries from a table of dates based on skills that a resource has to forecast availability of resource for booking. I'm not sure if it can be done and I'm certainly struggling with the logic!!
Tables
Dates
ID dateFrom StaffID
1 01-06-2014 1
2 02-06-2014 1
3 03-06-2014 1
4 04-06-2014 1
5 05-06-2014 1
6 01-06-2014 2
7 03-06-2014 2
8 04-06-2014 2
9 05-06-2014 2
10 06-06-2014 2
(Free dates on the 6th for staffID 1 and 2nd for staffID 2)
Staff
StaffID Name
1 John
2 Paul
Skills
ID StaffID SkillID
1 1 1
2 1 2
3 1 3
4 2 2
5 2 3
6 2 4
So I want to write a query that says in June, for each of the skills there is X no of days available to book. Is this even possible? looking for records that don't exist to join with a staff table?
I've put together a calendar table that can identify days without bookings but I'm struggling from there on to be honest.
Any help would be greatly appreciated!!
Steve
EDIT: DB is SQL 2005.
Expected output (if possible)
SkillID Number of days available
1 20
2 22
3 14
etc
create a calendar table with all possible dates (booked or not)
select count(distinct ad.calendarDate), s.SkillID
from all_dates ad
cross join skills s
where not exists (
select 1 from
dates where dateFrom = ad.calendarDate
and StaffID = s.StaffID
)
group by s.SkillID
If I understand your problem, your query will be some thing like:
Select sum(temp.nbrDate), temp.SkillID from
(Select s.SkillID, count (d.ID) as nbrDate from Skills s, Dates d
where s.StaffID = d.StaffID
Group by SkillID) temp
group by SkillID
If you want to add a date range, add this in your where close:
and d.DateForm between '01-06-2014' and '30-06-2014'