Mysql Left join multiple 1 to many tables - mysql

I am trying to get the following data from 3/4 tables in 1 Mysql query, wondering if it is possible ? The tables are
TOPIC
topicid (FK)(PK)
groupid
topic
user
LIKED
likeid
topicid (FK)
user
COMMENT
commentid (PK)
topicid (FK)
comment
user
I write my topics and store in TOPIC Table with unique topicid. I group each topic using groupid.
Other tables may have 0 or more data per topicid.
I am trying to get each topic for a particular group and also get other datas from the concerned Tables. I checked How to left join multiple one to many tables in mysql? and got few idea but that is for the count while I wanted to get details from that table (users who like), and (user and their comment).
I have tried
SELECT t.topicid,
topic,
group_concat(DISTINCT likeid,l.user SEPARATOR '|'),
group_concat(DISTINCT commentid,comment,c.user SEPARATOR '|') AS comments
FROM TOPIC t
LEFT JOIN LIKE l ON l.topicid = t.topicid
LEFT JOIN COMMENT c ON c.topicid = t.topicid
WHERE t.groupid='some_value'
GROUP BY t.topicid
While this works partly e.g. I do get the details but only if there is one topic in a group. If there are 2 or more topics in a group then the concat details are stored in the first record only and the later topics show no likes and comments.
Can someone please help me to correct this or any particular Mysql function I am missing

