Avoid Null values in Coalesce - sql

I have used coalesce to produce results but got duplicate rows with Null values.
Sample tables as below:
Table - Student
ID Student Subject id Subject Grade id
100 Peter 200 Chinese 201
101 Mary 300 English 302
102 Sam 400 Maths 403
103 John 900 Music
Chinese Table
Subject id Grade id Grade
200 201 Good
200 202 Average
200 203 Poor
English Table
Subject id Grade id Grade
300 301 Good
300 302 Average
300 303 Poor
Maths Table
Subject id Grade id Grade
400 401 Good
400 402 Average
400 403 Poor
Select Id, Name,
Coalesce (chinese.grade, english.grade, maths.grade)
from Student
Left join Chinese On student.id = Chinese.subjectId AND student.gradeId = Chinese.gradeId
Left join English On student.id = English.subjectId AND student.gradeId = Enlgish.gradeId
Left join Maths On student.id = Maths.subjectId AND student.gradeId = Maths.gradeId
Result
ID Student Subject Grade
100 Peter Chinese Good
100 Peter Chinese NULL
101 Mary English Average
101 Mary English NULL
102 Sam Maths Poor
102 Sam Maths NULL
103 John Music NULL
I am not sure where the duplicated row of Null values came from, just want to know how can I avoid giving null values by using coalesce?

Answering the "just want to know how can I avoid giving null values by using coalesce?" part of the question only, since the origin of the duplicate records requires more information than was provided:
COALESCE returns the first non-null value in its argument list, or NULL if all arguments are NULL.
So Select Id, Name, Coalesce (chinesetable.grade, englishtable.grade, mathstable.grade) ... will still give you a NULL grade if all of chinesetable.grade, englishtable.grade, and mathstable.grade are NULL. You need to decide what you want to do in that situation. You could either skip those rows:
Select * From
(Select Id, Name, Coalesce (chinesetable.grade, englishtable.grade, mathstable.grade) As Grade from Student where id = id...) As grades_with_nulls
Where Grade Is Not Null;
Or provide a default value as the last argument to Coalesce:
Select Id, Name, Coalesce (chinesetable.grade, englishtable.grade, mathstable.grade, 'None') from Student where id = id...

Use:
select s.id,s.name,s.[subject],
coalesce(coalesce(coalesce(c.grade,e.grade,c.grade),e.grade, coalesce(c.grade,e.grade,c.grade)),m.grade,coalesce(coalesce(c.grade,e.grade,c.grade),e.grade, coalesce(c.grade,e.grade,c.grade))) as grade
from student s
left join chinese c on s.subjectid = c.subjectid and c.gradeid = s.gradeid
left join english e on e.subjectid = s.subjectid and e.gradeid = s.gradeid
left join maths m on m.subjectid = s.subjectid and m.gradeid = s.gradeid
result
id--name----subject--grade
100 Peter Chinese Good
101 Mary English Average
102 Sam Maths Poor
103 John Music NULL

Related

SQL Join on Like Operator

