MySQL: Display one random result which a user has not voted on - mysql

I need help on displaying one random result in which the current user has not voted on.
Currently my database setup and the last query I have tried can be found on http://sqlfiddle.com/#!2/2f91b/1
Basically I can isolate each individual item using this query:
SELECT a.img_url, a.item_id, a.user_id, a.img_status, b.item_question, c.user_name, c.user_fbid, d.voter_id, count(d.img_id) AS totalVotes
FROM os_photos a
LEFT JOIN os_items b ON a.item_id = b.item_id
LEFT JOIN os_users c ON a.user_id = c.user_id
LEFT JOIN os_votes d ON a.img_id = d.img_id
GROUP BY a.img_id
ORDER BY RAND()
LIMIT 1
My Problem is: With the SQL knowledge that I have, I am unable to isolate the results to show only the rows in which user #2 has not voted on. I realize the problem is when I use group by, it combines the voter_id and therefore I am unable to check if user #2 has had any input for the item.
Example:
Item # | voter_id
1 | 2
1 | 3
2 | 2
3 | 1
3 | 4
4 | 3
4 | 1
5 | 1
5 | 2
With the above sample set, the resulting item should be either item #3, #4 or any other items which have not been voted on.
Your help, advise and knowledge is greatly appreciated.

To get the items that dont exist you need a LEFT JOIN with condition that would otherwise make a positive match, and then add a WHERE clause matching one of the resulting columns to NULL:
SELECT a.img_url, a.item_id, a.user_id, a.img_status, b.item_question, c.user_name,c.user_fbid, d.voter_id, count(d.img_id) AS totalVotes
FROM os_photos a
LEFT JOIN os_items b ON a.item_id = b.item_id
LEFT JOIN os_users c ON a.user_id = c.user_id
LEFT JOIN os_votes d ON a.img_id = d.img_id
LEFT JOIN os_votes d2 ON a.img_id = d2.img_id AND d2.voter_id=2
WHERE d2.voter_id IS NULL
GROUP BY a.img_id
ORDER BY RAND()
LIMIT 1

Related

MySql JOIN 3 tables and get child rows with same values

I have 3 tables: order, item and render.
1 order can have multiple items and each item has one render entry with status.
I want to have the order id's and date which have ALL its items with rendered 'done'.
In the example it would be the order 1 only, which has its 2 items (1 and 2) with status done in render table
order table
id| date
1 | 2021-12-10
2 | 2021-12-11
item table
id|order_id
1 | 1
2 | 1
3 | 2
4 | 2
render table
id|item_id| status
1 |1 | done
2 |2 | done
3 |3 | done
4 |4 | running
I would like to have it with JOIN statements. I've tried this but it does not work like expected (it returns both, order 1 and 2):
SELECT o.id, o.date
FROM order AS o
JOIN item AS i ON o.id = i.order_id
JOIN render r1 ON (i.`id` = r1.item_id AND r1.status = 'done')
LEFT JOIN render r2 ON (i.`id` = r2.item_id AND r2.status <> 'done')
AND r2.item_id IS NULL;
Any ideas? thank you in advance!
SELECT t1.id
, t1.date
FROM order
t1
WHERE t1.id NOT IN
(
SELECT o.id
FROM order
o
JOIN item
i
ON i.order_id = o.id
JOIN render
r
ON r.item_id = i.id
WHERE r.status <> 'done'
)
The choice of some column names is unfortunate, making the join conditions painful for a maintainer to read: it is not immediately obvious at a glance what kind if IDs the id columns refer to.
I would rename the following columns:
TABLE COLUMN_NAME BETTER_COLUMN_NAME
------ ----------- ------------------
order id order_id
order date order_date
item id item_id
render id render_id
render status render_status
I also disagree with the o / i / r table aliases, but arguments are in room 12A, just along the corridor.
Here I post what it eventually worked best for me in case it helps anybody :)
SELECT o.id, o.date
FROM order AS o
JOIN item AS i ON o.id = i.order_id
LEFT JOIN render r ON (r.item_id = i.id)
GROUP BY o.id
HAVING GROUP_CONCAT(DISTINCT IFNULL(r.status, "NULL")) = 'done';
In the HAVING clause I concatenate the statuses, remove all duplicates and check that only “done” is there. Since I needed to have a LEFT JOIN against render I explicitly set to “NULL“ the returned null rows.

MYSQL: Multiple Table Join - Conditional on previous join

