sqlquery to get missing - sql

I have a table student with following columns
StudentId SemesterId ExamYearId
1 1 1
1 2 2
1 3 3
3 1 1
3 2 2
3 3 4
7 1 1
7 3 4
8 1 1
8 2 2
I want a query to get all such student for whom no data exists for semesterid=3 and examyearid=3, however same student should have data for semesterid=2.
In this case it should return me studentid=8 as studentid=8 has data for semesterid=2 and examyearid=2 however no data for examyearid=3 and semesterid=3.
Basically I want to be able to input semesterid and examyearid and find which studentids have not filled data for that semesterid and examyearid.

DECLARE #SemesterId INT
,#ExamYearId INT
SET #SemesterId = 3;
SET #ExamYearId = 3;
SELECT *
FROM TableName t
WHERE EXISTS (SELECT 1
FROM TableName
WHERE SemesterId = #SemesterId - 1
AND ExamYearId = #ExamYearId - 1
AND t.StudentId = StudentId )
AND NOT EXISTS (SELECT 1
FROM TableName
WHERE SemesterId = #SemesterId
AND ExamYearId = #ExamYearId
AND t.StudentId = StudentId )

SELECT st1.*
FROM Student st1
left join Student st2
on st1.id = st2.id
and st1.Semesterid=2
and st2.Semesterid=3
and st2.Examyearid=3
where st2.id is null

Related

SQL Search for missing record, then insert value

Below is a very oversimplified problem I am trying to solve
I have the following tables:
**quiz**
id title
--------------
1 first
2 second
3 third
4 fourth
5 fifth
**quiz_status**
id status user_id quiz_id
-------------------------------
1 0 1 1
2 0 1 2
3 0 1 3
if a I run the following:
select *
from quiz as q
left join quiz_status as qs
ON q.id = qs.quiz_id
where qs.user_id=1
I'd get:
id title id status user_id quiz_id
-------------------------------------------
1 first 1 0 1 1
2 second 2 0 1 2
3 third 3 0 1 3
4 fourth null null null null
5 fifth null null null null
I would like to be able to insert values where missing/null in the quiz_status table.
so the final outcome would be:
id title id status user_id quiz_id
-------------------------------------------
1 first 1 0 1 1
2 second 2 0 1 2
3 third 3 0 1 3
4 fourth 4 0 1 4
5 fifth 5 0 1 5
What would be the insert statement for that?
Consider the insert ... select syntax:
insert into quiz_status(status, user_id, quiz_id)
select 0, u.user_id, q.id
from (select distinct user_id from quiz_status) u
cross join quiz q
left join quiz_status qz on q.id = qz.quiz_id and u.user_id = qz.user_id
where qz.quiz_id is null
This works by generating all combinations of users and quizs, and then left joining the status table to filter on missing records. In the real life, you would likely have a users table that you can use in place of the select distinct subquery.
If you need just one user it's simpler:
insert into quiz_status(status, user_id, quiz_id)
select 0, 1, q.id
from quiz q
left join quiz_status qz on q.id = qz.quiz_id and qz.user_id = 1
where qz.quiz_id is null
Note: presumably, id is a serial column so I left it apart in the inserts.

How to add extra value to select query result

SubjectMaster
Id Subject
1 English
2 History
3 Maths
UserSubjectAssociation
Id Userid SubjectId
1 1 1
2 1 3
3 2 2
Logs
Id Userid SubjectId Examdate Percentage
1 1 1 02/20/2020 50
2 1 0 Null Null
3 2 1 02/20/2020 70
4 2 2 02/20/2020 60
5 3 0 Null Null
6 4 3 02/18/2020 56
These are my sample tables.
I want to show records from log table of zero as well as all assigned subject of user 1
Suppose user 1 has 2 subject 1 and 3.
Show records from logs where subjectid comes in 0 as well as 1,3
Required Output :
Logs
Id Userid SubjectId Examdate Percentage
1 1 1 02/20/2020 50
2 1 0 Null Null
3 2 1 02/20/2020 70
4 3 0 Null Null
5 4 3 02/18/2020 56
Query :
select * from logs where rdatetime >= '' and subjectid in (select id from subjectmaster where userid = 1)
'Or' did not work.It was giving wrong output.How to handle it.
If I understand correctly, you want a correlated subquery and condition like this:
select l.*
from logs l
where l.subjectid = 0 or
exists (select 1
from subjectmaster sm
where sm.userid = l.userid and
sm.subjectid = l.subjectid
);
You can do left join :
select l.*
from logs l left join
subjectmaster sm
on sm.userid = l.userid and
sm.subjectid = l.subjectid
where not (l.subjectid <> 0 and sm.subjectid is null);
This query will give you the desired result:
select *
from logs l
where subjectid = 0
OR
subjectid IN (select subjectid
from UserSubjectAssociation
where Userid = 1)

