How to select all joined rows from 2 tables including null - mysql

I currently have 2 tables, vaccination and vaccination_type which can be joined using vaccine_id attribute.
vaccination is basically the joining many-to-many table between vaccination_type and patient. It has 3 attributes: vaccine_id, registration_no and date. Vaccination_type has vaccine_id, vaccine_name.
I want to join these 2 tables and get these entries:
vaccine_id vaccine_name registration_no date
1 influenza 1111 2015-01-15
2 hepatitis B null null
3 polio 1111 2015-01-15
4 hepatitis A 1112 2015-01-15
This means that even the patient has not done hepatitis B vaccination, I still want the entry to be null. But I only want the one to specific registration_no, in this case 1111, in other words I also want the hepatitis A is written as null, as it is not done by 1111. So my expected result if I am dealing with 1111 will be:
vaccine_id vaccine_name registration_no date
1 influenza 1111 2015-01-15
2 hepatitis B null null
3 polio 1111 2015-01-15
4 hepatitis A null null
I have tried using all of the join, but cannot find the one that works perfectly. Any suggestion will be appreciated, thanks!

If you want the information for one registration, then that should be your base table. Then you want to get all vaccination types for that registration. Finally join onto the vaccination table to get the date it was administered, or NULL if that person never got it:
SELECT
registrations.registration_no,
vaccination_type.vaccine_id,
vaccination_type.name,
vaccination.date
FROM
registrations
CROSS JOIN vaccination_type
LEFT JOIN vaccination ON registrations.registration_no = vaccination.registration_no AND vaccination_type.vaccine_id = vaccination.vaccine_id
WHERE
registrations.registration_no = 1111
Fiddle: http://sqlfiddle.com/#!2/e8b2d/5

Use LEFT JOIN it join both tables.
Try this:
SELECT VT.vaccine_id, VT.vaccine_name, V.registration_no
FROM Vaccination_type VT
LEFT JOIN vaccination V ON VT.vaccine_id = V.vaccine_id;

As I now understand the question, you need a listing of all vaccinations against which you will need to see all patients (or a particular patient) to see which vaccinations a patient has and has not taken. Yes, that is possible, but it requires all three tables. The Vaccination_Type table has the list of all vaccinations, the Patients table has the list of all patients. The Vaccinations table has the list of vaccinations that patients have taken.
The first step is to create a Cartesian product of all vaccinations with all patients. This will give you a list of all vaccinations and for each vaccination a list of all patients.
select vt.vaccine_id, p.registration_no
from Vaccination_Type vt
cross join Patients p;
Then left join with the existing cross table.
select vt.vaccine_id, p.registration_no, v.vaccination_date
from Vaccination_Type vt
cross join Patients p
left join Vaccinations v
on v.vaccine_id = vt.vaccine_id
and v.registration_no = p.registration_no;
This gives you all vaccinations with all patients. If the patient has received the vaccination, it will be followed by the date of that vaccination. If not, the date field will be NULL.
If this is something you need periodically, like for reporting, you can create a view of the above query and just select from the view.
If you are only interested in seeing this listing in regards to a particular patient, you can select from the view:
select vaccine_id, registration_no, vaccination_date
from All_Vaccinations
where registration_no = 1111;
However, if a particular patient's vaccination history is mostly what you want, you can obtain that with just two tables with a little better performance:
select vt.vaccine_id, v.vaccination_date
from Vaccination_Type vt
left join Vaccinations v
on v.vaccine_id = vt.vaccine_id
and v.registration_no = 1111;
Notice that registration_no is left out of the select list as we are no longer using the Patients table. We could have used the registration_no field from the Vaccinations table but then if the patient is missing a vaccination, not only the date field but also the registration_no field of the result set would contain NULL. There is no reason to expose a field that A) you already know the value of and B) will have the proper value in some rows and NULL in others. That could lead to some confusion. When you are looking at the data for more than one patient at a time, you must use the three-table join as the registration_no would be required to make sense of the output.

Related

SQL Query, LEFT JOINs with NULLs

