Return value for MySQL NULL result - mysql

I am working in MySQL. I have the following query:
SELECT tar.ID, COUNT(tsr.StudentID) AS Students
FROM Teacher_ApplicationRecord tar
LEFT JOIN Teacher_StudentRecord tsr ON sar.ID = tsr.TeacherID AND tsr.Session = 1 AND tsr.Year = 2017
WHERE tar.ApplicationYear = 2017
AND tar.Session1 = 1
This query returns no results. However if I take the COUNT(tsr.StudentID) out of the SELECT statement, it returns all the teacher ID's with NULL for the tsr table.
What I want is a query that returns all the teacher ID's and a count of the students assigned to that teacher, with 0 if the result is NULL.
I have tried COALESCE(COUNT(tsr.StudentID), 0) AND IFNULL(COUNT(tsr.StudentID), 0) with no success so far. Any other thoughts?
UPDATE:
The tsr table has 4 columns: TeacherID, StudentID, Year, Session. It has no records yet. It will be populated next year when students are assigned to teachers.
The tar table has a list of TeacherID's in it with some other data, such as year and faculty.
I want my results to look like below:
+-----------+-----------------+
| TeacherID | COUNT(StudentID)|
+-----------+-----------------+
| 1 | 0 |
+-----------+-----------------+
| 2 | 0 |
+-----------+-----------------+
etc.
As students are assigned to teachers, the COUNT(StudentID) numbers will go up. Hope this helps.
UPDATE 2
The tar table looks like this:
+---------+---------------+
|TeacherID|ApplicationYear|
+---------+---------------+
| 1 | 2017 |
+---------+---------------+
| 2 | 2017 |
+---------+---------------+
| 3 | 2017 |
+---------+---------------+
It has other columns but they are not relevant to the question.
The tsr table looks like this:
+---------+---------+----+-------+
|TeacherID|StudentID|Year|Session|
+---------+---------+----+-------+
| 1 | 10 |2017| 1 |
+---------+---------+----+-------+
| 1 | 11 |2017| 1 |
+---------+---------+----+-------+
| 2 | 12 |2017| 1 |

You can try joining the teacher table to the student table (in that order), and then using GROUP BY to count the number of students per teacher:
SELECT tsr.ID,
COUNT(sar.ID) AS numOfStudents -- count no matching students as zero
FROM Teacher_StudentRecord tsr
LEFT JOIN Student_ApplicationRecord sar
ON tsr.TeacherID = sar.ID AND
tsr.Session = 1 AND
tsr.Year = 2017
GROUP BY tsr.ID
The usefulness of a LEFT JOIN here is the edge case where a teacher has no matching students. In this case, the result set, before aggregation happens, would have a single record for that teacher, and the student ID value would be NULL, which be ignored by COUNT, resulting in a correct zero count.
Note that I removed the WHERE clause, whose intended logic is already contained in the ON clause. This WHERE clause was throwing off your results by removing teacher records before they could even be aggregated.
Update:
Please try the following query to see if you get results:
SELECT tsr.ID,
COUNT(sar.ID) AS numOfStudents
FROM Teacher_StudentRecord tsr
LEFT JOIN Student_ApplicationRecord sar
ON tsr.TeacherID = sar.ID
GROUP BY tsr.ID
If this gives you no results, then no teachers are actually connected to any students, and your data has a problem.
Here is a demo using your sample data. It works as expected:
SQLFiddle

Related

How to get list of students who have enrolled atleast once and then final status is not enrolled?