Select with limit for the same id

i have 3 tables
Posts_comments
comment_id, comment_post_id, comment_value, comment_time comment_user_id
1 1 test DATETIME 1
2 1 test2 DATETIME 2
3 2 test3 DATETIME 2
4 1 test4 DATETIME 2
5 1 test5 DATETIME 1
6 1 test6 DATETIME 2
Members
member_id member_fistname member_lastname member_slug
1 John Doe john-doe
2 Test User test-user
Members_photos
member_user_id member_photo_type member_photo_name
1 2 test.jpg
2 2 test2.jpg
And i have sql
SELECT
posts_comments.comment_id,
posts_comments.comment_post_id,
posts_comments.comment_value,
posts_comments.comment_time,
members.member_id,
members.member_lastname,
members.member_fistname,
members_photos.member_photo_name
FROM
posts_comments
LEFT JOIN
members ON posts_comments.comment_user_id = members.member_id
LEFT JOIN
members_photos ON members.member_id = members_photos.member_user_id
AND
members_photos.member_photo_type = 2
ORDER BY
posts_comments.comment_time DESC
LIMIT 4
But this query show only last 4 comments independently from the comment_post_id.
In this case i want to show last 4 comments for every comment_post_id (in this example: 4 comments where comment_post_id = 1 and 1 comment where comment_post_id = 2).
I hope I wrote it clearly enough.
Thx 4 help :)
use row_number() window function if it mariadb version MariaDB 10.2.0 or greatere
select a.* from ( SELECT
posts_comments.comment_id,
posts_comments.comment_post_id,
posts_comments.comment_value,
posts_comments.comment_time,
members.member_id,
members.member_lastname,
members.member_fistname,
members_photos.member_photo_name,
row_number()over(partition by posts_comments.comment_post_id order by posts_comments.comment_time desc) rn
FROM
posts_comments
LEFT JOIN
members ON posts_comments.comment_user_id = members.member_id
LEFT JOIN
members_photos ON members.member_id = members_photos.member_user_id
AND
members_photos.member_photo_type = 2
) a where a.rn<=4

Select Command return Duplicate Rows

I have 3 tables:
Table 1 ExamTB:
ID ExamTerm ExamDate
1 MidTerm 2017-09-24
2 FinalTerm 2017-12-01
Table 2 ExamSubMarksTB
ID ExamID ClassID SubjectID TotalMarks PassMarks
1 1 1 1 100 50
2 1 1 2 100 50
3 1 1 3 100 50
4 2 1 1 100 50
5 2 1 2 100 50
6 2 1 3 100 50
Table 3 ExamResultTB
ID ExamID ClassID SubjectID MarksObtain StdID
1 1 1 1 80 1
2 1 1 2 70 1
3 1 1 3 60 1
4 2 1 1 50 1
5 2 1 2 72 1
6 2 1 3 68 1
Now when I create a Stored Procedure the SELECT this Select Command returns duplicate rows
SELECT ExamResultTB.ExamID
, ExamTB.ExamTerm
, ExamTB.ExamDate
, ExamResultTB.StdID
, StudentTB.Name
, StudentTB.FatherName
, ClassTB.ClassName
, SubjectTB.Subject
, ExamResultTB.ObtainMarks
, ExamSubMarksTB.TotalMarks
, ExamSubMarksTB.PassMarks
FROM ExamResultTB
INNER JOIN ExamTB ON ExamResultTB.ExamID = ExamTB.ID
INNER JOIN ClassTB ON ExamResultTB.ClassID = ClassTB.ID
INNER JOIN SubjectTB ON ExamResultTB.SubjectID = SubjectTB.ID
INNER JOIN StudentTB ON ExamResultTB.StdID = StudentTB.ID
INNER JOIN ExamSubMarksTB ON ExamResultTB.ExamID = ExamSubMarksTB.ExamID
WHERE ExamResultTB.ExamID = 4
AND ExamResultTB.StdID=1
For sure this line make a duplicate row:
INNER JOIN ExamSubMarksTB ON ExamResultTB.ExamID = ExamSubMarksTB.ExamID
You should do it in this way:
INNER JOIN ExamSubMarksTB ON ExamResultTB.ExamID = ExamSubMarksTB.ExamID and
ExamResultTB.ClassId = ExamSubMarksTB.ClassId and ExamResultTB.SubjectID =
ExamSubMarksTB.SubjectID

