In a tutorial I saw this SQL query:
SELECT a.id as a_id, a.info as a_info,
b.id as b_id, b.info as b_info
FROM stats AS a
JOIN stats as b
ON a.id != b.id
LIMIT 25
The query is working well, but now I would need to add the WHERE condition. I have the table users. The table stats contains the column user_id.
I try to obtain all users from the table stats if users.city=1 (for example), but I still cannot to find a way, how to achieve that...
My problem is, that I currently have no clue, how to put into the query another JOIN (for table users).
I would be grateful for every advice, thank you
Well, as you use the table stats two times in your query, you would actually need two joins for the users table as well. (for stats A and stats B)
SELECT a.id as a_id, a.info as a_info,
b.id as b_id, b.info as b_info
FROM stats AS a
JOIN stats AS b ON a.id != b.id
JOIN users AS a_users ON a.user_id = a_users.id
JOIN users AS b_users ON b.user_id = b_users.id
WHERE a_users.city = 1
OR b_users.city = 1
LIMIT 25
So this is what you've got...
This is what you return
SELECT a.id as a_id, a.info as a_info, b.id as b_id, b.info as b_info
This is where it comes from
FROM stats AS a
JOIN stats as b on a.id != b.id
This is how many you want
LIMIT 25
So if you want to add the users table, add this after the other joins
JOIN users ON a.user_id = users.id
and to filter it, add this after the joins
WHERE users.city=1
Giving
SELECT a.id as a_id, a.info as a_info, b.id as b_id, b.info as b_info
FROM stats AS a
JOIN stats as b on a.id != b.id
JOIN users ON a.user_id = users.id
WHERE users.city=1
LIMIT 25
Here is one way to do that:
SELECT a.id as a_id, a.info as a_info,
b.id as b_id, b.info as b_info
FROM stats AS a
JOIN stats as b
ON a.id != b.id
where a.user_id in (select user_id from users where users.city = 1)
LIMIT 25
You can also express this as a join (which in MySQL is more efficient):
SELECT a.id as a_id, a.info as a_info,
b.id as b_id, b.info as b_info
FROM stats AS a
JOIN stats AS b
ON a.id != b.id
JOIN users AS u
ON a.user_id = u.user_id
AND u.city_id = 1
LIMIT 25
For convenience, I've reformatted your original SQL statement to make it readable by humans.
SELECT a.id AS a_id
, a.info AS a_info
, b.id AS b_id
, b.info AS b_info
FROM stats a
JOIN stats b
ON a.id != b.id
LIMIT 25
Your current query is almost returning a Cartesian product. Every row from stats is getting matched with every row from stats, except for matching itself. (Assuming that stats.id is a primary key or unique key.)
To add a join to the users table, to limit rows returned from a, for example:
SELECT a.id AS a_id
, a.info AS a_info
, b.id AS b_id
, b.info AS b_info
FROM stats a
JOIN users au ON au.id = a.user_id AND au.city=1
JOIN stats b ON a.id != b.id
LIMIT 25
If you want to limit rows returned for both a and b, add another join to the users table:
SELECT a.id AS a_id
, a.info AS a_info
, b.id AS b_id
, b.info AS b_info
FROM stats a
JOIN users au ON au.id = a.user_id AND au.city=1
JOIN stats b ON a.id != b.id
JOIN users bu ON bu.id = b.user_id AND bu.city=1
LIMIT 25
This is not the only way to accomplish this. For example, you could use an a.user_id IN (subquery) or an EXISTS (subquery) predicate.
(SQL is much easier to work with if you have it formatted in a way that is readable.)
Related
i create a web app like facebook by php and mysqli
in my app i have a table for posts , one table for likes , and one table for comments
i want to get the number of comments and likes of each post in one row with his post_id!!!
i try some querys likes this :
select `tblpost`.`post_id`, COALESCE(TCOMM.`comment_num`,0) as `c_num`, COALESCE(TLIKE.`like_num`,0) as `l_num`
from
(select `tblpost`.`post_id`, count(*) as `like_num` from `tblpost` join `tbllikes` on `tbllikes`.`post_id` = `tblpost`.`post_id` group by `tblpost`.`post_id`
) TLIKE
inner join
(select `tblpost`.`post_id`, count(*) as `comment_num` from `tblpost` join `tblcomments` on `tblcomments`.`post_id` = `tblpost`.`post_id` group by `tblpost`.`post_id`) TCOMM
on
TCOMM.`post_id` = TLIKE.`post_id`
but i don't know what's my problem
You can do count distincts with two left joins.
Something like this would work if there are fields like_id and comment_id in the tables tbllikes and tblcomments
SELECT
tblpost.post_id AS post_id,
COUNT(DISTINCT tbllikes.like_id) AS likes,
COUNT(DiSTINCT tblcomments.comment_id) AS comments
FROM tblpost
LEFT JOIN tbllikes ON tbllikes.post_id = tblpost.post_id
LEFT JOIN tblcomments on tblcomments.post_id = tblpost.post_id
GROUP BY tblpost.post_id
First, I think you can greatly simplify your query:
select l.post_id,
COALESCE(c.comment_num, 0) as c_num, COALESCE(l.like_num, 0) as l_num
from (select l.post_id, count(*) as like_num
from tbllikes l
group by l.post_id
) l inner join
(select c.post_id, count(*) as comment_num
from tblcomments c
group by c.post_id
) c
on l.post_id = c.post_id;
This will only get you posts that have both likes and comments. To get what you want, use a left join:
select p.post_id,
COALESCE(c.comment_num, 0) as c_num, COALESCE(l.like_num, 0) as l_num
from tblpost p left join
(select l.post_id, count(*) as like_num
from tbllikes l
group by l.post_id
) l
on l.post_id = p.post_id left join
(select c.post_id, count(*) as comment_num
from tblcomments c
group by c.post_id
) c
on c.post_id = p.post_id;
Okay, so I have a query below I am trying to get to work.
Basically everything works up until the inner join of the 'votes' table. What I am trying to do is order the results of this query in accordance with the number of votes each content row has in another table called votes. I know I'm not too far off from what I need to do!
Thanks in advance!!
mysql_query("
SELECT content.id, content.type, content.title, content.url, users.username
FROM content
INNER JOIN users ON content.uploaderuid = users.id
INNER JOIN votes ON votes.id = content.id
WHERE (content.type = 'pic')
ORDER BY COUNT(votes.id) DESC");
Try doing:
SELECT content.id, content.type, content.title, content.url, users.username
FROM content
INNER JOIN users ON content.uploaderuid = users.id
INNER JOIN (
SELECT id,COUNT(*) as voteCount
FROM votes
GROUP BY id
) v ON v.id = content.id
WHERE (content.type = 'pic')
ORDER BY v.voteCount DESC
When you do an INNER JOIN with votes table directly, if you have multiple occurrences of the same id, you will get a lot more rows than before you did the JOIN.
If you are only interested in the number of votes for each id, by doing a JOIN with a subquery that calculates the count of votes for each id, will leave your previous query results as they were, and lets you use the voteCount to order by it.
You could try this:
SELECT c.id, c.type, c.title, c.url, u.username, COUNT(v.id) votes
FROM content c
INNER JOIN users u ON c.uploaderuid = u.id
INNER JOIN votes v ON v.id = c.id
WHERE c.type = 'pic'
GROUP BY c.id, c.type, c.title, c.url, u.username
ORDER BY votes DESC
Here is the SQL Fiddle that demonstrates the below query:
SELECT c.id, c.type, c.title, c.url, u.username, COUNT(v.id)
FROM content AS c
INNER JOIN users AS u ON c.uploaderuid = u.id
INNER JOIN votes AS v ON v.id = c.id
WHERE c.type = 'pic'
GROUP BY c.id, c.type, c.title, c.url, u.username
ORDER BY COUNT(v.id) DESC
Hello I added GROUP_CONCAT function to my query and that function killed my query :/.
My query is :
SELECT u.username,a.user_id,a.id,a.text,a.lang as fromLang,b.lang as toLang,GROUP_CONCAT(DISTINCT b.id) AS translation_ids FROM sentence as a
INNER JOIN sentence_relationship as sr ON
(sr.sentence_id = a.id)
INNER JOIN sentence as b ON
(b.id = sr.translation_id AND a.id = sr.sentence_id)
INNER JOIN users as u ON
(u.id = a.user_id) GROUP BY a.id LIMIT 10;
What is wrong with that query ?
This is your query (somewhat formatted):
SELECT u.username, a.user_id, a.id,a.text,a.lang as fromLang, b.lang as toLang,
GROUP_CONCAT(DISTINCT b.id) AS translation_ids
FROM sentence a INNER JOIN
sentence_relationship sr
ON sr.sentence_id = a.id INNER JOIN
sentence b
ON b.id = sr.translation_id AND a.id = sr.sentence_id INNER JOIN
users as u
ON u.id = a.user_id
GROUP BY a.id
LIMIT 10;
It is unclear from your question whether the group_concat() was added with the group by. That could slow things down.
The limit 10 is taking the first 10 a.ids that match (the group by does an implicit ordering). If you do this with a subquery, it will probably speed up the query:
SELECT u.username, a.user_id, a.id,a.text,a.lang as fromLang, b.lang as toLang,
GROUP_CONCAT(DISTINCT b.id) AS translation_ids
FROM (select s.*
from sentence s
order by a.id
limit 10
) a INNER JOIN
sentence_relationship sr
ON sr.sentence_id = a.id INNER JOIN
sentence b
ON b.id = sr.translation_id AND a.id = sr.sentence_id INNER JOIN
users as u
ON u.id = a.user_id
GROUP BY a.id;
This assumes that all the joins do work and match records. If the joins are used for filtering, then you may get fewer than 10 rows back.
I have this database design:
**users_tbl**
id
username
name
**posts_tbl**
id
url
users_id *FK REFERENCE to users table*
**posts_contents_tbl**
id
posts_id *FK REFERENCE to posts table
title
description
date
views
click
isDeleted
I'm using this query
SELECT a.name,a.username,c.*
FROM users_tbl a
LEFT JOIN posts_tbl b ON a.id = b.users_id
LEFT JOIN posts_contents_tbl c ON b.id = c.posts_id
ORDER BY c.id ASC
Why I try to run this query it gives me NULL results, sample output is like this
But when I try to remove the ORDER BY c.id ASC it gives me this output:
That's not my expected result.
My expected result would be it will display the posts_contents_tbl in Ascending order at the same time it won't show some null values. Some users in my database doesn't have posts data in the posts_tbl so they should not show too.
How would I do that one? Your help would be greatly appreciated and rewarded!
Thanks!
PS: I already have thousands record in my database.
In that case, you have to use INNER JOIN instead of LEFT JOIN because you only want users with posts to show. The reason why there are Null values is because the records are based on table users_tbl and you've mentioned that some of them have no post. Right?
Try this:
SELECT a.name,
a.username,
c.*
FROM users_tbl a
INNER JOIN posts_tbl b
ON a.id = b.users_id
INNER JOIN posts_contents_tbl c
ON b.id = c.posts_id
ORDER BY c.`date` DESC
I think this is what you are looking for:
SELECT a.name,a.username,c.*
FROM users_tbl a
INNER JOIN posts_tbl b
ON a.id = b.users_id
LEFT JOIN posts_contents_tbl c
ON b.id = c.posts_id
ORDER BY IFNULL(c.id, 0) ASC;
If you really needs that posts_tbl data should not display if not available.And all data of posts_contents_tbl then you need a RIGHT JOIN and INNER JOIN .
The Query like :-
SELECT a.name,a.username,c.*
FROM users_tbl a
INNER JOIN posts_tbl b ON a.id = b.users_id
RIGHT JOIN posts_contents_tbl c ON b.id = c.posts_id
ORDER BY c.id ASC;
A brief description... I have 4 tables. "contacts" (a list of each person, unique IDs), contact_phones (multiple telephone numbers for each contact, joins on contact_id), and contact_communication (each time we've spoken to this contact, joins on contact_id), and schools (a list of schools, joins schools.school_id = contacts.contact_id).
What I need: I need to look up an individual school. For that school, I need a list of each person that goes there, their main telephone number, and the last communication we had with them (if any).
The problem is, if we have had NO communication with them, they don't show up in the list. If I take out the "AND" statement in the "WHERE" clause, then I get more than one communication record. I only want the latest communication record, but I want all the contacts. Some contacts don't have a communication record though.
This is my query:
SELECT c.id,
c.f_name,
c.l_name,
c.address1,
c.address2,
c.city,
c.state,
c.zip,
c.tel,
c.school_id,
c.email,
ct.tel,
cc.date,
cc.reason,
cc.result,
cc.caller
FROM contacts AS c
LEFT OUTER JOIN contact_phones AS ct ON c.id = ct.contact_id
LEFT OUTER JOIN contact_communication AS cc ON c.id = cc.contact_id
WHERE school_id = '$schoolId' AND
cc.id IN (SELECT MAX(id) FROM contact_communication WHERE contact_id = c.id)
ORDER BY cc.date DESC
The problem is, this query gives me only the latest communication (which is all I want) but won't list contacts that have no communication.
I've been at this for 3 days. Any tips?
Thanks!!
(PS: I'll edit and give more info if needed.)
EDIT
The answer (thank you, ysrb) is changing my where clause:
SELECT c.id,
c.f_name,
c.l_name,
c.address1,
c.address2,
c.city,
c.state,
c.zip,
c.tel,
c.school_id,
c.email,
ct.tel,
cc.date,
cc.reason,
cc.result,
cc.caller
FROM contacts AS c
LEFT OUTER JOIN contact_phones AS ct ON c.id = ct.contact_id
LEFT OUTER JOIN contact_communication AS cc ON c.id = cc.contact_id
WHERE school_id = '$schoolId' AND
(cc.id IN (SELECT MAX(id) FROM contact_communication WHERE contact_id = c.id) OR cc.id IS NULL)
ORDER BY cc.date DESC
Try:
SELECT c.id,
c.f_name,
c.l_name,
c.address1,
c.address2,
c.city,
c.state,
c.zip,
c.tel,
c.school_id,
c.email,
ct.tel,
cc.date,
cc.reason,
cc.result,
cc.caller
FROM contacts AS c
LEFT OUTER JOIN contact_phones AS ct ON c.id = ct.contact_id
LEFT OUTER JOIN contact_communication AS cc ON c.id = cc.contact_id
WHERE school_id = '$schoolId' AND
(cc.id = (SELECT MAX(id) FROM contact_communication WHERE contact_id = c.id) OR cc.ID IS NULL)
ORDER BY cc.date DESC