I've following table, It has log of students who enrolled and enrolled out datewise.
student_id | is_enrolled | created_at
-------------------------------------
1 | 1 | 2020-01-01
2 | 0 | 2020-01-02
3 | 0 | 2020-01-01
1 | 0 | 2020-01-02
4 | 1 | 2020-01-02
1 | 0 | 2020-01-03
3 | 0 | 2020-01-03
4 | 1 | 2020-01-04
If you see, the student 1 has enrolled on 2020-01-01 and then enrolled out on 2020-01-02. Student 2 and 3 have never enrolled. Student 4 enrolled multiple times but never enrolled out. Hence, not in the output.
Basically, I want to write a query whose output is students like 1, who have atleast enrolled once and final status is not enrolled. I was able to get all the enrolled students, but stuck after that point.
My queries,
SELECT DISTINCT student_id
FROM student
WHERE is_enrolled = 1
ORDER
BY student_id; # gives me 1 and 4
SQL fiddle
Ideally, a single query solution without nested query would be awesome. I'm, okay with multiple query solution as well.
Note: I was able to get the required output by using for-loops in my code, but I would like to learn can I do this just by SQL queries. I'm not looking for any programming language code.
SELECT DISTINCT x.*
FROM student x
JOIN
( SELECT student_id
, MAX(created_at) created_at
FROM student
GROUP
BY student_id
) y
ON y.student_id = x.student_id
AND y.created_at = x.created_at
JOIN student z
ON z.student_id = x.student_id
AND z.is_enrolled = 1
WHERE x.is_enrolled = 0;
As an aside, never use SELECT *, and in the absence of any aggregating functions, a GROUP BY clause is NEVER appropriate.
I'm not a DBA ( database expert ), but I'll normally use something like this for my MSSQL database:
WITH summary AS (
SELECT
student_id,
is_enrolled,
created_at
ROW_NUMBER() OVER(PARTITION BY s.student_id ORDER BY s.created_at DESC) AS rk
FROM student s)
SELECT s.*
FROM summary s
WHERE s.rk = 1
AND is_enrolled = 1
What I did was adding an extra column after the order by is done, you want to see if the latest created value has an is_enrolled value of 1.
The "With" part is used to define a sub query, with some extra logic in there.
You can use aggreation:
select student_id
from student s
group by student_id
having sum(is_enrolled) >= 1 and
max(created_at) = max(case when is_enrolled = 0 then created_at end);
The first condition checks that the student is enrolled at least once.
The second checks that the latest created_at is the latest created_at for an unenrolled record. That checks that the last status is "unenrolled".
Here is the SQL Fiddle.

How to select MYSQL databases elements without an "array" match?

I have 3 MYSQL tables, one with members information (with ids), one with subscription years (also with ids), and a join table to save every year each member have been subscribed (using years and members ids). I managed to get the list of all the members related to a specific year using the MYSQL code below, but I can't figure out how to get the list of members that are not related to the same years (the ones not included on the first list) using MYSQL code.
I already tried to search for the members without the specific year id, using the code below, but this will return a list with all the members except for the ones that were subscribed only on that year.
The code to extract all the member subscribed on a year knowing its year id (x):
SELECT DISTINCT m.id, first_name, last_name
FROM members_years sc
INNER JOIN members m ON m.id = sc.member_id
WHERE sc.year_id = x
While the nonworking code to extract the list of members not related with a subscription year knowing its year id (x):
SELECT DISTINCT m.id, first_name, last_name
FROM members_years sc
INNER JOIN members m ON m.id = sc.member_id
WHERE sc.year_id != x
To resume I need to extract using a single MYSQL code the list of all members that are not related with a specific subscription year.
Here an example of tables:
Members:
Id | First_name | Last_name
——————————————————
1 | John | Smith
——————————————————
2 | John | Doe
——————————————————
3 | Jane | Doe
Years:
Id | Year
——————
1 | 2013
——————
2 | 2014
——————
3 | 2015
Members_years:
Id | member_id | year_id
———————————————
1 | 1 | 1
———————————————
2 | 1 | 2
———————————————
3 | 2 | 3
———————————————
4 | 3 | 1
———————————————
5 | 3 | 2
———————————————
6 | 3 | 3
With the 3 previous tables, searching with x=3 (2015) with the first code we will get John Doe and Jane Doe, that is fine.
But with second code, using x=3, we get John Smith and Jane Doe instead of only Jonh Smith.
One way to do what you want is with a left join. You want to find all rows which don't have a particular relation for a year x
SELECT m.id, first_name, last_name
FROM members m
LEFT JOIN members_years sc ON (m.id = sc.member_id and sc.year_id = x)
WHERE sc.year_id IS NULL;
So, you LEFT JOIN members_years looking for rows with your target year for each member in the ON clause.
Any member which doesn't have that year would have NULL values from that join, so the WHERE clause looks for those and we end up with just the members you need.
If you're unfamiliar with LEFT JOIN, you might find it instructive to leave off the WHERE clause and include some columns from sc:
SELECT m.id, first_name, last_name, sc.year_id
FROM members m
LEFT JOIN members_years sc ON (m.id = sc.member_id and sc.year_id = x);
You'll get a row for every member - some with a value for year_id, some with NULL.
I think #IVO GELOV’s comment contains the code you are looking for. The reason you are seeing John Doe and Jane Doe pop up in the results of your second query is because both of those users have a record on members_years where year_id Is Not equal to 3. (Although John Doe doesn’t actually have a record where year_id Is Not equal to 3 in your example data, I am assuming that on the actual data you are querying against this is true). Your query needs to instead determine what users DO have a record where year_id Is Equal to 3, and then return every other user besides the users that meet this criteria in your search result.