I am very very sorry for wasting your time, after thorough re-check I found my table data were wrong (checked after making sqlfiddle thanks #Barmar).
I was by mistake inserting wrong data in like and comment table. So Likes and comments for 2nd topic topicid='2' of groupid='1' were inserted by mistake as topicid='1' that is why the details only showed in 1st topic and nothing came out for second topic.
The SQL above is absolutely correct and thankyou for helping me find the fault.
Extremely sorry for posting again.

Related

MySQL Return least common rows based on another Table

I was wondering if I could get some help please? Apologies for the title, it was hard to summarise what I needed.
I have 2 tables, one contains a bank of questions and the other one stores people's responses to said questions. Both are linked by a unique identifier string.
What I need is to be able to order the questions in the question bank based on which ones have the least amount of entries in the answers table.
Is this easy to do? I can elaborate if this isn't clear enough or provide examples of tables etc... if needed.
Many thanks.
I think I figured it out using the below query, thanks for having a look at my question though guys.
SELECT DISTINCT Question_Bank.*,
(
SELECT COUNT(ID)
FROM Question_Answers
WHERE Question_ID = Question_Bank.Question_ID
) AS cnt
FROM Question_Bank
JOIN Question_Answers
ORDER BY cnt ASC

How can I efficiently query 'Like' data from MySQL?

I am developing a social network iOS app using a PHP backend with a MySQL database. I have 3 tables in my database to store Posts, Comments and Likes.
Currently, the app requests up to 50 posts at a time which includes various specific data. I do this using a SELECT query from MySQL. My 'Likes' are stored in the 'Likes table' as 1 Like per row. This includes their username and which post they 'liked'.
When the app is refreshed, I need to gather the 'Likes' data on the posts that have just been loaded. My problem is that I do not know the best way to query MySQL to return all associated likes with that user. This means that when I load some posts, I want the user to see which posts they have already liked.
Here is a visual description of my problem:
Please understand that up to 50 of these posts will be shown at one time.
There is several way to do this and depending on your project you might want to optimise my answer.
I assume your Likes table has a structure of (int) id, (int) user_id, (int) post_id.
To answer directly to your question (about doing a query only for the refresh), the next MySQL query will give you the list of post_id liked by the user. You need to replace %user_id% by the id of the user and %post_ids% by the list of the 50 posts ids, separated by a comma:
SELECT post_id
FROM Likes
WHERE user_id = %user_id% AND
post_id IN (%post_ids%)
However, if the refresh implies the whole page (and not only an AJAX request or so on) I would consider implementing this directly in the query getting the posts. For example :
SELECT p.id, p.content, p...., l.id AS `Liked`
FROM Posts AS p
LEFT JOIN Likes AS l
ON p.id = l.post_id AND
l.user_id = %user_id%
WHERE ...
Please also consider adding indexes on the right columns.
As promised, my solution is here:
SELECT `posts`.`postnumber`, `posts`.`message`, `likes`.`didLike`
FROM `posts`
LEFT JOIN `likes`
ON `posts`.`postnumber`=`likes`.`post_id`
AND `likes`.`user_id`='USERNAME';

Joining on the same table more than once

I'm probably being a bit dumb, hopefully someone can help.
I have a simple 2 column user table (ID, USERNAME).
I have a comments table for images (COMMENT, COMMENTFROM, COMMENTTO)
COMMENTFROM is the ID of the user who made the comment. COMMENTTO is the ID of the owner of the image that the comment was added to. Both users are held within the USERS table.
I want to pull out and display rows like this
"really nice photo" - to USERXYZ - from USER123**
This has puzzled me, because if I join the USERS table to the comments table on:
WHERE comments.userfrom = users.id
That only gets me one (or the other) of the 2 usernames I need per row. Is there a way I can get both?
I'm not even sure how I would search for this answer on SOF, apologies if it has been answered before. If anyone can point me in the right direction it would be appreciated :)
You need to JOIN to the users table twice, and give them different identifiers (aka aliases) on each JOIN within your SQL.
SELECT
comment,
userFrom.username AS commentFrom,
userTo.username AS commentTo
FROM comments
JOIN users AS userFrom ON userFrom.ID = comment.commentFrom
JOIN users AS userTo ON userTo.ID = comment.commentTo
Please try following one
SELECT CONCAT(c.comment," - to ",
(SELECT USERNAME FROM user WHERE user.ID = c.COMMENTTO LIMIT 1),
" - from ",
(SELECT USERNAME FROM user WHERE user.ID = c.COMMENTFROM LIMIT 1)) FROM comments c

Database Design/SQL Optimisation: WHERE <id> NOT IN (thousands of IDs)

I'v been asked to add functionality to an application that lets users vote between two options: A and B. The table for those questions is very basic:
QUESTIONS
question_id (PK)
option_id_1(FK)
option_id_2(FK)
urgent (boolean)
Each time a user votes, that the user has voted is stored in an equally simple table:
USER VOTES
vote_id (PK)
user_id (FK)
question_id (FK)
The algorithm for selecting which question appears when a user requests a new one is complex, but for our purposes we can assume it's random. So, the issue?
Each user will be voting on many questions. Likely hundreds, and possibly thousands. I need to ensure no user is presented with a question they've already voted on, and the only way I can think to do that will, I'm guessing, pound the server into oblivion. Specifically, something like:
SELECT * from questions WHERE question_id NOT in (SELECT question_id from user_votes WHERE user_id = <user_id>) ORDER BY RAND() LIMIT 1.
[Note: RAND() is not actually in the query - it's just there as a substitute for a slightly complex (order_by).]
So, keeping in mind that many users could well have voted on hundreds if not thousands of questions, and that it's not possible to present the questions in a set order...any ideas on how to exclude voted-on questions without beating my server into the ground?
All advice appreciated - many thanks.
JOIN operator perform much better than nested queries in MySQL(that might have changed with latest MySQL releases but if you are experiencing performance problems than i guess my statement still holds)
what you could do is simply left join votes onto questions and only pick those records where none votes were joined(nobody voted):
SELECT *
FROM questions q
LEFT JOIN user_votes uv ON
uv.question_id = q.question_id AND
uv.user_id = '<user_id>'
WHERE vote_id IS NULL
RAND() is nasty however this may mitigate the problem while giving you the results you need. Seeing as you have mentioned that the RAND() is an example, I can't really provide more specific suggestions than that below however replacing the ORDER BY should work just fine.
The more you are able to limit the number of rows in the inner query, the faster the entire query will perform.
SELECT
q.*
FROM (
-- First get the questions which have not been answered
SELECT
questions.*
FROM questions
LEFT JOIN user_votes
ON user_votes.question_id = questions.question_id
AND user_votes.user_id = <user_id>
WHERE user_votes.user_id IS NULL
) q
-- Now get a random 1. I hate RAND().
ORDER BY RAND()
LIMIT 1

MYSQL joins not working as expected

I am trying to create a join statement to solve my problem but cannot get my head around it.
I am a new to join statements so please bear with me if my sql statement is nonsense.
I have two tables, one is a table of questions where users have asked questions about items for sale.
Second is a table of items that the user has asked a question about.
Table one called questions consists of question_ref, questioner_user_ref, item_ref, seller_ref, question_text, timestamp
questions
===========
+--------------+--------------------+---------+-----------+--------------+----------+
| question_ref |questioner_user_ref |item_ref |seller_ref |question_text |timestamp |
+--------------+--------------------+---------+-----------+--------------+----------+
Table two called my_item_comments consists of questioner_ref, item_ref, last_question_ref
my_item_comments
===========
+---------------+---------+------------------+
|questioner_ref |item_ref |last_question_ref |
+---------------+---------+------------------+
I have set up table two to keep track of items that the user has asked questions about so they can be informed when someone else asks the seller a question or the seller answers.
So I want to create a recordset of questions that
a). Someone has answered a question about an item the user is selling
b). A seller has replied to a question the user asked,
c). A third user has asked a question about an item that the user has also asked a question about.
A bit like facebook's commenting system, where you are informed about comments people have made on statuses that you have commented on.
So my current sql statement is as follows
$user_ref= current logged in user
$sql=mysql_query("
SELECT * FROM questions
LEFT JOIN my_item_comments
ON questions.item_ref=my_item_comments.item_ref
WHERE questions.questioner_user_ref!='$user_ref'
AND (questions.seller_ref='$user_ref' OR questions.item_ref=my_item_comments.item_ref
ORDER BY timestamp DESC");
The results don't work and I think its because of the OR questions.item_ref=my_item_comments.item_ref but for the life of me I cannot work it out.
Any help would be greatly appreciated, even if it means restructuring my database with more tables or new fields in the tables.
thanks in advance, Barry
SELECT * FROM questions
LEFT JOIN my_item_comments ON questions.item_ref=my_item_comments.item_ref
WHERE
questions.questioner_user_ref != '$user_ref' AND
(questions.seller_ref='$user_ref' OR questions.item_ref=my_item_comments.item_ref)
ORDER BY timestamp DESC
I think you were missing a ) to enclose the OR clause (in the example above I added it)
You should try to improve readability for yourself and us so that it is easier to debug and easier for us to help you :-)
$q = "
SELECT *
FROM questions
RIGHT JOIN my_item_comments ON questions.item_ref = my_item_comments.item_ref
WHERE questions.questioner_user_ref != '".$user_ref."'
AND ( questions.seller_ref = '".$user_ref."'
OR questions.item_ref=my_item_comments.item_ref
)
ORDER BY timestamp DESC";
$rs = mysql_query($q);
And preferably just the query with dummy data
SELECT *
FROM questions
RIGHT JOIN my_item_comments ON questions.item_ref = my_item_comments.item_ref
WHERE questions.questioner_user_ref != 'dummy_ref'
AND ( questions.seller_ref = 'dummy_ref'
OR questions.item_ref=my_item_comments.item_ref
)
ORDER BY timestamp DESC
Try a right join for your query. Left join will indeed get all the rows.