I am trying to create a list of hours by Company and Date, even if the Company does not have data for that Date. I've tried LEFT JOINing a Calendar table and Grouping on its dates and then Company, but to no avail.
SELECT cal.date, comp.name, comp.hours
FROM company AS comp
LEFT JOIN calendar AS cal ON cal.date=comp.date
GROUP BY cal.date, comp.name
I expect to get NULL outputs when a Company does not have hours for that Date, like so:
2018-01-01 Company A 100
2018-01-01 Company B NULL
2018-01-02 Company A NULL
2018-01-02 Company B NULL
2018-01-03 Company A 100
2018-01-03 Company B 50
But it only returns rows where data can be found, as if I used an INNER JOIN. Any help would be appreciated. Thank you!
You've made 3 mistakes, of which 2 are relevant:
You swapped the order of the tables in the join (easy to fix)
Your query does not reflect your stated intent (harder to fix)
You are using GROUP BY when it is unnecessary (irrelevant in this particular case)
I'll focus on 2. You're close to your goal but not all there. Fixing the first and third mistakes, here is your query:
SELECT cal.date, comp.name, comp.hours
FROM calendar AS cal
LEFT JOIN company AS comp ON cal.date=comp.date
This is going to do something like the following:
For each cal_row in calendar:
For each comp_row in company:
If cal_row.date equals comp_row.date:
Create output row and append to output
If no rows created:
Create output row, fill with NULLs, and append to output
So your query guarantees at least 1 output row per date. What you are looking for is 1 output row per date, per company. One way to do this is by creating an intermediate table, and left-joining to that:
SELECT tbl.date, tbl.name, comp.hours
FROM (
SELECT DISTINCT cal.date, comp.name
FROM calendar AS cal
CROSS JOIN company as comp
) AS tbl
LEFT JOIN LEFT JOIN company AS comp ON
tbl.date = comp.date AND tbl.name= comp.name
http://sqlfiddle.com/#!9/517b4e/11/0
This matches your desired output.
you can use calendar as a left table,in that case you will get all the date
SELECT cal.date, comp.name, comp.hours
FROM calendar AS cal
LEFT JOIN company AS comp ON cal.date=comp.date
I have not found any aggregate function so i removed group by

How to join any number of tables in MySQL?

I am having a major problem joining 5 tables because each table only has 1 column in common with only 1 other table.
Here are my tables and columns in each table:
TABLE (COLUMNS)
person (person_id, first_name, last name)
building (building_id, building_name)
room (room_id, room_number, building_id, capacity)
meeting (meeting_id, room_id, meeting_start, meeting_end)
person_meeting (person_id, meeting_id)
OK, now here is what I am trying to do (pasted from a homework assignment):
Construct the SQL statement to find all the meetings that person_id #1 has to attend. Display the following columns:
Person’s first name
Person’s last name
Building name
Room number
Meeting start date and time
Meeting end date and time
Now I know how to join 2 tables but I have no idea how to pull info from 5 different tables like this.
I tried looking up how to do this and it just says to do a UNION command, and I am just learning and have yet to cover that.
As UNION is used to combine the result from multiple SELECT statements into a single result set, you don't need it for this scenario. You have to join all the tables one by one based on their Id.
SELECT P.First_Name, P.Last_Name, B.Building_name, R.Room_Number,
M.Meeting_Start, M.Meeting_End FROM Person P
JOIN Person_Meeting PM ON P.Person_Id = PM.Person_Id
JOIN Meeting M ON PM.Meeting_Id = M.Meeting_Id
JOIN Room R ON M.Room_Id = R.Room_Id
JOIN Building B ON R.Building_Id = B.Building_Id
WHERE P.Person_Id = 1

Join based on part of a varchar field matching another field

I am looking join to do a left join on two tables. A string description on one table contains the UID of another table. I want to do the left join only if the description matches the UID. I need to return all records in the Transactions table.
Is it possible to use an IF or conditional statement to join the tables else leave the fields as null?
Here is a fiddle:
http://sqlfiddle.com/#!3/aaa38/5
Transactions table:
posted_date person_id description
2015-11-01 2 BZ#1414 Description 1414
2015-11-01 2 Another type of transaction with spaces in it
2015-11-01 3 BZ#1313 Another description 1313
2015-11-01 3 Another_description_without_spaces
Transaction_details table:
id person_id description
1414 2 additional stuff for 1414
1313 3 additional stuff for 1313
The results should look like:
posted_date person_id description id person_id description
2015-11-01 2 BZ#1414 Des... 1414 2 additional...
2015-11-01 2 Another typ... NULL NULL NULL
2015-11-01 3 BZ#1313 Ano... 1313 3 additional...
2015-11-01 3 Another_des... NULL NULL NULL
Joining like that really means that your Transactions table structure is not good (there should be a separate column for a field you could use to join to Transaction_details).
Having said that, you can still achieve what you want. You can join when the description starts with BZ# and then join on what follows, taking the length of id to extract the number. This is better than trying to find a space in the description as there may not be any spaces.
Left join will do the rest and fill the fields from tran_details with nulls if there was no match.
select *
from
transactions t
left join tran_details d
on t.person_id = d.person_id
and left(t.description, 3) = 'BZ#'
and substring(t.description, 4, len(cast(d.id as nvarchar(50)))) = d.id
I assumed that you want to join if BZ# is followed by the id from the detail table.
SQL Fiddle
The below query will work, as long as the first 3 chars are 'BZ#' in Transaction.Description column
select * from Transactions t left outer join Transaction_details td on t.person_id = td.person_id and left(t.description, 3) = 'BZ#' and
REPLACE(SUBSTRING(t.description, 0, CHARINDEX(' ',t.description, 0)), 'BZ#', '') = td.id

MySQL, Show value of column if match exist else leave as null