mysql using limit in a left join not working properly

I have two tables looking like this
Patient (table 1)
id | name
------------
1 | robel
2 | dave
Patient_followup (table 2)
id | Patient_id | date_created
-----------------------
1 | ---- 1 -- | 01/01/2015
2 | -----1 -- | 01/07/2016
I want to display all the patients with their perspective latest followup data. so i tried using this query
Select * from patient
left join Patient_followup pf on pf.Patient_id = patient.id
order by pf.date_created
Limit 1
but this is giving me only the first patient robel. i tryed removing the limit and its giving me two records of robel and one record of dave because robel has two followup data. so what should i do to get only one record of each patient ?
Try this:
Select
*
from
patient
left join
(SELECT
id as pf_id,
MAX(date_created) as latest_followup_date,
Patient_id
FROM
Patient_followup
GROUP BY
Patient_id) as pf
ON pf.Patient_id = patient.id
As mentioned by anton in the first comment, you need to use aggregation to get one record per patient.
Select patient.*,MAX(pf.date_created) as followupdate,group_concat(pf.date_created) from patient
left join Patient_followup pf on pf.Patient_id = p.patient.id
group by patient.id
order by pf.date_created
Here, you will get your values comma separated.
1) "Limit 1" will only return the first result. Typically this is used if the query will result in a very large result set and you only want the first few results.
Ex:
"LIMIT 30" will show the first 30 rows of the query.
2) I would change to setup of the tables so the query is smoother. Right now, you create a new line for each follow-up date even if the patient is already created. You could add another column in the table named "FollowUpDate". That way each patient record has the table id, patient id, creation date and followup date in the same row. That way, each patient has only one row.
EX:
Patient (table 1)
id | name | created_date | next_followup_date |
1 | Robel | 01/01/2015 | 01/01/2016 |
2 | Dave |[created_date]| [next_follup_date] |
Patient_followup (table 2)
id | Patient_id | date_created | followUpDate |
1 | 1 | 01/01/2015 | 06/01/2016 | // example date
2 | 1 | 01/01/2015 | 01/01/2016 |
3 | 2 |[date created]| [FollowUpDate] |
3) Change query to:
Use this select statement to get all patient records.
Select * from patient
left join Patient_followup pf on pf.Patient_id = patient.id
order by pf.Patient_id
Use this select statement to get the specific patient record information.
Select * from patient
inner join Patient_followup pf on pf.Patient_id = patient.id
where patient.id = 1 //to get robel. Edit this line as necessary, perhaps by user input...
order by pf.followUpDate
NOTE: When you insert a new record in Patient_followup, make sure you update Patient.next_followup_date.
I hope this helps!