SQL query to get multihierarchy items

in my SQL Table i have following data
ID Level Description Code MasterID
1 1 Cars AD0 NULL
2 1 Trucks JF1 NULL
3 1 Items YU2 NULL
4 2 New Cars AS3 1
5 2 Used Cars TG4 1
6 2 Car parts UJ5 1
7 2 New trucks OL6 2
8 2 Used trucks PL7 2
9 2 Truck parts KJL8 2
10 2 Factory stuff UY9 3
11 2 Custom stuff RT10 3
12 3 Toyota 6YH11 4
13 3 BMW 9OKH12 4
14 3 VW 13 5
15 3 Tiers Type I J14 6
16 3 Tiers Type II J15 6
17 3 Tiers Type III ADS16 9
18 3 Seats SA17 6
19 3 Doors UU18 6
20 3 Lights 9OL19 6
21 4 Left light GH20 20
22 4 Right light H21 20
23 4 Left door JHJ22 19
24 4 Michelin UY23 16
25 4 Vredestein GTF24 17
26 4 Dunlop 25 15
My achievement is to get all hierarchy data for each single item. For Exmaple, the outpu should look like as following
ID Level Description Code MasterId1 Description1 MasterId2 Description2 MasterId3 Description3
24 4 Michelin UY23 16 Tiers Type II 6 Car Parts 1 Cars
.
.
19 3 Doors UU18 6 Car Parts 1 Cars NULL NULL
.
.
10 2 Factory Stuff UY9 3 Items NULL NULL NULL NULL
.
.
3 1 Items NULL NULL NULL NULL NULL NULL NULL
.
.
If somebody can help or give an advise how to achieve this?
This is not dynamic but it could be pretty easily.
Using a recursive cte you can get the hierarchy for the entire table and self join a few times to get the table structure you want.
;WITH cte AS
(
SELECT *, ID AS [RootID], 1 AS [MasterLevel] FROM Table1
UNION ALL
SELECT t1.*, cte.[RootID], cte.[MasterLevel] + 1 FROM Table1 t1
JOIN cte ON t1.ID = cte.MasterID
)
SELECT r.ID, r.[Level], r.[Description], r.[Code],
m1.ID AS MasterId1, m1.[Description] AS Description1,
m2.ID AS MasterId2, m1.[Description] AS Description2,
m3.ID AS MasterId3, m1.[Description] AS Description3
FROM cte r
LEFT JOIN cte m1 ON m1.[RootID] = r.[RootID] AND m1.MasterLevel = 2
LEFT JOIN cte m2 ON m2.[RootID] = r.[RootID] AND m2.MasterLevel = 3
LEFT JOIN cte m3 ON m3.[RootID] = r.[RootID] AND m3.MasterLevel = 4
WHERE r.MasterLevel = 1
ORDER BY r.RootID DESC, r.MasterLevel
This would build a dynamic sql to get master and desciption fields based on the maximum Level value. or you could define how many levels you want to see by changing the #MaxLevel
DECLARE #Sql VARCHAR(MAX) = '',
#SelectSql VARCHAR(MAX) = '',
#JoinSql VARCHAR(MAX) = '',
#MaxLevel INT,
#idx INT = 1
SET #MaxLevel = (SELECT MAX([Level]) FROM Table1)
WHILE #idx < #MaxLevel
BEGIN
SET #SelectSql = #SelectSql + REPLACE(', m<index>.ID AS MasterId<index>, m<index>.[Description] AS Description<index> ', '<index>', #idx)
SET #JoinSql = #JoinSql + REPLACE(' LEFT JOIN cte m<index> ON m<index>.[RootID] = r.[RootID] AND m<index>.MasterLevel = <index> ', '<index>', #idx)
SET #idx = #idx + 1
END
SET #Sql = '
;WITH cte AS
(
SELECT *, ID AS [RootID], 0 AS [MasterLevel] FROM Table1
UNION ALL
SELECT t1.*, cte.[RootID], cte.[MasterLevel] + 1 FROM Table1 t1
JOIN cte ON t1.ID = cte.MasterID
)
SELECT r.ID, r.[Level], r.[Description], r.[Code]' + #SelectSql
+ 'FROM cte r ' + #JoinSql
+ 'WHERE r.MasterLevel = 0
ORDER BY r.RootID DESC, r.MasterLevel'
EXEC(#Sql)