I know this question has been asked a couple of times and i've tried to use the solution for my problem. Unfortunately it did not get me the output i wanted. I need to update ID column in one table by joining it to another table where the joining column does not have exact value.
TableA TableB
EmpNo EmpName ID EmpNo EmpName ID TermDate
101 John Doe Null 250termed_101 John Doe 250 11-15-2018
102 Jane Doe Null 251termed_102 Jane Doe 251 02-25-2019
101 Bryan Adams Null 252termed_101 Bryan Adams 252 03-12-2020
Here's what i tried but was unable to get the required output because the below query is giving me duplicates:
select *
from TableA as A left join
TableB as B
on B.EmpNo like '%' + A.EmpNo + '%' and A.EmpNo is not null
Output Required:
EmpNo EmpName ID
101 John Doe 250
102 Jane Doe 251
101 Bryan Adams 252
I need to populate ID column from TableB into TableA by joining these 2 tables on EmpNo. For the first record, John Doe is terminated on 11-15-2018 and his employee number is assigned to Bryan Adams with unique ID. I need to populate the ID column from TableB into Table A for the corresponding employee who had that number at the time.
Thanks in advance
If your problem is truly that you're getting duplicates that you don't want, you can throw a DISTINCT in. But your problem is in the data: Bryan Adams and John Doe both have employee numbers of 101, so they look duplicated when you join to TableB.
This SQL Fiddle might help you: http://sqlfiddle.com/#!18/f30476/10
You seem on the right path, the update statement should look like the following, also notice how I am making the like comparison more accurate:
update A SET
ID = B.ID
from
TableA as A
left join TableB as B on
B.EmpNo like '%_' + A.EmpNo and
A.EmpNo is not null;
that will break when you have 101 and 1101 or 2101, so it's not a good match, so let's revisit:
update A SET
ID = B.ID
from
TableA as A
inner join TableB as B on
RIGHT(B.EmpNo, len(B.EmpNo) - charindex('_', B.EmpNo)) = A.EmpNo
A.EmpNo is not null and --you don't need this,
charindex('_', B.EmpNo) > 0;--needed, otherwise you get string errors

How to inner join same table with different conditions?

I need to get contact information where employee id is null and not null. How do I join the same table with these different conditions. I need the information to populate a report with both employee information and person accompanied them to a event. Here is the query I have so far.
select events.id, (persons.firstname+' '+ persons.lastname) as employee
from events
inner join eventscontacts on events.id = eventcontacts.events_id
inner join contacts on eventcontacts.contact_id = contacts.id
inner join persons on contacts.person_id = person.id
Eventcontacts table
Id ContactType_id contact_id event_id
1 1 1 300
2 2 3 300
Contact type is 1 for employee and 2 for non emplopyees
contacts table
Id person_id employee_id
1 100 200
2 101 201
3 102 NULL
4 103 202
5 104 203
Person table
Id firstname lastname
100 John Stewart
101 Greg Larry
102 Kim Hans
103 Gloria June
104 Dan Duke
Result table
ID employee accompany
300 John Stewart Kim Hans
right now, I have information of all the employees for the event. I want the people who accompanied these person for the events. Their employee id is null in the contacts table. How do I join the contacts table again here?
An inner join will return only the rows that exist in both tables, where it seems like you want all the rows from the contacts table including the rows that don't match due to them lacking an employee id.
If you use an outer join, it will return rows that exist in contacts AND in events like an inner join but ALSO rows that ONLY exist in events and rows that ONLY exist in contacts.
In the case that I am explaining this poorly, I recommend you read this to help explain:
https://mode.com/sql-tutorial/sql-outer-joins/
If you can successfully use an outer join you will get all the visitors in one table regardless of having an id or not.

Querying 100k records to 5 records

I have a requirement in such a way that it should join two tables with more than 100k records in one table and just 5 records in another table as shown below
Employee Dept Result
id Name deptid deptid Name Name deptid Name
1 Jane 1 1 Science Jane 1 Science
2 Jack 2 2 Maths Dane 1 Science
3 Dane 1 3 Biology Jack 2 Maths
4 Drack 3 4 Social Drack 3 Biology
5 Drim 5 Zoology Kery 4 Social
6 Drum 5 Drum 5 Zoology
7 Krack
8 Kery 4
.
.
100k
Which join need to be used to get the query in an better way to perform to get the result as shown.
I just want the query to join with other table from employee table only which has dept which i thought of below query but wanted to know is there any better way to do it.
Select e.name,d.deptid,d.Name from
(Select deptid,Name from Employee where deptid IS NOT NULL) A
and dept d where A.deptid=d.deptid;
Firstly not sure why you are performing your query the way you are. Should be more like
SELECT A.name, D.deptid,D.Name
FROM Employee A
INNER JOIN dept D
ON A.deptid = D.deptid
No need of the IS NOT NULL statement.
If this is a ONE TIME or OCCASIONAL thing and performance is key (not a permanent query in your DB) you can leave out the join altogether and do it using CASE:
SELECT
A.name, A.deptid,
CASE
WHEN A.deptid = 1 THEN "Science"
WHEN A.deptid = 2 THEN "Maths"
...[etc for the other 3 departments]...
END as Name
FROM Employee A
If this is to be permanent and performance is key, simply try applying an INDEX on the foreign key deptid in the Employee table and use my first query above.