I'm sure this has been asked before but can't find the answer.
I have 3 tables OWNER, CAR, HOUSE
OWNER has 2 columns id and name
CAR has 3 columns id, ownerId and cartype
HOUSE has 4 columns id, ownerId, address, country
I want to write a SQL query that gets the owners name, cartypes, and addresses that are in Sweden
Here comes the tricky part. I want all the owners names and cartypes in the result table even if they don't own a house in Sweden. Can I get all that in 1 query or do I need to use 2? How would that query look?
You should be able to accomplish this with a simple left join:
SELECT O.name, C.cartype, H.address, H.country
FROM OWNER AS O
JOIN CAR AS C ON O.id = C.ownerid
LEFT JOIN HOUSE AS H ON O.id = H.ownerid AND Ucase(H.country) = "SWEDEN"
This will always give you a list of owners and their car types, in addition, it will give you a list of those that also happen to have a house address in sweden.
First you need to join the table then add new column in query by using CASE to check
SELECT o.* , c.* ,h.*,
(CASE WHEN h.county ='sweden' THEN h.county ELSE NULL END) AS HasCountry
FROM OWNER o
JOIN CAR c ON (c.ownerId =o.id)
JOIN HOUSE h ON (h.ownerId =o.id)

IF ELSE statement to join table sql

I have one supertype table where I have to pick 1 subtype table from 2 subtypes a,b. A subtype cannot go with the other one so for me to query I have to check whether if the supertype id is contained on one of the subtypes. I have been doing experiment queries but cannot get it right.
This is what somehow I thought of:
SELECT * from supertypetable INNER JOIN
IF (a.id = given.id) then a ON a.id = supertypetable.id
ELSE b ON b.id = supertetable.id
job Table
________________________________
|job_id| blach2x....
________________________________
| 1 |
| 2 |
| 3 |
________________________________
partime Table
________________________________
|job_id| blach2x....
________________________________
| 2 |
| 3 |
________________________________
fulltime Table
________________________________
|job_id| blach2x....
________________________________
| 1 |
| |
________________________________
I want to join tables that satisfy my given id
This looks a lot like a polymorphic join in rails/activerecord. The way it's implemented there, the 'supertype' table has two fields: subtype_id and subtype_type. The subtype_type table has a string that can be easily turned into the name of the right subtype table; subtype_id has the id of the row in that table. Structuring your tables like this might help.
The next question you have to ask is what exactly are you expecting to see in the results? If you want to see the supertype table plus ALL of the subtype tables, you're probably going to have to join them one at a time, then union them all together. In other words, first join against just one of the subtype tables, then against the next one, etc. If this isn't what you're going for, maybe you could clarify your question further.
If a.id can never equal b.id you could do joing on both tables and then do a UNION and only the table where the id matched would return results:
SELECT * from supertypetable
INNER JOIN
a ON a.id = supertypetable.id
UNION
SELECT * from supertypetable
INNER JOIN
b ON b.id = supertypetable.id
If a.id can equal b.id, then this would not work. But it's an idea
EDITTING PER COMMENTS:
This approach only works if the structures of a and b are identical.
So one simple suggestion might be just:
SELECT * FROM job
left join parttime on parttime.job_id = job.job_id
left join fulltime on fulltime.job_id = job.job_id
where job.job_id = #job_id
And then let your application figure out which of the two joined tables doesn't have NULL data and display that.
If you don't mind inconsistent datasets and just always want the correct returned set regardless (although you're still going to need some kind of application logic since as you said, the structures of parttime and fulltime are different, so how are you going to display/utilize their data conditionally without some kind of inspection? And if you're going to do that inspection, you might as well do it up front, figure out for your given job_id what the subtype is, and then just pick the appropriate query to run there.)
Sorry! Digression!
A stored procedure can do this logic for you (removed all the joins, just an example):
CREATE PROCEDURE getSubTypeDATA (#job_id int)
BEGIN
IF (SELECT EXISTS(SELECT 1 FROM parttime WHERE job_id = #job_id)) = 1
BEGIN
SELECT * from parttime
END
ELSE
BEGIN
SELECT * from fulltime
END
END
Alternatively, if you want a consistent dataset (ideal for application logic), why not put the common columns between fulltime and parttime into a UNION statement, and create hard-coded NULLs for the columns they don't share. For example, if fullTime looked like
EmployeeID, DepartmentID, Salary
and partTime looked like
EmployeeID, DepartmentID, HourlyRate
you could do
SELECT job.*, employeeid, departmentid, salary, null as hourlyrate FROM job inner join fulltime on fulltime.job_id = job.job_id
where job.job_id = ?
union
SELECT job.*, employeeid, departmentid, null as salary, hourlyrate FROM job inner join parttime on parttime.job_id = job.job_id
where job.job_id = ?
If there are hundred different columns, this might be unwieldy. Also, in case this didn't make it obvious, having subtypes with completely different structures but using the same foreign key is a very good clue that you're breaking third normal form.