SQL "chained" queries?

I have two tables.
I am a total newbie to SQL. Using mysql at the moment.
I have the following setup for a school-related db:
Table A contains students records.
Student's id, password,name, lastname and so on.
Table B contains class attendancy records.
Each record goes like this: date, student id, grade
I need to gather all the student info of students that attended classes in a certain date range.
Now, the stupid way would be
first I SELECT all classes from Table B with DATE IN BETWEEN the range
then for each class, I get the student id and SELECT * FROM students WHERE id = student id
What I can't wrap my mind around is the smart way.
How to do this in one query only.
I am failing at understanding the concepts of JOIN, UNION and so on...
my best guess so far is
SELECT students.id, students.name, students.lastname
FROM students, classes
WHERE classes.date BETWEEN 20140101 AND 20150101
AND
classes.studentid = students.id
but is this the appropriate way for this case?
Dont add the join statement in the where clause. Do it like this:
SELECT s.id, s.name, s.lastname,c.date,c.grade
FROM classes c
inner join students s
on c.studentid=s.id
WHERE c.date BETWEEN '01/01/2014' AND '01/01/2015'
This sounds like an assignment so I will attempt to describe the problem and give a hint to the solution.
An example of a union would be;
SELECT students.name, students.lastname
FROM students
WHERE students.lastname IS NOT NULL
UNION
SELECT students.name, 'N/A'
FROM students
WHERE students.lastname IS NULL;
+--------------+--------------+
| name | lastname |
+--------------+--------------+
| John | Doe | <- First two rows came from first query
| Jill | Smith |
| Bill | N/A | <- This came from the second query
+--------------+--------------+
The usual use case for a union is to display the same columns, but munge the data in a different way - otherwise you can usually achieve similar results through a WHERE clause.
An example of a join would be;
SELECT authors.id, authors.name, books.title
FROM authors LEFT JOIN books ON authors.id = books.authors_id
+--------------+--------------+------------------+
| id | name | title |
+--------------+--------------+------------------+
| 1 | Mark Twain | Huckleberry Fin. |
| 2 | Terry Prat.. | Good Omens |
+--------------+--------------+------------------+
^ First two columns from ^ Third column appended
from authors table from books table linked
by "author id"
Think of a join as appending columns to your results, a union is appending rows with the same columns.
In your situation we can rule out a union as you don't want to append more student rows, you want class and student information side by side.

how to find duplicate count without counting original

I need to count the number of duplicate emails in a mysql database, but without counting the first one (considered the original). In this table, the query result should be the single value "3" (2 duplicate x#q.com plus 1 duplicate f#q.com).
TABLE
ID | Name | Email
1 | Mike | x#q.com
2 | Peter | p#q.com
3 | Mike | x#q.com
4 | Mike | x#q.com
5 | Frank | f#q.com
6 | Jim | f#q.com
My current query produces not one number, but multiple rows, one per email address regardless of how many duplicates of this email are in the table:
SELECT value, count(lds1.leadid) FROM leads_form_element lds1 LEFT JOIN leads lds2 ON lds1.leadID = lds2.leadID
WHERE lds2.typesID = "31" AND lds1.formElementID = '97'
GROUP BY lds1.value HAVING ( COUNT(lds1.value) > 1 )
It's not one query so I'm not sure if it would work in your case, but you could do one query to select the total number of rows, a second query to select distinct email addresses, and subtract the two. This would give you the total number of duplicates...
select count(*) from someTable;
select count(distinct Email) from someTable;
In fact, I don't know if this will work, but you could try doing it all in one query:
select (count(*)-(count(distinct Email))) from someTable
Like I said, untested, but let me know if it works for you.
Try doing a group by in a sub query and then summing up. Something like:
select sum(tot)
from
(
select email, count(1)-1 as tot
from table
group by email
having count(1) > 1
)