How to get the student academic progress?

course Table
course_code course_name credit_points_reqd
1 Comp Science 300
2 Soft Engineering 300
subject Table
subject_code subject_name credit_points
CS123 C Prog 15
CS124 COBOL 15
enrolment table
student_id student_name course_code subject_code Results
1 Lara Croft 1 CS123 70
1 Lara Croft 1 CS124 50
2 Tom Raider 2 CS123 60
2 Tom Raider 2 CS124 40
3 James Bond 1 CS123 NULL
3 James Bond 1 CS124 40
OUTPUT TABLE
student_name course_name credit_points_obt credit_points_reqd
Lara Croft Comp Science 30 300
Tom Raider Soft Engineering 15 300
I'm currently using TSQL. So here's the situation. I've prepared these tables to get the output like the way it i showed u up there. I need to calculate the credit points obtained. Credit points are achieved if the student receives > 50 for a subject they took. I want to ignore students that has not received any credit points at all (eg, James Bond is ignored as he has not achieved any points yet)
select student_name, course_name,credit_points_obt,credit_points_reqd
FROM enrolment (SELECT student_full_name, SUM(credit_points) AS credit_points_obt
FROM enrolment
GROUP BY student_id),
Totally stuck...I have no idea where to go now.
You can sum conditionally to get points for subject. If none are given result will be null, so you filter out those student/course pairs in having clause.
I've changed > 50 condition to >= 50 because your results contradict your requirements. Also, by the data I'd say that you have omitted student table for brewity, but if you haven't, it is a must.
Live test is # Sql Fiddle.
select enrolment.student_name,
course.course_name,
course.credit_points_reqd,
sum(case when enrolment.results >= 50
then subject.credit_points
end) credit_points_obt
FROM enrolment
inner join course
on enrolment.course_code = course.course_code
inner join subject
on enrolment.subject_code = subject.subject_code
group by enrolment.student_name,
course.course_name,
course.credit_points_reqd
having sum(case when enrolment.results >= 50
then subject.credit_points
end) is not null

Oracle Query to retrieve records when one of the conditions is true but not when both are true

I have the following table and I want to retrieve students (STUD_ID) who have taken either ENGLISH or SCIENCE. If they have taken both ENGLISH and SCIENCE, then do not retrieve.
So the desired output is 101,102,104,106,107
The table is actually a View with the first 2 columns from the table STUD_INFO and the subject column are from a Nested table within the STUD_INFO table.
SELECT groupid,
Stud_id,
NST.Name
FROM STUD_INFO,
TABLE(SUBINFO) NST
Can anyone help me with a SQL query? The interesting part is when I use
Subject = ENGLISH and Subject = Science it does not retrieve any data.
groupid Stud_id Subject
------- ------- --------
1 101 ENGLISH
1 102 MATH
1 103 ENGLISH
1 103 SCIENCE
1 104 ENGLISH
1 104 MATH
1 105 PT
1 105 ENGLISH
1 105 SCIENCE
2 106 ENGLISH
2 107 SCIENCE
2 108 SCIENCE
2 108 ENGLISH
Subject = ENGLISH and Subject = SCIENCE says "Subject needs to be both ENGLISH and SCIENCE at the same time" which can never be true.
select stud_id from your_view
where subject in ('ENGLISH', 'SCIENCE')
group by stud_id
having count(subject) = 1
Does this work for you?