SQL efficiency join on two fields or one - mysql

Let say that I have got three tables: account, contact and address. To account and contact I want to assign multiple addresses. Is it better to have two fields in address join_type and join_id and then for example running query:
SELECT a.*
FROM address a
INNER JOIN contact c ON c.id = a.join_id AND a.join_type = 'CONTACT';
and
SELECT a.*
FROM address a
INNER JOIN account ac ON ac.id = a.join_id AND a.join_type = 'ACCOUNT';
or have account_id and contact_id instead of join_type and join_id and running query:
SELECT a.*
FROM address a
INNER JOIN contact c ON c.id = a.contact_id;
and
SELECT a.*
FROM address a
INNER JOIN account ac ON ac.id = a.account_id;
or maybe even have got two seperate address tables for account and contact? The first option is the best for future if I would like e.g. have also addresses assigned to users.

SELECT a.*
FROM address a
INNER JOIN contact c ON c.id = a.contact_id;
The fact that you have multiple types of addresses should not cause any problem since the contact_id should always be unique.
I would also consider having another column in address with account_id and have another join like this:
SELECT a.*
FROM address a
INNER JOIN account a ON a.id = a.account_id;

Related

SQL join while retaining the data

I'm using an SQL join to show all my clients addresses using address_id and to check if the booking is completed(done 1=completed). The second join connects the user_id of addresses to clients (since 1 client can have more than 1 address).
Using php, if done=1 a button needs to appear but if there are any bookings under that address_id (i.e done=0) then no button apears.
select *, coalesce(b.done, 1) completed
from addresses c
left join bookings b on b.address_id = c.address_id and b.done = 0
LEFT JOIN clients d ON c.client_id=d.client_id;
I have tried all the join methods. The button is appearing correctly with the corresponding client name so the joins are working. Although, only the client.name and addresses.address ever consistently appear. I need the address_id in the button to be able to book for that address, unfortunately, when the done=0 address_id is blank.
Is there any way that I can get the same result from the joins, while also getting the address_id?
I think you want the joins the other way. Then you should list the columns that you are using explicitly:
select c.name, a.address, a.address_id, coalesce(b.done, 1) as completed
from clients c left join
bookings b
on b.client_id = c.client_id and b.done = 0 left join
addresses a
on b.address_id = a.address_id ;
Also note that I changed the table aliases so they are abbreviations for the table name -- much easier to follow than confusing aliases.

MySQL Join 3 tables by 2 columns ids in the first table

I have 3 tables... Contacts, Outbound Operator, Digital Operator.
The contacts table contains all the contacts generated by either outbound operator and digital operator.
So I will have these columns in "Contacts":
id, name, surname, id_outbound_operator, id_digital operator
In the second table I have all the outbound operators so the table is something like this:
uid, out_bound_operator_full_name
The third table is the same as the second but with these columns:
uid, digital_operator_full_name
I want to obtain something like this:
id, name, surname, outbound_operator_full_name (if this was generated by a outbound operator), digital_operator_full_name (if this was generated by a outbound operator).
I have to specify that in contacts table at lease one of the two
(id_outbound_operator/id_digital_operator) is not null
I tried this
SELECT CONTATTI.id, CONTATTI.nome_azienda, CONTATTI.telefono, CONTATTI.stato, CONTATTI.id_outbound, CONTATTI.id_digital_marketing_op, CONTATTI.blacklisted, OUTBOUND_INT_login.uid, OUTBOUND_INT_login.nome_completo, MARKETING_DIGITAL_login.uid, MARKETING_DIGITAL_login.nome_completo
FROM CONTATTI
JOIN OUTBOUND_INT_login
ON CONTATTI.id_outbound = OUTBOUND_INT_login.uid
JOIN MARKETING_DIGITAL_login
ON CONTATTI.id_digital_marketing_op = MARKETING_DIGITAL_login.uid
but it doesn't work properly
You should use a UNION of queries that join with each of the other tables.
SELECT c.id, c.name, c.surname, o.outbound_operator_full_name
FROM CONTATTI AS c
JOIN OUTBOUND_INT_login AS o ON c.id = o.uid
UNION ALL
SELECT c.id, c.name, c.surname, m.digital_operator_full_name
FROM CONTATTI AS c
JOIN MARKETING_DIGITAL_login AS m ON c.id = m.uid
You can join the three tables as follows. You want left join so that contact records that cannot joined with both outbound_operator and digital_operator are not eliminated from the resultset.
select
c.id,
c.name,
c.surname,
op.outbound_operator_full_name,
do.digital_operator_full_name
from contact c
left join outbound_operator op on c.id_outbound_operator = op.uid
left join digital_operator do on c.id_digital_operator = do.uid
As commented by Uueerdo, if you want a unique column with either the outbound_operator_full_name or the digital_operator_full_name, then:
select
c.id,
c.name,
c.surname,
coalesce(op.outbound_operator_full_name, do.digital_operator_full_name) operator_full_name
from contact c
left join outbound_operator op on c.id_outbound_operator = op.uid
left join digital_operator do on c.id_digital_operator = do.uid
The advantage of this approach is that it requires a single scan on contact, vs two scans when using UNION (ALL).

join sql query based on value of another table (in one query)

