Mysql subquery fails, need help :) - php

I have a table PICTURES:
username varchar(50)
picture_id varchar(50)
datetime
...and I have a table FRIENDS:
user_1 varchar(50)
user_2 varchar(50)
datetime
When you have friends on the website your username goes in user_1, and your friend username's go in user_2. For each new friend a new row...
I want to show the 5 last pictures of the friends of one user (user_1)
so I try
SELECT p.picture_id, p.datetime
FROM pictures AS p
WHERE p.username = (
SELECT f.user_2
FROM friends AS f
WHERE f.user_1 = '(ENTER USERNAME HERE)'
ORDER BY f.datetime DESC
LIMIT 5
)
ORDER BY p.datetime DESC;
And as you can see, the subquery return more than one row so... I need your help or suggestions to help me managing this solution!

Try using IN instead of = in WHERE p.username = (. Since you're selecting up to 5 rows = doesn't quite make sense.
SELECT p.picture_id, p.datetime
FROM pictures AS p
WHERE p.username IN (
SELECT f.user_2
FROM friends AS f
WHERE f.user_1 = '(ENTER USERNAME HERE)'
ORDER BY f.datetime DESC
LIMIT 5
)
ORDER BY p.datetime DESC;

I suggest you try a JOIN instead:
SELECT
p.picture_id, p.datetime
FROM
friends AS f
INNER JOIN pictures AS p ON f.user_2 = p.username
WHERE
f.user_1 = '(ENTER USERNAME HERE)'
ORDER BY
p.datetime DESC
LIMIT 5
This will give you the last 5 pictures from any of user_1's friends

I assume you mean you want the latest 5 pictures from each of the friends, not the latest 5 pictures among all the friends' pictures.
This is one of the greatest-n-per-group problems that appears so frequently on StackOverflow. Normally the problem is to find the top one from each group, but here's how I solve it when you want the top 5 or some other quantity:
SELECT p1.*
FROM friends AS f
JOIN pictures AS p1 ON (f.user_2 = p1.username)
LEFT OUTER JOIN pictures AS p2 ON (p1.username = p2.username
AND p1.datetime < p2.datetime)
WHERE f.user_1 = ?
GROUP BY p1.picture_id
HAVING COUNT(*) < 5;
Explanation: for each picture p1 that belongs to one of my friends, count the pictures belonging to the same friend and with a more recent datetime. The pictures that are in the most 5 recent must have fewer than 5 other pictures that are more recent.

Try changing the WHERE p.username =(subquery) to WHERE p.username in(subquery)
SELECT p.picture_id, p.datetime FROM pictures AS p WHERE p.username IN (SELECT f.user_2 FROM friends AS f WHERE f.user_1 = '(ENTER USERNAME HERE)' ORDER BY f.datetime DESC LIMIT 5) ORDER BY p.datetime DESC;

Related

PHP: 2 tables - Maximum id2

I want to get all id with the max id2 value.
I tried just to get the max id2 but then it will looks for the overall maximum value of id2 inside the table , but i want to get all maxiums of id.
So I got 2 tables - table news and table topics.
Everytime I create a news there will automaticly create a topic. Now I want to show all news - and the current number of replies. So first step - topicid = id.
and every topic got id and id2.
id is the topic id
and id2 is the reply id
so if i got topic (a) with 4 comments it would look like
(id(1),id2(1))
(id(1),id2(2))
(id(1),id2(3))
(id(1),id2(4))
now a new topic (b) with 6 comments
(id(2),id2(1))
(id(2),id2(2))
(id(2),id2(3))
(id(2),id2(4))
(id(2),id2(5))
(id(2),id2(6))
so i want to get ((id(1),id2(4)) and (id(2),id2(6)))
<?php
$news = "SELECT n.titel,n.datum,n.typ_news,n.news,n.verfasser,n.time,n.topicid,
t.id, t.id2 FROM news n LEFT JOIN topics t ON t.id = n.topicid
ORDER BY n.id DESC LIMIT 10 ";
$neuenews = mysql_query($news);
while ($dnews = mysql_fetch_array($neuenews))
{
echo " <div style='text-align:center;color:#FFFFFF;font-size: 24px;'> "
.$dnews['titel'].
"a";
}
Ehm this was the solution :
$dn1 = mysql_query('select c.id, c.name, c.description, c.position,c.bild,
(select count(t.id) from topics as t where t.parent=c.id and t.id2=1) as topics,
(select count(t2.id) from topics as t2 where t2.parent=c.id and t2.id2!=1) as replies
from categories as c group by c.id order by c.position asc');
Try this:
SELECT n.titel,n.datum,n.typ_news,n.news,n.verfasser,n.time,n.topicid, t.id, MAX(t.id2) AS id2
FROM news n
LEFT JOIN
topics t
ON t.id = n.topicid
GROUP BY n.topicid
ORDER BY n.id DESC LIMIT 10
It looks like the specified/desired result from the topics table is accomplished by a query like this:
SELECT t.id
, MAX(t.id2) AS max_id2
FROM topics t
GROUP BY t.id
OPTION 1
To get that result joined to rows in news, you could use that query as an inline view in your query in place of the topics table. For example:
SELECT n.titel
, n.datum
, n.typ_news
, n.news
, n.verfasser
, n.time
, n.topicid
, t.id
, t.max_id2
FROM news n
LEFT
JOIN ( SELECT m.id
, MAX(m.id2) AS max_id2
FROM topics m
GROUP BY m.id
) t
ON t.id = n.topicid
ORDER BY n.id DESC LIMIT 10
OPTION 2
If id is UNIQUE (or PRIMARY KEY) in news table, then you may be able to eliminate the inline view, do a join to topics, and do a GROUP BY n.id, something like this:
SELECT n.titel
, n.datum
, n.typ_news
, n.news
, n.verfasser
, n.time
, n.topicid
, t.id
, MAX(t.id2) AS max_id2
FROM news n
LEFT
JOIN topics t
ON t.id = n.topicid
GROUP BY n.id
ORDER BY n.id DESC LIMIT 10
Not clear enough. what are the columns id= 1 id2=1 etc.? Two tables each with 2 columns? No clue.
I'm thinking something on the order but No clue what you actually want.
SELECT `MAX(`id2`) as MAX,`id` FROM `News` WHERE `id2` = `MAX`

Select each first record with id in another table's result

I have two tables, Users(id, username) and Posts(id, user_id, content). I want to list a summary of them which, I want to list all users and the first post of each user. How can I realize this in one query?
I tried something like.
QUERY
SELECT Users.*, Posts.content
FROM Users, Posts
WHERE Posts.user_id=T_Users.id
But it will return all posts for each user (I cannot add LIMIT 1 at the tail of course which only returns one user).
Some sample records:
Users table:
id username
1 test1
2 test2
Posts table:
id user_id content
1 1 This is a test1's content.
2 1 This is another test1's content.
3 2 This is a test2's content.
And I want the result:
Users.id Users.username Posts.content
1 test1 This is a test1's content.
2 test2 This is a test2's content.
Here is one approach to get the latest record per user i assume the latest record will be considered as the minimum post id
SELECT u.*, p.content
FROM Users u
join Posts p on p.user_id=u.id
join (select user_id ,min(id) id from Posts group by user_id ) p1
on (p.id = p1.id and p.user_id = p1.user_id )
Demo
Try this
select min(x.id) as Id,x.user_id,x.content from(
select p.id,p.user_id,p.content from Users u inner join Posts p
on u.id=p.user_id and u.gender=1
)x group by x.user_id
Have you tried group by clause.
SELECT Users.*, Posts.content
FROM Users, Posts
WHERE Posts.user_id=T_Users.id GROUP BY Posts.user_id

mysql multiple COUNT() from multiple tables with LEFT JOIN

I want to show the conclusion of all users.
I have 3 tables.
table post
post_id(index) user_id
1 1
2 3
3 3
4 4
table photo
photo_id(index) user_id
1 2
2 4
3 1
4 1
table video
photo_id(index) user_id
1 4
2 4
3 3
4 3
and in table user
user_id(index) user_name
1 mark
2 tommy
3 john
4 james
in fact, it has more than 4 rows for every tables.
I want the result like this.
id name post photo videos
1 mark 1 2 0
2 tommy 0 1 0
3 john 2 0 2
4 james 1 1 2
5 .. .. .. ..
Code below is SQL that can work correctly but very slow, I will be true appreciated if you help me how it using LEFT JOIN for it. Thanks.
SQL
"select user.*,
(select count(*) from post where post.userid = user.userid) postCount,
(select count(*) from photo where photo.userid = user.userid) photoCount,
(select count(*) from video where video .userid = user.userid) videoCount
from user order by user.id"
(or ORDER BY postCount, photoCount or videoCount ASC or DESC as i want )
I done researched before but no any helped me.
SELECT u.user_id,
u.user_name,
COUNT(DISTINCT p.post_id) AS `postCount`,
COUNT(DISTINCT ph.photo_id) AS `photoCount`,
COUNT(DISTINCT v.video_id) AS `videoCount`
FROM user u
LEFT JOIN post p
ON p.user_id = u.user_id
LEFT JOIN photo ph
ON ph.user_id = u.user_id
LEFT JOIN video v
ON v.user_id = u.user_id
GROUP BY u.user_id
ORDER BY postCount;
Live DEMO
Your method of doing this is quite reasonable. Here is your query:
select user.*,
(select count(*) from post where post.userid = user.userid) as postCount,
(select count(*) from photo where photo.userid = user.userid) as photoCount,
(select count(*) from video where video.userid = user.userid) as videoCount
from user
order by user.id;
For this query, you want the following indexes:
post(userid)
photo(userid)
video(userid)
user(id)
You probably already have the last one, because user.id is probably the primary key of the table.
Note that a left join approach is a bad idea in this case. The three tables -- posts, photos, and videos -- are independent of each other. If a user has five of each, then joining them together would produce 125 intermediate rows. If a user has fifty of each, it would be 125,000 -- a lot of extra processing.
Your answer is probably slow as it is using a correlated sub-query i.e. the sub query is running once for each user_id (unless the optimizer is doing something smart - which shouldn't be counted on).
You could use a left outer join and count or use something temporary like:
SELECT u.user_id,
u.user_name,
ph.user_count AS 'photoCount',
p.user_count AS 'postCount',
v.user_count AS 'videoCount'
FROM user u
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM photo
GROUP BY user_id
) ph
ON ph.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM post
GROUP BY user_id
) p
ON p.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM video
GROUP BY user_id
) v
ON v.user_id=u.user_id
There are pros and cons for both (depending on indexes). Always have a look at the query plan (using EXPLAIN for MySQL).

Top users(comments)

I have a users table and a comments table and I want to select from users the top users that have the biggest amount of comments from the comments table and order them by numbers of comments
Just the forum comments should be counted(type can be forum,picture,news etc)
table structure
users
id | username | password
comments
id | text | type | author_id
SELECT
u.id,u.username,u.password ,count(c.id) as total
FROM
users u
JOIN comments c on c.author_id = u.id
GROUP BY u.id
ORDER BY total DESC;
This should do the trick, try and let us know how it worked out for you
UPDATE
For details on how to do this you will fall inlove with this article
Update : New Link, old one is broken Databases
Tested on PostgreSQL 8.1:
select users.id, count(comments.author_id)
from users, comments
where users.id = comments.author_id
group by users.id
order by 2 desc
SELECT users.id, users.name, COUNT(comments.id) AS cnt
FROM users
LEFT JOIN comments ON users.id = comments.author_id
WHERE type IN ('forum', 'picture', 'news', 'etc')
GROUP BY users.id
ORDER BY cnt DESC
LIMIT 10
Get the 10 most prolific commenter where the comment type is 'form', 'picture', 'news', or 'etc'.

Multiple tables, multiple statement

Let's take 3 tables that has all tons of rows:
TABLE Posts
PostPID
PostUID
PostText
TABLE Users
UserUID
UserName
TABLE Favorites
FavoriteUID
FavoritePID
Now, in order to get all the recent posts I perform a query such as:
SELECT p.PostPID, p.PostUID, p.PostText, u.UserUID, u.UserName
FROM Posts AS p
JOIN Users AS u
ON p.PostUID = u.UserUID
ORDER BY p.PostPID DESC
LIMIT 0, 30
Which works fine. Now I was wondering, how could I get only the posts a certain UserUID prefers? So only the one with FavoriteUID = UserUID = X?
You could use a subquery.
...
Where p.PostUID in (select f.FavoritePID from Favorite f where f.FavoriteUID = UserUID)
...
second join will do the same
SELECT
p.PostPID, p.PostUID, p.PostText, u.UserUID, u.UserName
FROM
Posts AS p
JOIN
Users AS u ON p.PostUID = u.UserUID
Join
Favorites as f on f.FavoriteUID = u.UserUID and f.FavoritePID=p.PostPID
ORDER
BY p.PostPID DESC
LIMIT 0, 30

Categories