selecting from multiple tables (mysql) - mysql

table 1
id | question
1 | who will win the election
table 2
id | answers | question id
1 | I will | 1
table 3
id | photo | question id
1 | xy.gif| 1
table 4 * users can both suggest questions and vote for others
id | username
1 | joe
table 5
id | vote | question_id | user_id
1 | He | 1 | 1
What is the query that will get me the following information in one query
t1.* (all the questions)
t2 all the answers connected to the questions
t3 all the photos related to the questions
t4 usernames of the author for each question
t5 the votes for the questions (it is possible that some questions will not have a vote of the logged in user)
my problem is the last point, getting the votes (while not all questions have votes by the specific logged user)
Here is how my query looks like:
SELECT
poll_questions.id,
poll_questions.question,
poll_questions.qmore,
poll_questions.total_votes,
poll_questions.active,
poll_questions.created_at,
poll_answers.answer,
poll_answers.votes,
poll_answers.id AS answer_id,
poll_photos.photo_name_a,
vote_history_raw.vote,
users.username
FROM poll_questions
LEFT JOIN (poll_answers, poll_photos)
ON (poll_answers.question_id = poll_questions.id AND
poll_photos.question_id = poll_questions.id
)
LEFT JOIN users ON poll_questions.author = users.id
INNER JOIN vote_history_raw ON users.id = vote_history_raw.user_id
WHERE poll_questions.active = 1
ORDER BY poll_questions.created_at DESC
thanks much!

Didn't try it, but does this work?
SELECT
poll_questions.id,
poll_questions.question,
poll_questions.qmore,
poll_questions.total_votes,
poll_questions.active,
poll_questions.created_at,
poll_answers.answer,
poll_answers.votes,
poll_answers.id AS answer_id,
poll_photos.photo_name_a,
vote_history_raw.vote,
users.username
FROM poll_questions
LEFT JOIN (poll_answers, poll_photos)
ON (poll_answers.question_id = poll_questions.id AND
poll_photos.question_id = poll_questions.id
)
LEFT JOIN users ON poll_questions.author = users.id
LEFT JOIN vote_history_raw ON (users.id = vote_history_raw.user_id OR users.id <> vote_history_raw.user_id AND vote_history_raw.user_id IS NOT NULL)
WHERE poll_questions.active = 1
ORDER BY poll_questions.created_at DESC

Related

mysql search with two joins and a count

I am currently trying to write a general query which returns the content of 1 table and another joined table plus the count of resulting rows from a third table.
Now my description might seem abstract so I'll try to visualize it
Tables:
posts
| ID | title | description | creator_id |
1 Title1 Descr1 1
2 Title2 Descr2 1
users
| ID | name | avatar |
1 User1 PATH
interactions
| ID | type | target_id | identifier |
1 view 1 IP
2 view 1 IP
Now what I am looking for is an output like this:
| ID | title | description | name | avatar | view_count |
1 Title1 Descr1 User1 PATH 2
2 Title2 Descr2 User1 PATH 0
My current query looks like following:
SELECT
posts.id, posts.title, posts.description,
users.name, users.avatar,
COUNT(interactions.id) AS view_count
FROM
posts
LEFT JOIN
users
ON
posts.creator_id = users.id
LEFT JOIN
interactions
ON
posts.id = interactions.target_id
But only prints out the posts result which has an interaction like this:
| ID | title | description | name | avatar | view_count |
1 Title1 Descr1 User1 PATH 2
How do I need to alter the query in order to also get the other rows which happen to not have any interactions yet?
Thank you for your help!
You can simply subquery third table to count entries:
SELECT
posts.id, posts.title, posts.description,
users.name, users.avatar,
(SELECT COUNT(*) FROM interactions i WHERE i.target_id = posts.id) AS view_count
FROM
posts
LEFT JOIN
users
ON
posts.creator_id = users.id
This is also better for performance (no groups, no unoptimized joins)
Try this:
SELECT P.ID
, P.title
, P.description
, U.name
, U.avatar
, IFNULL(COUNT(I.ID), 0) AS view_count
FROM posts P
LEFT JOIN users U ON U.ID = P.creator_id
LEFT JOIN interactions I ON I.target_id = P.ID
GROUP BY P.ID
It seems like you missed the GROUP BY clause. Without this, when you use an aggregate function like COUNT, the documentation says:
there is a single group and it is indeterminate
which name value to choose for the group
That's why your query only returned 1 row.
Try this;)
select posts.id, posts.title, posts.description, users.name, users.avatar, coalesce(t3.view_count, 0) as view_count
from posts
left join users on posts.creator_id = users.id
left join (
select target_id, count(1) as view_count from interactions group by target_id
) t3 on posts.id = t3.target_id
SQLFiddle HERE

