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

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

Related

How can i solve blank spots in a SQL View?

In my sqllite-excercises i have discovered the following problem:
I basically have three different Tables:
Subjects
PRIMARY KEY(ID)
Subject
1
Business
2
IT
3
Sports
Participants
PRIMARY KEY(ID)
Name
semester
1
Meyer
6
2
Smith
4
3
Brown
4
4
White
2
5
Anthonie
2
6
Frankson
2
They are referenced in the Table participants List
SUBJECT.ID
Participant.ID
1
2
1
3
1
5
2
4
2
6
3
1
Now im supposted to create a VIEW that contains: The Participants.ID, Participants.Name and Subjects.Subject so i have a Table that shows the ID, the Name and the Subject the participant is visiting.
So far I did this:
CREATE VIEW[OVERVIEW]AS
SELECT Participants.ID,
Participants.Name,
Subjects.Subject
from Participants
LEFT JOIN Subjects on Participants.ID = Subjects.ID;
As a result i get this:
Participants.ID
Participant.Name
Subjects.Subject
1
Meyer
Business
2
Smith
IT
3
Brown
Sports
4
White
None
5
Anthonie
None
6
Frankson
None
And it makes sense since there are only three Subjects and i Leftjoined 6 Participants.ID on only 3 Subjects.Subject
How can i fill out the blank Subjects? So that the subjects for 4-6 are shown aswell?
I hope you can understand my problem and i declared it good enough.
You can't join Participants to Subjects because they are not directly related, so this ON clause:
on Participants.ID = Subjects.ID
does not make sense because the IDs of participants are not related to the IDs of the subjects.
You have the table ParticipantsList that can be used as an intermediate link between the other 2 tables:
SELECT p.ID, p.Name, s.Subject
FROM Participants p
LEFT JOIN ParticipantsList pl ON pl.Participant_ID = p.ID
LEFT JOIN Subjects s ON s.ID = pl.Subject_ID;
This will return all participants, even if they are not linked to any subject.
If you want only the participants for which there is at least 1 subject in the table ParticipantsList then you can use INNER joins.
For the sample data that you provide in your question in both cases the results are the same.
See the demo.

What will be the query to get Student names for every individual study group?

meetings which looks like this:
meeting_ID
place
1
A
2
B
3
C
study_groups which looks like this:
study_group_id
meeting_id (fk)
topic
1
1
Basics
2
2
Photography
3
1
Film
students which looks like this:
student_id
name
1
xyz
2
abc
2
pqr
group_members which looks like this:
study_group_id (fk)
student_id (fk)
2
10
1
1
2
5
3
15
1
9
3
2
3
11
A meeting has many study_groups and each study_groups has many students.
I want to find the student names in each group individually for meeting_id = 1.
Is that possible or not to do in the same query?
If not what should I do, please suggest me.
This is my present SQL query:
SELECT
study_groups.study_group_id,
(SELECT COUNT(*)
FROM group_members
WHERE study_groups.study_group_id = group_members.study_group_id) AS no_of_students,
study_groups.topic
FROM
study_groups
WHERE
study_groups.meeting_id = 1;
Now the table looks like:
study_group_id
no_of_student
topic
1
2
Basics
2
3
Photography
student_name column result in below is demo but I want the data as like this.
The result I expect:
study_group_id
no_of_student
topic
student_name
1
2
Basics
xyz, abc, pqr
2
3
Photography
abc, pqr
If you want the students in the study group and you are using Postgres, you can aggregate them into an array:
SELECT sgs.study_group_id,
(SELECT ARRAY_AGG(s.name)
FROM group_members gm JOIN
students s
ON gm.student_id = s.student_id
WHERE sg.study_group_id = gm.study_group_id
) AS student_names,
sg.topic
FROM study_groups sg
WHERE sg.meeting_id = 1;

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

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

sqlquery insert data from one table to other with id from columnname

