How to remove matches between unioned tables with joined tables - mysql

I have 2 Tables
emails and fields
emails is just a list of emails and their unique identifiers and fields is in fifth normal form (i think) with a column of values(that ID data in the second column),a data column, and ids that link to the uuids in the first table.
For example:
Table 1:
ID EMAIL
-- -----
1a2 Test#Test
2a3 email#email|
3a4 add#add
Table 2:
value Data ID
----- ------ ---
1 123 Main 1a2
2 John Smith 1a2
3 US 1a2
4 555-555-5555 1a2
In this case, table 2 only shows the data for "1a2" because they were the only one to fill them out.
So I'm trying to make a table, that couples email to the corresponding country codes AND also provides the emails that haven't registered country codes as a Null value
I've tried using UNION to display all the emails from Table 1 on Table 2 after joining table 1 and 2 on ID and then using where for the value but that shows all the emails that have the proper country code and then duplicates them in the UNIONed portion as well.
This is an example of what I'm getting:
EMAIL COUNTRY CODE
----- ------------
test#test US
test#test NULL
email#email NULL
add#add NULL
You'll notice that test#test is duplicated due to the union not having the proper filter
My code looks like:
select
e.email as "Email",
f.value as "Country Code"
from
email e
join
fields f
ON e.id = f.id
where
f.value = '3'
[[and f.data like concat({{CountryCode}},'%')]]
-- curly brackets are for user entered variables
Anyways, all said and done. I'm looking for a table that appears something like this with No Duplicates
EMAIL COUNTRY CODE
----- ------------
test#test US
email#email NULL
add#add NULL

You are storing your user metadata in key value format. One approach here is to left join the user table to a subquery which pivots to find the country:
SELECT
e.EMAIL,
f.country_code
FROM email e
LEFT JOIN
(
SELECT ID, MAX(CASE WHEN value = 3 THEN Data END) AS country_code
FROM fields
GROUP BY ID
) f
ON e.ID = f.ID;
Demo

You can try using max() aggregation and group by -
select
e.email as "Email",
max(f.value) as "Country Code"
from
email e
left join
fields f
ON e.id = f.id
where
f.value = '3'
[[and f.data like concat({{CountryCode}},'%')]]
group by e.email

You can make the field you're interested in part of your LEFT JOIN condition.
SELECT
e.EMAIL,
f.data as countryCode
FROM email e
LEFT JOIN fields f ON f.value=3 AND e.ID=f.ID
Users with a value for that field will return it. Users without will simply be NULL due to it being a LEFT join.
If you want to do a condition on the fields value, then you'll need to include a check for null as well.
SELECT
e.EMAIL,
f.data as countryCode
FROM email e
LEFT JOIN fields f ON f.value=3 AND e.ID=f.ID
WHERE
f.data IS NULL
OR f.data='US'

Well I feel pretty silly after posting this, but I found the solution and am going to post it in case someone else bumps into the issue.
left join
fields f
ON e.id = f.id
where
f.value = '3' OR f.value is null
[[and f.data like concat({{CountryCode}},'%')]]
I just needed to add an "OR" for null values. Sorry if I wasted anyone's time, I was clearly overcomplicating things and managed to achieve what I was trying to do without a union at all.

Related

MySQL Left Join with child IsDeleted Flag

I have been searching google, but can't seem to exactly figure out my case. What I am trying to do is join a customer who has a list of jobs associated to him. I want the customer and any "active" jobs, in other words they haven't been deleted. The deleted flag is whether or not the datedeleted column is null or not.
So Lets say I have a table Customer:
ID - Name
1 - Joe Blow
2 - John Smith
And I also have a table of Jobs
ID - CustomerId - Name - DateDeleted
1 - 1 Build Fence NULL
2 - 2 - Clean Yard - 25/12/2014
What I want to do is get a customer with a list of their jobs. Now I know that if I do a join with Jow Blow it works fine, but it doesn't work when I want to get John Smith, it doesn't return any rows because I am checking the Deleted flag which then doesn't return any rows for jobs which in turn doesn't return any customer.
SELECT c.id, c.name, j.name as JobName from customer c
left join job j on c.id = j.CustomerId
where c.id = :id AND j.date_deleted IS NULL
And I want a results to be either:
Id - Name - JobName
1 - Jow Blow - Build Fence
or
Id - Name - JobName
2 - John Smith - NULL
Any help would be greatly appreciated. Thanks!
The problem is in your WHERE clause. When you add the AND j.date_deleted IS NULL, you are turning your OUTER JOIN into an INNER JOIN. You will need to move this condition to the ON clause of your OUTER JOIN.
Use this instead:
Select c.id,
c.name,
j.name As JobName
From customer c
Left Join job j On c.id = j.CustomerId
And j.date_deleted IS NULL
Where c.id = :id
Just try with this approach.As per my understanding you want to show all those jobs which has no date.
select e.Id,e.Name,( case when j.date is null then j.name else null end) as Jobprocess from
customer e
left join job j on e.Id=j.custId

Improve this query to fetch data from different tables