Let's say that I have two tables A and B where
A is table countries with columns id, name, created, modified
that contains a bunch of countries
And B is table users with columns id, first_name, last_name, email, country_id, created, modified
that contains a bunch of users linked to countries via foreign key country_id
What is the most efficient query to get all the countries that don't have a user with email address "myemail#test.com" associated to it?
I tried something like the following but that didn't work:
SELECT DISTINCT
c.*
FROM
countries c
LEFT JOIN
users u ON u.country_id = c.id
WHERE
u.email <> 'myemail#test.com'
Thanks for any help
NOTE I also tried putting the condition on the email column in the ON clause that didn't work either
A left join is fine, you just need to set it up correctly:
SELECT c.*
FROM countries c LEFT JOIN
users u
ON u.country_id = c.id AND u.email = 'myemail#test.com'
WHERE u.country_id IS NULL;
In terms of performance, this should be pretty similar to NOT EXISTS and NOT IN (although I do not recommend the latter because it has different behavior when there are NULL values).
When you say "that don't have a user with email address "myemail#test.com"",
do you mean no email address -or- not that exact email address?
Updated
Then this should do:
SELECT DISTINCT c.*
FROM countries c
LEFT JOIN users u ON u.country_id = c.id and u.email = 'myemail#test.com'
WHERE u.country_id is null
Which I believe is what Gordon already had.
Updated Again
In that case, try:
SELECT DISTINCT c.*
FROM countries c
INNER JOIN users u ON u.country_id = c.id and ISNULL(u.email, '') = ''
This looks for Null or Empty String email adresses all others are excluded from the join and therefore from the result set.
I hope this helps.

MySQL LEFT JOIN multiple tables from same source table

I have the following tables:
companyContacts
addresses
states
phoneNumbers
And I need to do a LEFT JOIN on addresses, states, and phoneNumbers. I need to connect phoneNumbers and addresses with companyContacts, and need to connect states with addresses.
I can do a select like the following to grab everything. However, If someone has an address and not a phone number, it will not return a result. How do I make it so that both phoneNumbers and addresses are joined from companyContacts?
SELECT * FROM
companyContacts AS c
LEFT JOIN phoneNumbers AS p
ON c.entityID = p.entityID
LEFT JOIN addresses AS a
ON p.entityID = a.entityID
LEFT JOIN states AS s
ON a.stateID = s.id
It sounds like you want this:
SELECT *
FROM companyContacts AS c
LEFT JOIN phoneNumbers AS p
ON c.entityID = p.entityID
LEFT JOIN addresses AS a
ON c.entityID = a.entityID
LEFT JOIN states AS s
ON a.stateID = s.id
If you join on the companyContacts.entityId to both the phoneNumbers and addresses then you will return records even if there is not a phone number or address. The way you had it written the phone number needed to exist to return an address

Problem using MySQL Join

i have a MySQL SELECT query which fetches data from 6 tables using Mysql JOIN. here is the MySQL query i am using.
SELECT
u.id,u.password,
u.registerDate,
u.lastVisitDate,
u.lastVisitIp,
u.activationString,
u.active,
u.block,
u.gender,
u.contact_id,
c.name,
c.email,
c.pPhone,
c.sPhone,
c.area_id,
a.name as areaName,
a.city_id,
ct.name as cityName,
ct.state_id,
s.name as stateName,
s.country_id,
cn.name as countryName
FROM users u
LEFT JOIN contacts c ON (u.contact_id = c.id)
LEFT JOIN areas a ON (c.area_id = a.id)
LEFT JOIN cities ct ON (a.city_id = ct.id)
LEFT JOIN states s ON (ct.state_id = s.id)
LEFT JOIN countries cn ON (s.country_id = c.id)
although query works perfectly fine it sometimes returns duplicate results if it finds any duplicate values when using LEFT JOIN. for example in contacts table there exist two rows with area id '2' which results in returning another duplicated row. how do i make a query to select only the required result without any duplicate row. is there any different type of MySQL Join i should be using?
thank you
UPDATE :
here is the contacts table, the column area_id may have several duplicate values.
ANSWER :
there was an error in my condition in last LEFT JOIN where i have used (s.country_id = c.id) instead it should be (s.country_id = cn.id) after splitting the query and testing individually i got to track the error. thank you for your response. it works perfectly fine now.
Duplicating the rows like you mentioned seems to indicate a data problem.
If users is your most granular table this shouldn't happen.
I'd guess, then, that it's possible for a single user to have multiple entries in contacts
You could use DISTINCT as mentioned by #dxprog but I think that GROUP BY is more appropriate here. GROUP BY whichever datapoint could potentially be duplicated....
After all, if a user has corresponding contact records, which one are you intending to JOIN to?
You must specify this if you want to remove "duplicates" because, as far as the RDBMS is concerned, the two rows matching
LEFT JOIN contacts c ON (u.contact_id = c.id)
Are, in fact, distinct already
I think a DISTINCT may be what you're looking for:
SELECT DISTINCT
u.id,u.password,
u.registerDate,
u.lastVisitDate,
u.lastVisitIp,
u.activationString,
u.active,
u.block,
u.gender,
u.contact_id,
c.name,
c.email,
c.pPhone,
c.sPhone,
c.area_id,
a.name as areaName,
a.city_id,
ct.name as cityName,
ct.state_id,
s.name as stateName,
s.country_id,
cn.name as countryName
FROM users u
LEFT JOIN contacts c ON (u.contact_id = c.id)
LEFT JOIN areas a ON (c.area_id = a.id)
LEFT JOIN cities ct ON (a.city_id = ct.id)
LEFT JOIN states s ON (ct.state_id = s.id)
LEFT JOIN countries cn ON (s.country_id = c.id)
This should only return rows where the user ID is distinct, though you may not get all the joined data you'd hoped for.