MEMBERS_TABLE
member_id
---------------------------------------------
1
ACCOUNTS_TABLE
account_id member_id
---------------------------------------------
1 1
INVESTMENTS_TABLE
investment_id account_id
---------------------------------------------
1 1
2 1
FUNDS_TABLE
fund_id investment_id
---------------------------------------------
1 1
2 2
This is my current query:
SELECT
m.member_id,
a.account_id,
i.investment_id,
f.fund_id,
COUNT(a.account_id) AS member_accounts_total,
COUNT(i.investment_id) AS member_investments_total,
COUNT(f.fund_id) AS member_funds_total
FROM members AS m
LEFT JOIN accounts AS a ON m.member_id = a.member_id
LEFT JOIN investments AS i ON a.account_id = i.account_id
LEFT JOIN funds AS f ON f.fund_id = i.fund_id
I would like to see the following results:
member_accounts_total: 1
member_investments_total: 2
member_funds_total: 2
Instead, I am getting these results:
member_accounts_total: 2
member_investments_total: 2
member_funds_total: 2
I really don't want to write multiple queries for this.
Just need to change
COUNT(a.account_id) AS member_accounts_total,
to
COUNT( distinct a.account_id) AS member_accounts_total,
The reason you're getting 2 is because the left join on accounts to investments results in 2 records. To get a distinct count of members you need to add well... distinct.
Note you may have problems with the other totals as well (Distinct may be needed there as well in the long run...) say if a member had multiple accounts. you may get odd counts as well (if each account had the same investment... would you want to see the count only once or twice?

Left Outer Join with CONDITIONAL

I've done a good bit of research on how to do this and was not able to find anything. I don't think what I'm trying to do is too difficult, but I'm not sure where to go from here and wanted to post this question.
I have two tables, one (DEVICE) is a table of devices and one (USER_DEVICE) is mapping a user to a device (user is assigned a device via this mapping table):
DEVICE USER_DEVICE
id user_id device_id
------ ------- ----------
1 1 1
2 1 2
3 1 5
4 2 3
5
6
I'm trying to create an LEFT OUTER JOIN that will return all the devices from DEVICES currently NOT ASSIGNED to a specific user_id. For example, running this query for user_id 1 should return
DEVICE
id
------
3
4
6
And running this query for user_id 2 should return
DEVICE
id
------
1
2
4
5
6
I have created the following LEFT OUTER JOIN which successfully returns the following
SELECT id FROM DEVICE d LEFT OUTER JOIN USER_DEVICE ud ON d.id = ud.device_id WHERE ud.device_id IS null;
DEVICE
id
------
4
6
I'm not sure where I need to include the user_id=1 statement in the above sql. What I need is something to the effect of:
SELECT id FROM DEVICE d LEFT OUTER JOIN USER_DEVICE ud ON d.id = ud.device_id WHERE ud.user_id=1 AND ud.device_id IS null;
But this returns no rows. Any help on how to do this LEFT OUTER JOIN with a conditional statement looking for a specific user_id would be greatly appreciated! Thanks :)
You want to include the condition on the device in the on clause:
SELECT id
FROM DEVICE d LEFT OUTER JOIN
USER_DEVICE ud
ON d.id = ud.device_id and
ud.user_id = 1
WHERE ud.device_id IS null;

MySQL - select mutual rows

I have a table like this:
user_id | wants_user_id | state
And I want to select rows which are mutual and have a state column with value "yes" or "maybe".
Example data:
Row 1: 1 | 2 | "yes"
Row 2: 2 | 1 | "maybe"
Row 3: 3 | 2 | "yes"
In this case I would like to select row 1 and 2 and on wants_user_id (second column) I would like to join user data from table users, but I can't figure out the sql command.
Thank you for your help.
Use a self-join to get matching rows (I'm assuming "mutual rows" here means users that want each other):
SELECT *
FROM myTable a
INNER JOIN myTable b ON a.user_id = b.wants_user_id
AND b.user_id = a.wants_user_id
INNER JOIN users ua ON a.user_id = ua.id
INNER JOIN users ub ON b.user_id = ub.id
WHERE a.state IN ('yes', 'maybe')
AND b.state IN ('yes', 'maybe')
In this case, you will get two "duplicate" rows, one for 1<->2 and one for 2<->1. To eliminate this, you can add a AND b.user_id > a.user_id to the join condition - this will select only one row of each pair.

mysql: How to INNER JOIN a table but limit join to 1 result with the highest vote or count?

I have 2 tables. One is items and another is votes for those items.
Items table has: |item_id|name|post_date
Votes table has: |votes_id|item_id|answer|total_yes|total_no
What I want to do is show all items based on post_date and show the answer in the votes table with the HIGHEST total_yes. So I want to show only a SINGLE answer from the votes table with the highest total_yes vote.
I was trying:
SELECT a.*, b.* FROM Items a
INNER JOIN Votes b ON a.item_id = b.item_id
GROUP by a.item_id
ORDER by a.post_date DESC, b.total_yes DESC
But that doesnt work.
The result I would like to see is:
<item><answer><yes votes>
Buick | Fastest | 2 yes votes
Mercedes | Shiny | 32 yes votes
Honda | Quick | 39 yes votes
Any help is appreciated!
SELECT a.*, b.*
FROM Items a
LEFT JOIN Votes b on a.item_id = b.item_id
and b.total_yes = (select max(total_yes)
from Votes v
where v.item_id = a.item_id)
ORDER BY a.post_date DESC, b.total_yes DESC
N.B.: if you have for an item 2 answers with the same total_yes = max, you will have 2 rows for that item.
add LIMIT 1 to the end of your query :)
that will take only one record, but at the moment you're ordering by the date first, so you'll get the highest vote for the last date voted on. Is that what you want?
If you want the highest total vote regardless you'll need to order by that first.