I got this 3 tables as shown aboved.
My goal is to get all bits that user 1 (jm) did not put a reaction to.
Currently I have this MySQL code:
select * from bit b LEFT JOIN bit_reaction br ON (br.bitId=b.id AND br.userId != 1)
The problem here is that bit_reaction.id = 2 is being returned since br.userId is not equal to 1. The correct behavior is that it would only return bits with id 2 and 3.
Thanks for the tip!
select b.*
from bit b
left join bit_reaction br ON br.bitId = b.id
AND br.userId = 1
WHERE br.bitId is null
I assume that you want every user to react every bit. So the more generic answer will be...
SELECT *
FROM bit AS tBit
LEFT JOIN user AS tUser ON 1 = 1
LEFT JOIN bit_reaction AS tReaction ON tBit.id = tReaction.bitID
AND tUser.id = tReaction.userID
WHERE tReaction.userID IS NULL
Related
Lets say I have these tables
I want to get all matches for a concrete matchday along with bets for each match if its made by a concrete user. Basically avoid getting results from other users but still get all matches for the matchday. I tried WHERE match.matchday = 1 AND user.userId = 1 but this gives only the results where both the matchday and the userId match, so if there is no bet on a match from the user for the matchday it is not added to the results
The result should be like
Also I am open for suggestions if this is a good way to get what I want, or I should just use multiple requests to get the data and manage it in the application
There are multiple ways to achieve it with small tweaks.
Here are 2 working solutions.
SELECT *
FROM match
LEFT JOIN bet ON match.matchId = bet.matchId AND bet.userId = 1
LEFT JOIN user ON bet.userId = user.userId
WHERE match.matchday = 1
SELECT *
FROM match
LEFT JOIN bet ON match.matchId = bet.matchId
LEFT JOIN user ON bet.userId = user.userId
WHERE match.matchday = 1 AND (
bet.userId = 1 OR bet.userId IS NULL
)
I have the following Database Design:
Database Design
I want to get all Information from table 'info' where the id IS NOT in table 'archived'. To do so I wrote:
SELECT *
FROM traffic_info i
LEFT JOIN
traffic_info_archived a ON (i.info_id = a.info_id)
WHERE
i.branch_id = 4 AND i.user_id = 7 a.info_id IS NULL ORDER BY i.info_date_from ASC
This works as expected.
The next challenge is to only show information that are also included in the 'published' table. To get this done I have expanded my previous query to :
SELECT *
FROM traffic_info i
LEFT JOIN
traffic_info_archived a ON (i.info_id = a.info_id)
RIGHT JOIN
traffic_info_publised p ON (i.info_id = p.info_id)
WHERE
i.branch_id = 4 AND a.info_id AND i.user_id = 7 IS NULL ORDER BY i.info_date_from ASC
This does also work as expected.
The final challenge is to Order this result according to table 'read'. Information´s id that are NOT in table 'read' should be ordered ASC. But even if its id does not appear in table 'read' they should not be excluded from the query output. BUT the primary ORDER should be
i.info_date_from ASC
I hope this is understandable, my English is not the best :) If not, please comment and I will do my best to make it understandable. Hope some can help!
I´ve tried to create a SQLFiddle, but I wasn´t able to create a runnable example, sorry for that.
UPADTE:
Using the approach from #Dylan Su
SELECT *
FROM traffic_info i
LEFT JOIN
traffic_info_archived a ON (i.info_id = a.info_id)
INNER JOIN
traffic_info_publised p ON (i.info_id = p.info_id)
WHERE
i.branch_id = 4 AND a.info_id AND i.user_id = 7 IS NULL
ORDER BY
CASE WHEN NOT EXISTS(SELECT 1 FROM read WHERE i.info_id = read.info_id)
THEN i.info_date_from END ASC;
the goal is nearer then it ever was :)
Sample Data output
Both entries marked with a red "X" are in table read. Therefore id 3 should be last the, in the middle 1 and 2 at the top.
So the last thing to archive is to do the correct order of table read. I´ve tried sth like:
(SELECT 1 FROM traffic_info_read WHERE i.info_id = traffic_info_read.info_id ORDER BY traffic_info_read.info_id DESC)
But that didn´t had any influnce.
Try this:
SELECT *
FROM traffic_info i
LEFT JOIN
traffic_info_archived a ON (i.info_id = a.info_id)
INNER JOIN
traffic_info_publised p ON (i.info_id = p.info_id)
WHERE
i.branch_id = 4 AND a.info_id AND i.user_id = 7 IS NULL
ORDER BY
EXISTS(SELECT 1 FROM read WHERE i.info_id = read.info_id) ASC,
i.info_date_from ASC;
The Answer of #Dylan Su is absolutely correct and I won´t unmark it as accepted.
However, based on the Information I gained from the conversation I have created another solution, that doesn´t make use of sub query.
I heard that using just JOIN´s will result in better performance, I don´t know if it´s correct and I don´t have that much test data currently to find out, but here is the solution using no sub query.
SELECT *
FROM traffic_info i
LEFT JOIN
traffic_info_archived a ON (i.info_id = a.info_id)
INNER JOIN
traffic_info_published p ON (i.info_id = p.info_id)
LEFT JOIN
traffic_info_read r ON (i.info_id = r.info_id)
WHERE
i.branch_id = 4 AND a.info_id IS NULL ORDER BY r.info_id IS NULL DESC, i.info_date_from ASC
;
I'm pulling information out with the code below, which works absolutely fine.
I need to pull out the data without the contractorID affecting it, then subtract the rows from the first query from it.
SELECT
DISTINCT a.addID
, p.propdetailID
, p.plotID
FROM address AS a
LEFT JOIN propdetail AS p ON (a.addID = p.addID)
JOIN housebuildertoproperty AS htp ON (htp.addID = p.addID)
JOIN contractortopropdetail AS ctp ON (ctp.propdetailID = p.propdetailID)
WHERE htp.housebuilderID = 1
AND ctp.contractorID = 1
So what I mean is:
row 1
that's the result from the first query
row 1
row 2
row 3
That's the result from the query without the AND ctp.contractorID = 1 part. I need code that will pull out:
row 2
row 3
I tried MINUS but phpMyAdmin just didn't accept it. I've looked into NOT IN but I can't seem to figure this out.
First, the left join is unnecessary, because the subsequent join conditions and where clause turn it into an inner join.
Your problem is that some of the addresses on with contractorID = 1 have other contractors. The solution is to use group by instead of distinct and use a having clause to filter out addresses for contractor 1:
SELECT a.addID, p.propdetailID, p.plotID
FROM address a JOIN
propdetail p
ON (a.addID = p.addID) JOIN
housebuildertoproperty htp
ON (htp.addID = p.addID) JOIN
contractortopropdetail ctp
ON (ctp.propdetailID = p.propdetailID)
WHERE htp.housebuilderID = 1
GROUP BY a.addID, p.propdetailID, p.plotID
HAVING SUM(ctp.contractorID = 1) = 0
I have 4 tables user, posts, follows, notifications. The posts table has a field called privacy in which a value of 1 means "anyone can see" and 2 means "only friends can see".
My notifications table looks like this:
id user_id tonotify_id notification post_id
1 2 3 --- 1
2 3 2 --- 2
3 2 4 --- 3
And my follows table looks like this:
id user_id tofollow_id status fstatus
1 1 2 1 1
2 1 3 1 1
The field fstatus value of 1 means that they are also friends.
Now I am trying to get all notifications sent out by people who a certain user is following (lets say a user with id 1). Assuming 1 is following user 2 and 3, I need to get all notifications from the above table. However, before this I need to make sure that the post (posted by the users in the tonotify_id column) is available. That is, I need to check the privacy setting. I have the following code:
SELECT
u.username AS sender,
ux.username AS receiver,
p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
WHERE f.user_id = 1
AND fr.user_id = 1
AND f.status = 1
AND p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1)
ORDER BY n.id DESC
It seems to be working except for one glitch. Since user 1 is not following user 4 all notifications aimed at 4, even if the posts are public don't show up. This my guess has to do with JOIN follows fr ON (n.tonotify_id = fr.tofollow_id) because, since the user 1 hasn't followed 4, there are no rows matching this join. Any suggestions to solving this?
I did try [the outer join], but the output is the same.
When you use an outer join, and then use one of the "outer" columns in an equality check in the WHERE clause, you convert your outer join to an inner join. This is because your condition that checks post's privacy requires the post to be there:
AND p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1)
When an outer join is about to produce a row that corresponds to a notification without a post, it would check the above condition. Since the post is not there, p.privacy would evaluate to NULL, "contaminating" both sides of the OR, and eventually making the whole condition evaluate to false.
Moving this condition into the ON condition of the join will fix the problem:
SELECT
u.username AS sender,
ux.username AS receiver,
p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
AND (p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1))
WHERE f.user_id = 1
AND fr.user_id = 1
AND f.status = 1
ORDER BY n.id DESC
Another way to fix this would be adding an IS NULL condition to your OR, like this:
SELECT
u.username AS sender,
ux.username AS receiver,
p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
WHERE f.user_id = 1
AND fr.user_id = 1
AND f.status = 1
AND (p.privacy IS NULL OR p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1))
ORDER BY n.id DESC
You need to use an outer join such as LEFT JOIN instead of an inner join using just JOIN. An outer join will always return rows from one "side" (which is why it is called LEFT [OUTER] JOIN and RIGHT [OUTER] JOIN) even if the other "side" does not match anything.
I asked this last week over the weekend and it got buried in the archives before anyone could answer. So forgive me if you've already seen this.
I teach classes and want to be able to select those students who have taken one class, but not another class. I have two tables: lessons_slots which is the table for every class such as:
--------------------
-ID name slots-
-1 basics 10 -
-2 advanced 10 -
-3 basics 10 -
---------------------
The other table is class_roll, which holds enrollment info, such as:
--------------------
-sID classid firstname lastname-
-1 1 Jo Schmo
-2 1 Person Two
...
-13 2 Jo Schmo
---------------------
What I want to do, I select everyone who has not had the advanced class (for example). I've tried doing
SELECT *
FROM lessons_slots
LEFT JOIN class_roll
ON lessons_slots.ID = class_roll.classid
WHERE lessons_slots.name != 'advanced'
But that doesn't work...All it does is eliminate that row, without eliminating the user. I want Jo Schmo, for example, to not show up in the results. Any ideas?
Not pretty, but works.
SELECT c.*
FROM lessons_slots l
join class_roll c on l.id=c.classid
where concat(firstname,lastname) not in (select concat(firstname,lastname)
from lessons_slots l
join class_roll c on l.id=c.classid where name='advanced')
SELECT * FROM class_roll cl LEFT JOIN lessons_slots ls
ON cl.classid = ls.id AND ls.name != 'advanced'
So you actually want to delete rows? Well then:
DELETE FROM class_roll cl LEFT JOIN lessons_slots ls
ON cl.classid = ls.id AND ls.name != 'advanced'
You could try something to the effect of
SELECT FirstName, LastName FROM class_roll
WHERE NOT EXISTS
(SELECT * FROM class_roll, lesson_slots
WHERE class_roll.classid = lesson_slots.ID
AND lesson_slots.name = 'advanced')
You can then use the result as the basis of a DELETE query.