How to get data from table by intermediaries

I have three table:
// users
+----+------+------------+
| id | name | reputation |
+----+------+------------+
// posts
+----+-------+---------+
| id | title | user_id |
+----+-------+---------+
// votes
+----+---------+---------+
| id | user_id | post_id |
+----+---------+---------+
Note: user_id in votes is belong to who gives a vote. But user_id in posts table is belong to who wrote that post.
So I want to give +5 rep to the owner of post when his comment get a upvote.
Example: when userA gives a upvote to the post (wroted by userB), I want to run this:
update users set reputatuin=reputation+5 where id = {how get the id of userB}
Now I want to know, how should I obtain the userB (post owner who wrote it) id ?
In an UPDATE statement, you must join through posts to the votes table using MySQL's multi-table UPDATE syntax.
If you want to update by targeting the votes.id of the new vote, use that in the WHERE clause.
UPDATE
users
INNER JOIN posts ON users.id = posts.user_id
INNER JOIN votes ON votes.post_id = posts.id
SET
users.reputation = users.reputation + 5
WHERE votes.id = {vote id to update}
If your code is already aware of the posts.id of the voted post, then the votes table need not be joined and you can use users and posts.
UPDATE
users
INNER JOIN posts ON users.id = posts.user_id
SET
users.reputation = users.reputation + 5
WHERE posts.id = {post id of new vote}
This query can be done just as easily with a subquery in the WHERE clause.
UPDATE users
SET reputation = reputation + 5
WHERE
id = (SELECT user_id FROM posts WHERE post_id = {post id of new vote})

MySQL Many to Many query confusion

I've looked at a bunch of questions and solutions regarding many to many queries. I just can't seem to wrap my head around it. Maybe I'm not completely understanding the keywords in MySQL. But...
I have 3 tables. The first table is a list of peoples contact information. The second table is a list of mailing list categories. The third table is an associative table that holds the id's from the first and second table. How would I write a MySQL query to get all the contacts from the contact table that match the VIP list id (which I already have)?
Table 1 (contacts)
id | name | email
-----------------------------
1 | John | john#gmail.com
-----------------------------
2 | Jane | jane#gmail.com
-----------------------------
Table 2 (list_type)
id | list_name |
-----------------
1 | VIP's |
-----------------
2 | Generic |
-----------------
Table 3 (list_contact_joiner)
contact_id | list_type_id |
----------------------------
1 | 2 |
----------------------------
2 | 1 |
----------------------------
This is what I tried but get a syntax error
$listID = 1;
SELECT list_contact_joiner.contact_id
FROM list_contact_joiner
WHERE list_id = $listID AS lcj
INNER JOIN contact_lists AS cl
ON cl.id = lcj.contact_id
SELECT c.*
FROM contacts c
JOIN list_contact_joiner j on j.contact_id = c.id
JOIN list_type t on j.list_type_id = t.id
WHERE t.list_name = 'VIP''s'
If you already have the id of VIP's then you need to join only 2 tables
SELECT c.*
FROM contacts c
JOIN list_contact_joiner j on j.contact_id = c.id
WHERE j.list_type_id = 1
Yes the join statement is not correct. It should be something as
select
c.name,
c.email,
lt.list_type
from list_contact_joiner lcj
join contacts c on c.id = lcj.contact_id
join list_type lt on lt.id = lcj.list_type_id
where
lt.id = ?
If you are looking for data with $listID = 1; then the place holder is
lt.id = ?
Your syntax error is because all the table specification (FROM and JOIN) has to occur before the WHERE and you got your alias syntax a little messed up - this should work better:
$listID = 1;
SELECT lcj.contact_id
FROM list_contact_joiner AS lcj
INNER JOIN contact_lists AS cl
ON cl.id = lcj.contact_id
WHERE lcj.list_id = $listID
But if all you are trying to do is get the contact_id then you don't need to do any join at all...
SELECT contact_id
FROM list_contact_joiner
WHERE list_id = $listID
If you need the rest of the contact information then you might try this:
SELECT contact.*
FROM list_contact_joiner
JOIN contacts ON contacts.id = list_contact_joiner.contact_id
WHERE list_id = $listID
Note that you still don't need to get the list_type table at all.

