MySQL check if result is favourited - mysql

I have a query that show results for a search and I want to add one more field that checks if each result appears as a favourited result in another table. To keep it simple, as most of the search parameters only add some JOIN and WHERE and are not important to my question, let's consider there is a results table that has all the right fields:
id | title | description
And the result_favourites table:
userid | resultid
Here is the MySQL query to get results (once again without all the search criterias for simplicity):
SELECT id, title, description FROM results
What I want is something like that (let's say the user is #1):
SELECT r.id r.title, r.description, (something here) AS is_favourited
FROM results AS r
RIGHT JOIN result_favourites AS rf ON rf.resultid = r.id
WHERE userid = 1
With is_favourited being either 1 (there is at least one row in result_favourites with both userid and resultid matching r.id and userid = 1) or 0 (there is none).
I've tried to use COUNT(rf.userid) AS is_favourited but that didn't work. Any help is welcome!

I think you want a left join, not right join. A left join keeps all rows from the first table. Then just check if there is a match:
SELECT r.id r.title, r.description, (rf.resultid is not null) AS is_favourited
FROM results r LEFT JOIN
result_favourite rf
ON rf.resultid = r.id and
rf.userid = 1;

Try this one:
SELECT r.id r.title, r.description, if(rf.userid is null,0,1) AS is_favourited
FROM results AS r
left JOIN result_favourites AS rf ON (rf.resultid = r.id)
WHERE userid = 1

Related

Trying to do a LEFT JOIN, but ending up getting empty result

I tried doing a LEFT JOIN (the one on the image second of the left). So basically I want all records from table A without the records from table B, so I want to take only the information from table B that crosses with table A. There is more info like book details in table B that I want to be able to extract, but I don't need of the rows from table B, just the extra info. I figure the best way to do this is with LEFT JOIN and this is the code I tried, but it result in a empty string/result:
SELECT WF.*, W.* FROM WalletFrom AS WF LEFT JOIN Wallet AS W ON W.BookID = WF.BookID WHERE WF.BookID = 1360 AND W.BookID IS NULL
What I am doing wrong?
Columns from Wallet:
WalletID
UserID
BookID
OrderID
WalletStatusID
Win
WalletProductID
Columns from WalletFrom:
OrderID
BookID
Count
Win
WinTotal
Tax
WinEnd
DateTime
WalletProductID
You get empty result, because of W.BookID IS NULL clause, which is guaranteed to be not null by left join and its condition.
If you want only recrods from WalletFrom with BookId existing in Wallet, you could use below query:
SELECT * FROM WalletFrom wf
WHERE EXISTS(SELECT 1 FROM Wallet
WHERE BookId = wf.BookId)
What I end up needing to do is just add an AND cause to my JOIN cause like this
SELECT WF.*, W.* FROM WalletFrom AS WF LEFT JOIN Wallet AS W ON W.BookID = WF.BookID AND W.OrderID = WF.OrderID WHERE WF.BookID = 1360
In order to not get duplicated records/rows. Thanks for everyone who have answer my questions you helped me to figure this out
According to what you say in the comments, this sounds like inner join.
SELECT *
FROM TableA
INNER JOIN TableB ON TableA.BookID = TableB.BookID
WHERE TableA.BookiD = 1360
this gives you all the records from tableA that have a corresponding row in TableB without extra rows from TableB.
Ceck if this is what you need
Update
SELECT *
FROM Students AS S
INNER JOIN Grades AS G ON s.StudentId = G.StudentId
WHERE S.ClassId = 1360

How is joining with a subquery different from joining without a subquery? Looking for difference between two similar queries

I want to see which user created floor equipment for which customer -- both of these queries do what I want. The second query, however, results with 700 more rows than the first. Could you please explain the difference?
I ran another query that found the difference between the two sets -- sure enough, this query yielded 700 rows. Therefore, the data output is the same, but somehow the second query catches more results. I tried looking at the additional 700 rows, but they all seemed normal and similar to the other results. I can't find the difference by looking at the code, which is what I'm hoping someone can help me with
First query
SELECT customer.name, user.name, floor_equipment.id
FROM customer, user, floor_equipment, floor, building, site
WHERE (floor_equipment.floorID = floor.ID AND floor.buildingID = building.id AND
building.siteID = site.id AND floor_equipment.created_by = user.id)
Second Query
SELECT newTable.custName, newTable.userName, newTable.equipID
FROM (SELECT customer.name as "custName", user.name as "userName",
floor_equipment.id as "equipID", floor_equipment.created_by as "creatorID"
FROM customer, floor_equipment, floor, building, site
WHERE (floor_equipment.floorID = floor.ID AND floor.buildingID = building.id AND
building.siteID = site.id AND site.customerID = customer.ID)) as newTable, user
WHERE user.id = newTable.creatorID
I would expect both of these queries to have the same result, however the second query yields 700 more rows than the first. Aside from the extra rows, both queries result in the same data. The 700 additional rows seem to be normal and similar to the other rows.
NOTE: There is a seemingly pointless subquery in the second query. The purpose of this was for optimization. I am running these queries within Domo, a business intelligence webapp. I wrote the subquery in hopes that it would run faster. Because of the way Domo works, the former took 2 hours whereas the latter took 45 seconds.
Ignoring (or perhaps rectifying) the syntax errors, your first query can be written as follows:
SELECT c.name
, u.name
, fe.id
FROM customer c
CROSS
JOIN user u
JOIN floor_equipment fe
ON fe.created_by = u.id
JOIN floor f
ON f.ID = fe.floorID
JOIN building b
ON b.id = f.buildingID
JOIN site s
ON s.id = b.siteID
Likewise, written a little more coherently, your second query is as follows:
SELECT x.custName
, x.userName
, x.equipID
FROM
( SELECT c.name custName
, u.name userName
, fe.id equipID
, fe.created_by creatorID
FROM customer c
JOIN site s
ON s.customerID = c.ID
JOIN building b
ON b.siteID = s.id
JOIN floor f
ON f.buildingID = b.id
JOIN floor_equipment fe
ON fe.floorID = f.ID
) x
JOIN user u
ON u.id = x.creatorID
Again, we can omit the subquery and write it thus...
SELECT c.name custName
, u.name userName
, fe.id equipID
, fe.created_by creatorID
FROM customer c
JOIN site s
ON s.customerID = c.ID
JOIN building b
ON b.siteID = s.id
JOIN floor f
ON f.buildingID = b.id
JOIN floor_equipment fe
ON fe.floorID = f.ID
JOIN user u
ON u.id = fe.created_by
...so we can see that the first query had a cartesian product (CROSS JOIN), whereas the second query does not.
Your code is a Cartesian product between the tables:
customer, user, floor_equipment, floor, building, site
and your where condition is not for a join but just for a tuple of Boolean value
floor_equipment.floorID = floor.ID,
floor.buildingID = building.id,
building.siteID = site.id,
floor_equipment.created_by = user.id
( boolean, boolean, boolean, boolean)
each boolean is the result for the corresponding match eg:
floor_equipment.floorID = floor.ID
so practically return all the rows because have not matching counterpart.
In the second, your first Cartesian product is expanded by the join between the first result and the matching rows for user.id and newTable.creatorID. Looking to your code, it could be that you need an explicit join syntax and proper on condition.

Return unrated rows from a MySQL database

I have a system where people rate items, and I have two tables, I want to only show the user items they have not rated.
item (i and simplified for this example)
----
item_id, name
1, widget1
2, widget2
I have a rating table, which stores three columns
rating
------
item_id
user_id
rating
So I want to only return results that that user has not yet rated, now I did try this;
psuedo-query
SELECT * FROM item LEFT JOIN rating r ON r.item_id = i.item_id WHERE r.user_id != USER_ID_OF_THE_USER;
However that still returned items that they had rated, as other people had rated the item...
So if I have 100 items in the database, user a has rated 30 and user b has rated 70... then user a should get the 70 items they have to rate, and user b should get the 30 items they havent rated.
My rating table has a compound unique key, so if they rate item_id = 1 once, and rate it again, it just updates the rating value, it doesnt make a new row. One row is inserted for every item that is rated by a user.
This feels like it should be easy and it probably is, but I am stuck.
I'm assuming every user has to rate every item. If so, then you can do this with not exists:
select *
from item i
where not exists (
select 1
from rating r
where i.item_id = r.item_id and r.user_id = ?)
SQL Fiddle Demo
Give the fact you're using mysql, a left join / null check would probably be faster:
select i.*
from item i
left join rating r on i.item_id = r.item_id and r.user_id = ?
where r.user_id is null
More Fiddle
http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/
Just use an anti-join pattern, like this:
SELECT i.*
FROM item i
LEFT
JOIN rating r
ON r.item_id = i.item_id
AND r.user_id = USER_ID_OF_THE_USER
WHERE r.user_id IS NULL
The outer join returns all rows from item, along with rating by the user. If there is no related row from the rating table, then the values of the columns from rating will be NULL. So all we need is to add a WHERE clause to filter out the rows that had a match.
You can use this query:
SELECT * FROM item i
WHERE NOT EXISTS (
SELECT * FROM rating r
WHERE r.item_id = i.item_id
AND r.user_id = USER_ID_OF_THE_USER);
This will return you those items for which there is no rating of user USER_ID_OF_THE_USER.

Outer join providing inner join results

This should be a very simple solution, which means you'll probably think this is a dumb question, but I've tried everything I can think of.
I have two tables, one for possible poll choices, and the other for actual responses. They're structured like so:
choices responses
----------- ----------
poll_id poll_id
choice_id user_id
choice_text choice_id
I have a poll with two choices (yes/no), so I'm trying to fetch results so that if no one has voted for a certain choice, that choice shows up in the result set with a null value. So if 3 users have voted "yes" and none have voted "no", I want the result set to be:
choice_text num
-----------------------------
yes 3
no null
I would have thought this simply an outer join like so:
select
c.choice_text,
count(*) num
from
choices c
left outer join responses r
on c.poll_id = r.poll_id
and c.choice_id = r.choice_id
where
r.poll_id = 1
group by r.choice_id
order by r.choice_id asc;
But alas, that is giving me:
choice_text num
-----------------------------
yes 3
...without any record for "no".
I've tried every language for joins I can think of, with the same erroneous result.
Thoughts?
Your where condition on the outer joined table turns the outer join into an inner join. Move that condition into the JOIN
select c.choice_text,
count(*) num
from choices c
left outer join responses r
on c.poll_id = r.poll_id
and c.choice_id = r.choice_id
and r.poll_id = 1
group by r.choice_id
order by r.choice_id asc;
Btw: your usage of group by is incorrect and every other DBMS would reject that statement. MySQL simply chooses to return random data instead of failing with an error. For more details please see these blogs:
Debunking GROUP BY myths
Wrong GROUP BY makes your queries fragile
Figured it out thanks to #a_horse_with_no_name pointing me in the right direction.
The winning query:
select c.choice_text,
count(r.user_id) num
from choices c
left outer join responses r
on c.poll_id = r.poll_id
and c.choice_id = r.choice_id
and c.poll_id = 1
group by c.choice_id
order by c.choice_id asc;
Moved the c.poll_id clause to the join, like #a_horse_with_no_name suggested, but then had to group by the choices table instead of the responses table, and finally changed my count(*) to count(r.user_id), and voila.

Listing top 5 users measured by most common rows in foreign key table

Here's a picture of my database structure:
When I input an Observation, I'm entering a row into Observations but also 0, 1 or more rows into Criteria.
I'm trying to write an SQL statement which allows me to select the Strongest 5 teachers in a specific criteria.
So for example I'd click on Questioning (which might have an ID of 5 in Criteria_Labels), I'd want to return a list of 5 teachers (Teacher_ID from Observations) who have the most rows of Criteria_ID = 5 in Criteria.
The statement that I've attempted to write is as follows:
SELECT t.Name AS Teacher_Name
FROM observations o
LEFT JOIN teachers t ON o.Teacher_ID = t.Teacher_ID
LEFT JOIN criteria c ON o.ID = c.Observation_ID
WHERE c.Criteria_ID = 5
ORDER BY COUNT(c.Criteria_ID) DESC
LIMIT 0,5
However, it only appears to return one member of staff. I'm not sure I've got this right at all, but hopefully I'm along the right lines.
Can anyone help? Thanks in advance,
SELECT t.Teacher_ID, t.Name AS Teacher_Name, count(*) as total
FROM observations o
LEFT JOIN teachers t ON o.Teacher_ID = t.Teacher_ID
LEFT JOIN criteria c ON o.ID = c.Observation_ID
WHERE c.Criteria_ID = 5
group by t.Teacher_ID, t.Name
order by total desc
limit 5