JOIN where CAN be joined, else no join - mysql

So, I have this MySQL problem.
I've been looking at LEFT JOIN, INNER JOIN etc. And as I understand LEFT JOIN joins from left and if the right part doesn't exist it's values get set to NULL. And with INNER JOIN if there is no match for the join, an empty set is returned.
As you can see from my previous statement I join different statistics and such for one of the users in the match. But if uid2 isnt friend with uid1 this will give null in my case for all fields in friends.*
How would I do to get all the friends fields if they are friends and no fields from friends if they arent. (I dont want to do a separate statement before to check if they're friends).
This is my previous statemenet:
SELECT matches.*, statistics.*, friends.*, users.username
FROM `matches`
LEFT JOIN statistics ON matches.uid1=statistics.uid
LEFT JOIN users ON matches.uid1=users.uid
LEFT JOIN friends ON matches.uid1=friends.fid AND matches.uid2=friends.uid
WHERE matches.mid IN (269,231,131)

Then you just replace the last LEFT JOIN with an INNER JOIN:
SELECT matches.*, statistics.*, friends.*, users.username
FROM `matches`
LEFT JOIN statistics ON matches.uid1=statistics.uid
LEFT JOIN users ON matches.uid1=users.uid
INNER JOIN friends ON matches.uid1=friends.fid AND matches.uid2=friends.uid
WHERE matches.mid IN (269,231,131);
However, it's not possible to selectively not return the columns from the friends table when they can't be joined.

I ended up with still doing a LEFT JOIN, and then filter the result;
$temp = array_filter($temp, "is_not_null");
function is_not_null($var)
{
return ! is_null($var);
}

Related

SQL Left Join a Table on a Left Joined Table

Iam currently trying to left join a table on a left joined table as follows.
I have the tables:
accounts (id, vorname, nachname)
projektkurse (id, accounts_id, projektwochen_id)
projektkurs_einzel (id, projektkurse_id)
projektkurs_einzel_zeiten (id, date, shift, projektkurs_einzel_id)
Now I want to get every account and the amount times they have an entry inside of projektkurs_einzel_zeiten, which should also be unique. So having the same date and shift multiple times does not count as multiple entries. The result should also be limited by the column projektwochen_id from the table projektkurse. This column should match a certain value for example 8.
Some Accounts don't have any entries in projektkurse, projektkurs_einzel and projektkurs_einzel_zeiten, this is why my first thought was using LEFT JOIN like this:
SELECT accounts.id, accounts.vorname, accounts.nachname, COUNT(DISTINCT projektkurs_einzel_zeiten.date, projektkurs_einzel_zeiten.shift) AS T
FROM accounts
LEFT JOIN projektkurse on accounts.id = projektkurse.creator_id
LEFT JOIN projektkurs_einzel on projektkurse.id = projektkurs_einzel.projektkurs_id
LEFT JOIN projektkurs_einzel_zeiten ON projektkurs_einzel.id = projektkurs_einzel_zeiten.projektkurs_einzel_id
WHERE projektkurse.projektwochen_id = 8
GROUP BY accounts.id
This query does not achieve exactly what I want. It only returns accounts that have atleast one entry in projektkurse even if they have none in projektkurs_einzel and projektkurs_einzel_zeiten. The Count is obviously 0 for them but the accounts that have no entries in projektkurse are being ignored completly.
How can I also show the accounts that don't have entries in any other table with the Count 0 aswell?
I would recommend writing the query like this:
SELECT a.id, a.vorname, a.nachname,
COUNT(DISTINCT pez.date, pez.shift) AS T
FROM accounts a LEFT JOIN
projektkurse
ON a.id = pk.creator_id AND
pk.projektwochen_id = 8 LEFT JOIN
projektkurs_einzel pe
ON pk.id = pe.projektkurs_id LEFT JOIN
projektkurs_einzel_zeiten pez
ON pe.id = pez.projektkurs_einzel_id
GROUP BY a.id, a.vorname, a.nachname;
Notes:
Your problem is fixed by moving the WHERE condition to the ON clause. Your WHERE turns the outer join into an inner join, because NULL values do not match.
Table aliases make the query easier to write and to read.
It is a best practice to include all unaggregated columns in the GROUP BY. However, assuming that id is unique, your formulation is okay (due to something called "functional dependencies").
You should not use eft join table's column ins where condition this work as inner join
You should move the where condition for a left joined table in the corresponding ON clause
SELECT accounts.id, accounts.vorname, accounts.nachname, COUNT(DISTINCT projektkurs_einzel_zeiten.date, projektkurs_einzel_zeiten.shift) AS T
FROM accounts
LEFT JOIN projektkurse on accounts.id = projektkurse.creator_id
AND projektkurse.projektwochen_id = 8
LEFT JOIN projektkurs_einzel on projektkurse.id = projektkurs_einzel.projektkurs_id
LEFT JOIN projektkurs_einzel_zeiten ON projektkurs_einzel.id = projektkurs_einzel_zeiten.projektkurs_einzel_id
GROUP BY accounts.id

Mysql count left join strange result

Can someone help me to understand those results ? (For me all 3 should return 6455).
(Using RDS mysql-8.0.13)
SELECT COUNT(p.product_id) FROM product p LEFT JOIN product_attributes pa ON p.pdt_id = pa.pdt_id WHERE pa.code = 'season';
Results : 6332
SELECT COUNT(*) FROM product p;
Results : 6455
SELECT COUNT(p.product_id) FROM product p LEFT JOIN product_attributes pa ON p.pdt_id = pa.pdt_id AND pa.code = 'season';
Results : 6455
Your first join uses the WHERE clause, this mean sit selected all the rows, including those with a null join and then filters out those WHERE the pa.code = season, i.e. the null joins.
The last one joins on both, but because it is a left join you still get the full table of results, and nothing is filtered because you remove the WHERE clause. If you were to use an INNER JOIN in the last query you should get the same result (6332).
This link might be useful What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?

Filtering one table with multiple inner joins against other tables

I have four tables, Photo, Event, News, Spot and Photo is the table i want to check for records with relations to other tables.
Photo has the following sructure:
id
rel_model -> one of "news", "spot" and "event"
rel_id -> id of the related record in rel_model table
...
Tables other than Photo are constantly updated and some of the records ar deleted. I want to filter the photos to get the records that are related to existing records on other tables.
I tried the following
select
count(*)
from
Photo
inner join Event ON (rel_id = Event.id and rel_model="event")
inner join News ON (rel_id = News.id and rel_model="news")
inner join Spot ON (rel_id = Spot.id and rel_model="spot");
but I get 0 results where trying it with only one inner join works for checking against single table
select
count(*)
from
Photo
inner join Event ON (rel_id = Event.id and rel_model="event") ;
I need to add some and-or logic inbetween inner joins, bit could not figure out how.
How can I fetch the photos that still have unbroken relations to other tables?
you could use this query
select
count(*)
from Photo as P
where
P.rel_model = "event" and P.rel_id in (select T.id from Event as T) or
P.rel_model = "news" and P.rel_id in (select T.id from News as T) or
P.rel_model = "spot" and P.rel_id in (select T.id from Spot as T)
If you want to change your query, you should use left outer join:
select
count(*)
from Photo as P
left outer join Event ON (rel_id = Event.id and rel_model="event")
left outer join News ON (rel_id = News.id and rel_model="news")
left outer join Spot ON (rel_id = Spot.id and rel_model="spot")
where News.id is not null or Spot.id is not null or Event.id is not null
Your query return null rows because you trying to join same row with all three tables, but you join condition is matched only one, so other two inner joins eliminate you row.
You can do this using outer joins. With the inner joins you are losing a row when rel_id fails to match any of the three (and presumably, it matches only one of them, so you lose all rows). Then, you need to count each one separately:
select count(Event.id) + count(News.id) + count(Spot.id)
from Photo p left join
Event
ON p.rel_id = Event.id and rel_model="event" left join
News
ON p.rel_id = News.id and rel_model="news" left join
Spot
ON p.rel_id = Spot.id and rel_model="spot";

How to fix this JOIN query?

I have this query:
SELECT *
FROM people
JOIN pets ON people.id = pets.owner_id;
It returns results for people who have pets. But not all people have pets. I want the query to return all persons from the people table and IF they happen to have pets to return the pets also.
But since pets are optional, how can I make this query return even people with no pets?
Use LEFT JOIN
SELECT *
FROM people
LEFT JOIN pets ON people.id = pets.owner_id;
JOIN Explanation
JOIN in SQL
JOIN default to INNER JOIN
in your case you should use
left join

Need an alternative to two left joins

Hey guys quick question, I always use left join, but when I left join twice I always get funny results, usually duplicates. I am currently working on a query that Left Joins twice to retrieve the necessary information needed but I was wondering if it were possible to build another select statement in so then I do not need two left joins or two queries or if there were a better way. For example, if I could select the topic.creator in table.topic first AS something, then I could select that variable in users and left join table.scrusersonline. Thanks in advance for any advice.
SELECT * FROM scrusersonline
LEFT JOIN users ON users.id = scrusersonline.id
LEFT JOIN topic ON users.username = topic.creator
WHERE scrusersonline.topic_id = '$topic_id'
The whole point of this query is to check if the topic.creator is online by retrieving his name from table.topic and matching his id in table.users, then checking if he is in table.scrusersonline. It produces duplicate entries unfortunately and is thus inaccurate in my mind.
You use a LEFT JOIN when you want data back regardless. In this case, if the creator is offline, getting no rows back would be a good indication - so remove the LEFT joins and just do regular joins.
SELECT *
FROM scrusersonline AS o
JOIN users AS u ON u.id = o.id
JOIN topic AS t ON u.username = t.creator
WHERE o.topic_id = '$topic_id'
One option is to group your joins thus:
SELECT *
FROM scrusersonline
LEFT JOIN (users ON users.id = scrusersonline.id
JOIN topic ON users.username = topic.creator)
WHERE scrusersonline.topic_id = '$topic_id'
Try:
select * from topic t
left outer join (
users u
inner join scrusersonline o on u.id = o.id
) on t.creator = u.username
If o.id is null, the user is offline.
Would not it be better to match against topic_id in the topics table by moving the condition to the join. I think it will solve your problem, since duplicates come from joining with the topics table:
SELECT * FROM scrusersonline
JOIN users
ON users.id = scrusersonline.id
LEFT JOIN topic
ON scrusersonline.topic_id = '$topic_id'
AND users.username = topic.creator
By the way, LEFT JOIN with users is not required since you seem to search for the intersection between scrusersonline and users