Select with Multiple Counts on Left Join on Same Table

I'm not certain that this can be done, I have a table of users with a related table of user activity joined on a foreign key. Activity has different types, e.g. comment, like etc. I need to get users filtered by the number of each different type of activity.
What I have so far is this:
SELECT
users.*,
COUNT(t1.id) AS comments,
COUNT(t2.id) AS likes
FROM users
LEFT JOIN activity AS t1 ON users.id = t1.user_id
LEFT JOIN activity AS t2 ON users.id = t2.user_id
WHERE t1.activity_type_id = 1 AND t2.activity_type_id = 2
GROUP BY users.id
HAVING comments >= 5 AND likes >= 5
This seems to be close but it's returning a user with a count of 5 both likes and comments, when in reality the user has 5 likes and 1 comment.
To be clear I want this query to return users who have 5 or more likes and also users who have 5 or more comments.
UPDATE:
I've created an SQL Fiddle. In this case I have 3 users:
User 1: 6 comments, 8 likes
User 2: 3 comments, 2 likes
User 3: 5 comments, 2 likes
I want the query to return only user 1 and 3, with their respective totals of likes and comments.
http://sqlfiddle.com/#!2/dcc63/4
You can use conditional summing to do the count and due to the way MySQL treats boolean expressions an expression like sum(case when et.name = 'comment' then 1 else 0 end) (the "normal" SQL syntax) can be reduced to sum(case when et.name = 'comment').
SELECT
u.id,
sum(et.name = 'comment') AS comments,
sum(et.name = 'like') AS likes
FROM users AS u
LEFT JOIN engagements AS e ON u.id = e.user_id
JOIN engagement_types AS et ON e.engagement_type_id = et.id
GROUP BY u.id
HAVING sum(et.name = 'comment') >= 5
OR sum(et.name = 'like') >= 5
Result:
| ID | COMMENTS | LIKES |
|----|----------|-------|
| 1 | 6 | 8 |
| 3 | 5 | 2 |
Sample SQL Fiddle

"Friend" relationships across two mysql tables

I'm not quite sure how to construct an sql join inorder to find who a specific users "friends" are.
For example I have two table
User Table
u_ID | u_Name
-------------
1 | bob
2 | jill
3 | jack
4 | susan
Friends Table
f_ID | u_ID1 | u_ID2
--------------------
1 | 1 | 2
2 | 3 | 1
3 | 4 | 2
I need to find a way of getting all of bobs friends, or all of jills friends, for example.
Friends cannot have duplicate results
I.e. can have a row with either (u_ID1 = 1, u_ID2 = 2) or (u_ID1 = 2, u_ID = 1) but not both, as they are technically the same.
Here is my incorrect query
SELECT u.u_Name
FROM user u
INNER JOIN friends f ON (f.u_ID1 = '1' OR f.u_ID2 = '1')
Thanks in advance
Solution
Check Kris Babic reply for solution,
also thank you for everyone elses help
This uses a STRAIGHT join, but should work:
select u.u_Name
from friends f, user u
where (f.u_ID1 = '1' and u.u_ID = f.u_ID2)
or (f.u_ID2 = '1' and u.u_ID = f.u_ID1)
For all of bob's friends this should work (untested)
select u.u_Name
FROM user u
inner join friends f1 on f1.u_uID1 = u.u_ID
inner join friends f2 on f2.u_uID2 = u.u_ID
where u.u_ID = 1
Try this:
SELECT u1.u_Name as user1 , u2.u_Name as user2
FROM user as u1 INNER JOIN friends ON u1.u_ID=friends.u_ID1
INNER JOIN user as u2 ON u2.u_ID=friends.u_ID2