This is my scenario. I have a table of events with a field type, with values 1 = food, 2 = recipe. Simplifying, my events table have this structure:
id | entity_id | user_id | type | timestamp | field1 | ... field n
Field "entity_id" refers to a unique autoincremental value from "Entities" table. Food and recipe table structure is very similar, with entity_id and user_id fields.
What I want is to get all the common data from the table events of last 10 registers, and fetch some needed fields of corresponding table based on type value of table events. By now I have achieved some quite similar, but not exactly what I want, with this query:
SELECT a.*, b.name, b.field1, b.field2, c.name, c.field1, c.field2
FROM events a
LEFT JOIN foods b ON b.entity_id = a.entity_id
LEFT JOIN recipes c ON c.entity_id = a.entity_id
ORDER BY timestamp DESC LIMIT 10
This allways returns all fields for all tables, with NULL values when the field is not of the type of this specific register.
So I want to get all fields of events table, and name, field1, field2 of the corresponding table.
EDIT:
Here is the sqlfiddle sqlfiddle.com/#!2/18d45/9 I'd like the query returned different field values based on the table. In the example table recipes has description field while foods not. Is it possible?
Please helpe me with this!
You might use a COALESCE to get the first not NULL column:
SELECT a.*,
COALESCE(b.name, c.name),
COALESCE(b.field1, c.field1),
COALESCE(b.field2, c.field2)
FROM events a
...

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)

SQL Select based on certain Data

I have a few tables in my database:
user table: user_id (primary key), first_name, last_name, email_address.
inst table: inst_id (primary key), inst_name, and type.
user_insts table: user_insts_id (primary key), user_insts_status, inst_name (foreign key) and user_id(foreign
key).
I'm using this on my website and i need it to display all of the entrys in the inst_name column for the inst table, but only the entrys for a certain id in the right side or else show as null. I've tried a few things like below:
SELECT inst.inst_name,inst.inst_id,user_insts.user_id,user_insts.inst_name
FROM inst LEFT JOIN user_insts ON inst.inst_name=user_insts.inst_name;
SELECT inst.inst_name,inst.inst_id,user_insts.user_id,user_insts.inst_name
FROM inst LEFT JOIN user_insts ON inst.inst_name=user_insts.inst_name
WHERE user_insts.user_id='11';
Any help would be greatly appreciated.
EDIT::
this is what i currently get:
inst_name inst_id user_id:
ASB 1 11
BNZ 3 11
FMG 5 11
i was hoping to be able to get something more like this:
inst_name inst_id user_id:
ASB 1 11
ANZ 2 NULL
BNZ 3 11
paymark 4 NULL
FMG 5 11
STATE 6 NULL
What your original query will do is to get all rows from the inst table and then see whether there is a row that matches on inst_name in your user_insts table. If there is it will return the data from that table. Otherwise it will return NULLs. If you change the JOIN to be an INNER JOIN then it will only return rows where the right-hand side matches. Like this:
SELECT inst.inst_name,inst.inst_id,user_insts.user_id,user_insts.inst_name
FROM inst INNER JOIN user_insts ON inst.inst_name=user_insts.inst_name
WHERE user_insts.user_id='11';
But you should look at changing you schema. You have an integer primary key on inst so you should use that rather than inst_name as the foreign key on inst_name.
Do your provided queries show the following results?
Query 1 : Shows all inst + user_insts entries but unfiltered by user_id
Query 2 : Shows just the inst + user_insts entries filtered by user_id
What's happening in Query 2 is that the where clause filters the joined tables AFTER they are joined. I guess what you want to happen is to first filter the right side of the result (user_insts) by a specific user_id like so:
SELECT ui.inst_name, ui.user_id FROM user_insts ui WHERE ui.user_id = :PARAM
Then, you want to LEFT JOIN this with all the entries in the inst table AFTER the filter. You could use an inner view in order to filter by user_id first before the actual joining to the inst table. The resulting query should be something like this:
SELECT i.inst_name, filtered_ui.user_id
FROM inst i
LEFT JOIN (SELECT ui.inst_name, ui.user_id
FROM user_insts ui
WHERE ui.user_id = :PARAM) filtered_ui
ON i.inst_name = filtered_ui.inst_name
Thinking about it more, I'm not too familiar with MySQL so I'm not sure if this alternative query is valid syntax:
SELECT i.inst_name, ui.user_id
FROM inst i
LEFT JOIN user_insts ui ON i.inst_name = ui.inst_name AND ui.user_id = :PARAM
... which may be simpler than an inner view.
The main point is you have to do the filtering first before joining, so that all the inst_names will be displayed.
You question isn't 100% clear so I'm making the assumption that you want to show the inst_name for each user; showing the inst_name as null when an inst entry doesn't exist.
Something like this should work:
select u.user_id, i.inst_name
from user u
left join user_insts ui on ui.user_id = u.user_id
left join inst i on i.inst_name = ui.inst_name
You can constrain the results by user_id by adding
where u.user_id = '11'

Join query with flattened result

I have the following entities
AddressType is simply an enum field that define if the Email is Personal/Work/Other.
Is it possible to do a query that returns a flattened result like the one in the following sample?
CustomerID Full Name Personal Email Work Email
----------- -------------- ----------------- -----------------------
1 John Doe johndoe#hotmail.com john.doe#company.com
select c.CustomerID,
c.FullName as [Full Name],
epersonal.AddressText as [Personal Email],
ework.AddressText as [Work Email]
from Customer c
left outer join Email epersonal on c.CustomerID = epersonal.CustomerID
and epersonal.AddressType = 'personal'
left outer join Email ework on c.CustomerID = ework.CustomerID
and epersonal.AddressType = 'work'
Two main choices:
1) Select it as typical (with two rows, one for each email), then use the pivot operator to flatten.
Example of pivot (I call it an example as I wrote it in notepad. It may be slightly wrong, but it should point you the right way):
select
CustomerID,
FullName
[1] as WorkEmail,
[2] as HomeEmail
from
(select
c.CustomerID, c.FullName, e.AddressText, e.AddressType
from
Customer c
join emails e on e.CustomerID = c.CustomerID) as Source
pivot (
AddressText
FOR AddressType in ([1], [2])
)
2) Join to the email table twice, once for each type of address. Suggest outer joins so if one is missing you still get the other.