I have an old database with some complex joining of the data. As given below
Subjects
Id Name
-------------------------------
1 Math
2 Science
3 English
Results
Id StudentId Math MathMax Science ScienceMax English EnglishMax TotalMarks Max
-----------------------------------------------------------------------------------------
1 81 5 10 6 10 3 10 14 30
2 82 8 10 8 10 9 10 25 30
3 83 7 10 8 10 7 10 22 30
Now I am trying to convert it to more easy and readable database. So I come up with the tables like
Results
ResultId StudentId TotalMarks MaxMarks
-------------------------------------------
1 81 14 30
2 82 25 30
3 83 22 30
ResultDetails
Id ResultId SubjectId Marks MaxMarks
--------------------------------------------------------
1 1 1 5 10
2 1 2 6 10
3 1 3 7 10
& so one
Now the real question I can insert data in the new Results table but I am confused on the second table. I can't understand that how to pass column name of one table and get the id of that name from second table and insert it in the third one.
I am trying on but can't understand the right commands to achieve this. My database already have 50000+ records and I have to merge them according to this new tables.
Assuming this is a one-off conversion of data, and you've already populated your new Results table, something as simple as the following should work:
INSERT INTO ResultDetails(ResultId, SubjectId, Marks, MaxMarks)
SELECT
R.StudentId,
1 AS SubjectId,
OldR.Math AS Marks,
OldR.MathMax AS MaxMarks
FROM Results R
INNER JOIN OldResults OldR ON R.StudentId = OldR.StudentId
INSERT INTO ResultDetails(ResultId, SubjectId, Marks, MaxMarks)
SELECT
R.StudentId,
2 AS SubjectId,
OldR.Science AS Marks,
OldR.ScienceMax AS MaxMarks
FROM Results R
INNER JOIN OldResults OldR ON R.StudentId = OldR.StudentId
INSERT INTO ResultDetails(ResultId, SubjectId, Marks, MaxMarks)
SELECT
R.StudentId,
3 AS SubjectId,
OldR.English AS Marks,
OldR.EnglishMax AS MaxMarks
FROM Results R
INNER JOIN OldResults OldR ON R.StudentId = OldR.StudentId
It's not a very elegant solution, but it doesn't need to be for a one-off conversion.

Is it possible to get all the parent of a particular role in SQL?

I have a hierarchial (parent-child) role based structure to be used for authorization. I have a simple schema, which stores roles along with the id of their parent. Now I am given a role_id, from which I want to get all the parents of that role.
For example, I have a table like this:
ROLE_ID ROLENAME IS_PARENT PARENT_ROLE_ID
1 1 ABC CORP Y NULL
2 2 ABC EC Y 1
3 3 ABC WC Y 1
4 4 ABC NY Y 2
5 5 ABC OH Y 2
6 6 NY ORTH N 4
7 7 NY CARD N 4
8 8 OH ORTH N 5
9 9 OH CARD N 5
Now, when I am provided with a ROLE_ID of 8, the query should return me:
ROLE_ID
1
2
5
8
Since OH ORTH is a child of ABC OH. ABC OH is a child of ABC EC. ABC EC is a child of ABC CORP, I should get 1,5,2 and 1.
In the above example, there are only 4 levels. But, in actual situation, there might be many levels.
I tried googling solution to this problem and stumbled up here. But this is particular to Oracle, and it gives me syntax error when executing queries on Microsoft SQL Server.
Is this possible? I am using Microsoft SQL server on Microsoft Windows Azure.
I have limited knowledge of SQL. Please help
Yes it's possible. You can use recursive CTE for it.
http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
;WITH RCTE AS
(
SELECT ROLE_ID, PARENT_ROLE_ID FROM Table1
WHERE ROLE_ID = 8
UNION ALL
SELECT t.ROLE_ID, t.PARENT_ROLE_ID FROM RCTE r
INNER JOIN Table1 t on r.PARENT_ROLE_ID = t.ROLE_ID
)
SELECT * FROM RCTE
SQLFiddle DEMO