I have 5 tables [structure] :
"medias" to store pictures [id, creatorID (user who create the media), date]
"likes" to store likes on pictures [id, senderID (user who liked), mediaID (media liked)]
"comments" to store comments on pictures [id, mediaID (media commented)]
"follow" to store a follow [id, follow (user X), following (one following of the user X)]
"users" to store users [id]
All tables are made with an ID which increment at insert.
Here my request to display a flux of pictures for an user :
SELECT
m.id as mediaID,
COUNT(l.id) as likesCount,
COUNT(c.id) as commentsCount
FROM medias m
INNER JOIN follow f
ON f.follow = 'user_here' AND m.creatorID = f.following AND m.date < 'timestamp_here'
INNER JOIN users u
ON u.id = m.creatorID
LEFT JOIN likes x
ON m.id = x.mediaID AND x.senderID = 2
LEFT JOIN likes l
ON m.id = l.mediaID
LEFT JOIN comments c
ON m.id = c.mediaID
GROUP BY m.id
When there's more than 1 comment, likesCount take the value of the commentsCount. And when I dislike a picture, the commentCount decrement of 1 comment. So, I really don't know how can I solve it...
The easy way to solve your problem is to use distinct:
SELECT m.id as mediaID,
COUNT(DISTINCT l.id) as likesCount,
COUNT(DISTINCT c.id) as commentsCount
If you have lots of likes and comments, a better way may be to aggregate before joining or use a correlated subquery.
Related
There are 3 tables, named as account_has_account1, account_has_photos, photos_has_message_photos where account_has_account1 have columns account_id, account_id1, status, type_id and this table takes care on storing accounts following to another account
account_has_photos stores information about all the photos one account has uploaded, it's columns are photos_id, account_id, type_id, this also stores likes according to type_id
photos_has_message_photos stores all messages posted to a photo, its a relational table from photos and message_photos
i need to fetch a count of all likes from account_has_photos where type_id = 1 which points to like from table type
i have done this SQL:
SELECT account_has_photos.photos_id as id, "photos" as type, account_has_photos.update_at, account_has_photos.account_id, posts.total as total_messages, likes.total as total_likes
FROM account_has_account1
INNER JOIN account_has_photos
ON (account_has_photos.account_id = account_has_account1.account_id1 AND account_has_photos.type_id = 17)
INNER JOIN (
SELECT photos_has_message_photos.photos_id, count(*) as total
FROM photos_has_message_photos
GROUP BY photos_has_message_photos.photos_id
) posts
ON(posts.photos_id = account_has_photos.photos_id)
INNER JOIN (
SELECT account_has_photos.photos_id, COUNT(account_has_photos.photos_id) as total
FROM account_has_photos
WHERE account_has_photos.type_id = 1
) likes
ON (likes.photos_id = account_has_photos.photos_id)
WHERE account_has_account1.account_id = 7 AND account_has_account1.`status` = "Active"
the first INNER JOIN account_has_account1 is for showing all accounts that one account is following, The second INNER JOIN photos_has_message_photos is only for getting the count of all posted messages from a account's photos.
At this point everything goes fine, but when i insert the third INNER JOIN account_has_photos again, the query result is now 0, the purpose of this third JOIN is for getting a count of likes a photo has which is stored in account_has_photos where type_id = 1
The rest is for setting the general conditions for the search.
Again the problem only happens in this query
INNER JOIN (
SELECT account_has_photos.photos_id, COUNT(account_has_photos.photos_id) as total
FROM account_has_photos
) likes
ON (likes.photos_id = account_has_photos.photos_id)
it could be that no likes are found on any photo, i have made the test for searching it separately and as i said, there is no like made on any photo, i didn't add any record because i want it to say 0 on count as it is going to be shown alot
here is much different way to write your query that should yeild the same results.
SELECT
account_has_photos.photos_id as id
,"photos" as type
,account_has_photos.update_at
,account_has_photos.account_id
,COUNT(photos_has_messages.photos_id) as total_messages
,COUNT(DISTINCT likes.photos_id) as total_likes
FROM
account_has_account1
INNER JOIN account_has_photos
ON account_has_photos.account_id = account_has_account1.account_id1
AND account_has_photos.type_id = 17
LEFT JOIN photos_has_message_photos
ON photos_has_message_photos.photos_id = account_has_photos.photos_id
LEFT JOIN account_has_photos likes
ON likes.photo_id = account_has_photos.photo_id
AND likes.type_id = 1
WHERE account_has_account1.account_id = 7 AND account_has_account1.`status` = "Active"
GROUP BY
account_has_photos.photos_id
,"photos"
,account_has_photos.update_at
,account_has_photos.account_id
I would recommend changing:
,COUNT(photos_has_messages.photos_id) as total_messages
to
,COUNT(DISTINCT photos_has_messages.WhateverTablesUniqueIdIs) as total_messages
Also this line
,COUNT(DISTINCT likes.photos_id) as total_likes
will always give you 1. so if likes does repeat photo_id then you also want to count whatever that account_has_photos unique identifier is....
Your last subquery is missing a GROUP BY. Try this:
INNER JOIN (
SELECT account_has_photos.photos_id, COUNT(account_has_photos.photos_id) as total
FROM account_has_photos
GROUP BY account_has_photos.photos_id
) likes
ON likes.photos_id = account_has_photos.photos_id
You may also want to replace the INNER JOINs with LEFT OUTER JOIN if you want rows with no likes.
I'm trying to get data from three tables (photos, albums, album_photos),
then the program searches a user's albums in the album table, then look for every album the ID's of the photos in album_photos, and then, for each ID, look at the photos table all data by ID.
Yesterday I asked something like this: Inner join with 3 tables, but now, I think the question is different, I'm wondering how I can add a limit to a request by inner join.
So, I'm working now in this code:
SELECT a.album_name, a.album_id, c.*
FROM albums a
INNER JOIN album_photos b ON a.album_id = b.album_id
INNER JOIN photos c ON b.photo_id = c.photo_id
WHERE (
SELECT COUNT(*)
FROM album_photos d
WHERE b.album_id = d.album_id
AND d.nick = :nick
) <=5
Ok, this code select's the albums that have 5 or less photos. I do not want the code to do that, no matter how many photos have the album, I want to show the album with a LIMIT OF 5 photos.
Other people have told me that you can not do it, I believe that this is not so, because the SQL language is very complex and I think we should have the tool to do it.
Is there any way to do this in a proper way?
*In the link that I'm shared above I put an example about the output data.
Try changing the where clause to this:
WHERE (
SELECT COUNT(*)
FROM album_photos d
WHERE d.album_id = b.album_id and
d.photo_id <= b.photo_id
AND d.nick = :nick
) <= 5
This counts the number of photos in order, not just the number of photos in the album.
Since album_photos has a mapping relationship between photos and albumns, you can specify the number of photos to join on by using TOP:
SELECT a.album_name, a.album_id, p.*
FROM albums a
INNER JOIN album_photos ap ON
ap.photo_id = (select top 5 photo_id from album_photos where a.album_id = ap.album_id order by photo_id)
INNER JOIN photos p ON ap.photo_id = p.photo_id
The Order by photo_id in the subquery will ensure the same 5 (or fewer) photos are returned
EDIT PER COMMENT. Modifying to use MySql LIMIT instead of T-SQL TOP
SELECT a.album_name, a.album_id, p.*
FROM albums a
INNER JOIN album_photos ap ON
ap.photo_id = (select photo_id from album_photos where a.album_id = ap.album_id order by photo_id LIMIT 0, 5)
INNER JOIN photos p ON ap.photo_id = p.photo_id
I have run into some troubles while writing a query for MySQL. I don't know how to describe my problem well enough to search the web for it, so sorry if my question is stupid.
I have 3 tables:
CREATE TABLE posts( id INT, author INT );
CREATE TABLE users( id INT, nick varchar(64) );
CREATE TABLE groups( id INT, name varchar(64) );
CREATE TABLE membership (user INT, group INT, date INT ) ;
Membership contains info about users that have joined some groups. "Date" in the membership table is the time when a user joined that group.
I need a query which will return a post, its author's nick and the name of the group with the least joining date.
All I have currently is:
SELECT p.id, u.nick, g.name
FROM posts AS p
LEFT JOIN users AS u ON u.id = p.author
LEFT JOIN membership AS m ON m.user = p.author
LEFT JOIN groups AS g ON g.id = m.group
WHERE 1;
but of course it returns a random group's name, not the one with earliest joining date.
I also tried the following variant:
SELECT p.id, u.nick, g.name
FROM posts AS p
LEFT JOIN users AS u ON u.id = p.author
LEFT JOIN
(SELECT * FROM membership WHERE 1 ORDER BY date ASC)
AS m ON m.user = p.author
LEFT JOIN groups AS g ON g.id = m.group
WHERE 1;
but it gave me same result.
I would appreciate even pointers to where I could start, because at the moment I have no idea what to do with it.
I don't know why you want what you do, however, if you want the information for the earliest membership date (since there's no date for posting itself), no problem. Now, we have the earliest membership which will always point to the same one person as you are not asking for a specific group.. (or did you want the earliest person PER membership group -- which is what I'll write the query for). Now, we have the earliest user and can link to the posts table (by apparently the author), but what if someone has 20 posts under their name... Do you also want the FIRST ID for that author.
Just copying from your supplied tables as a reference...
posts: id (int), author(int)
users: id (int), nick (varchar)
groups: id (int), name (varchar)
membership: user (int), group (int), date (int)
select
u1.nick,
m2.date,
g1.name,
p1.id as PostID
from
( select m.group,
min( m.date ) as EarliestMembershipSignup
from
Membership m
group by
m.group ) EarliestPerGroup
join Membership m2
on EarliestPerGroup.Group = m2.Group
AND EarliestPerGroup.EarliestMembershipSignup = m2.Date
join groups g1
on m2.group = g1.id
join users u1
on m2.user = u1.ID
join posts p1
on u1.id = p1.author
Something like this
SELECT p.id, u.nick, g.name
FROM posts p,
users u,
membership m,
groups g
WHERE p.author = u.id
AND m.user = u.id
AND m.group = g.id
ORDER BY m.date ASC
LIMIT 1;
Take care to have good indexes when joining these 4 tables.
I'd recommend moving your date column from the membership table into your groups table since that seems to be where you're tracking that information. The membership table is just an intersection table for the many-to-many users<->groups tables. It should only contain user ID and the group ID columns.
What about this?
SELECT p.id, u.nick, g.name
FROM
users u,
posts p,
groups g
INNER JOIN membership m
ON u.id = m.user
INNER JOIN groups
ON m.group = groups.id
ORDER BY g.timestamp DESC
LIMIT 1;
I am using a system I didn't create. The system has 3 main tables:
users, courses, and usergroups. I am using an extra table called coursehits.
It's a MySQL DB, 5.0. There aren't any relationships in the DB, so users are assigned to courses by simply adding an entry to usergroups (course_id and user_id) from the courses and users table. Likewise if they start a course an entry is made to coursehits.
I am trying to count the number of users in coursehits and usergroups for reporting data. So far I have the following which doesn't count correctly in one query but this doesn't count correctly, the results are much too high:
SELECT DISTINCT
c.course_name,
COUNT(ug.user_id) AS "Enrolled",
COUNT(ch.user_id) as "Started"
FROM courses c, usergroups ug, coursehits ch, users u
WHERE ch.user_id = u.id
AND ug.user_id = u.id
AND ug.course_id = c.id
AND ch.page_id != 4
GROUP BY 1
Before I was doing the following individually which does work:
SELECT DISTINCT c.course_name, COUNT(ug.user_id) AS "Enrolled"
FROM courses c, usergroups ug
WHERE ug.course_id = c.id
GROUP BY 1
Where as now I am trying to report the status of users for each course on one query, I hope that makes sense?!?
Try:
SELECT c.course_name,
COUNT(DISTINCT ug.user_id) AS "Enrolled",
COUNT(DISTINCT ch.user_id) as "Started"
FROM courses c
left join usergroups ug on ug.course_id = c.id
left join coursehits ch on ch.course_id = c.id and ch.page_id != 4
GROUP BY c.course_name
I have three tables (user, friends, posts) and two users (user1 and user2).
When user1 adds user2 as friend then user1 can see the posts of user2 just like on Facebook. But only the posts after the date when user1 added user2 as friend. My query is like this:
$sql = mysql_query("SELECT * FROM posts p JOIN friends f ON
p.currentuserid = f.friendid AND p.time >= f.friend_since OR
p.currentuserid='user1id' WHERE f.myid='user1id'
ORDER BY p.postid DESC LIMIT 20");
it is working all the way fine but with a little problem.....!!
it displays user2, user3 (all the users as friends of user1) posts for single time but shows user1 posts multiple.......i.e
user2. hi
user1. userssfsfsfsfsdf
user1. userssfsfsfsfsdf
user3. dddddddd
user1. sdfsdsdfsdsfsf
user1. sdfsdsdfsdsfsf
but i in database it is single entry/post why it is happening........!!
How can I fix it?
I'm not a SQL expert, but I think your problem is in the JOIN condition. I cannot see a way how you can join with posts and friends and get the result that you need. A SQL expert may know this, but for me it's just too difficult.
If I were you I would break the problem down in 2 parts:
Select the user's own posts
Select the user's friend's posts
For example, you can do this by using 2 different conditions and do the join with the friends table in a sub query (I have not tested this!):
select *
from posts p
where
p.currentuserid = 'user1id'
or
p.postid in
(
select p2.postid
from posts p2
join friend f on p2.currentuserid = f.friendid
where p2.time >= f.friend_since and f.myid='user1id'
)
Another way to do it is to use a union (also not tested..):
select *
from posts p
where
p.currentuserid = 'user1id'
union
select p2.*
from posts p2
join friend f on p2.currentuserid = f.friendid
where p2.time >= f.friend_since and f.myid='user1id'
I think, the easiest solution is to use GROUP BY statement on column posts.userId to remove duplicate entries. However it is not optimized way to solve the problem.
The reason you're getting the posts of all of user1's friends is you're not qualifying which friend's posts the query should return.
Add a f.friendid = 'user2id' (or whatever the column name is) in there before the WHERE clause.
You really should give some idea of what the schema looks like so we don't have to make so many assumptions. I'm assuming the primary key of user is id, and friends has a userid as well as a friendid field. I'm also assuming posts.currentuserid is the id of the user who created the post. If not, replace it with posts.userid or whatever the correct field is.
The reason your query doesn't work right is that you need at least 2 joins. When creating a query, it's easiest to start with what you have and work up to what you want, one join at a time. Here's the query to get the posts that a particular user can read:
SELECT p.*
FROM user u
JOIN friends f ON u.id = f.userid
JOIN posts p ON ((u.id = p.currentuserid) OR (f.friendid = p.currentuserid AND p.time >= f.friend_since))
WHERE u.id = ?
ORDER BY p.postid DESC LIMIT 20
The second join is where the meat is. It specifies that in order to read a post it (a) has to be written by you or (b) has to be written by a friend of yours AFTER you friended them.
If you want to also get the name of the user who created the post (assuming user.name holds the user name) you need a 3rd join:
SELECT pu.name as 'Posted By', p.*
FROM user u
JOIN friends f ON u.id = f.userid
JOIN posts p ON ((u.id = p.currentuserid) OR (f.friendid = p.currentuserid AND p.time >= f.friend_since))
JOIN user pu ON p.currentuserid = pu.id
WHERE u.id = ?
ORDER BY p.postid DESC LIMIT 20