Mysql display random 4 users with more than 5 articles - php

I have users table and also articles tables. Article table contains articles submitted by users. I am working on a sql query to display random 4 users with more than 5 articles. user_id is stored in articles table. I have searched around in stackoverflow and google even though there are some similar questions, i couldn't find anything specific to mine.
Can anyone let me know if this question has been answered before and give me a link if yes otherwise I have the following query:
SELECT *
FROM users WHERE type = 3
INNER JOIN articles ON
users.user_id = articles.user_id HAVING COUNT(user_id) > 5
This doesn't seem to work. I will appreciate any help to improve this query.
Database table is as follows:
USERS:
user_id
username
email
type
ARTICLES:
id
user_id
title
For example, total user count is 100. User with user_id 49 has 10 articles, and another user with user_id 50 has 20 articles and the rest of the users have less than 5 articles. So the query should return only the user 49 and 50.
Hope this makes sense.
regards

I've mocked up some table data to test my query. WHERE clauses must be positioned after JOINs. You are also a little ambiguous about the comparison of COUNT AND 5 -- if you want more than 5 then >5, if you want 5 or more then >=5.
SQL: (SQLFiddle Demo)
SELECT a.user_id,a.username,COUNT(b.user_id)
FROM users a
INNER JOIN articles b ON a.user_id=b.user_id
WHERE a.type=3
GROUP BY a.user_id
HAVING COUNT(b.user_id)>5
ORDER BY RAND()
LIMIT 4

You have where join and having in wrong position, you missed group by for a correct functioning of having
and you need an order by rand and limit 4
SELECT u.user_id
FROM users u
INNER JOIN articles a ON u.user_id = a.user_id
WHERE a.type = 3
group by u.user_id
HAVING COUNT(a.user_id)>= 5
order by rand() limit 4

Related

Select all from a table ORDER BY another tables column, with match primary key [duplicate]

This question already has answers here:
PHP/MySQL Order by column in a different table
(2 answers)
Closed 5 years ago.
I've been looking for a while but the query I am trying to accomplish seems fairly hard to find any information or documentation on how to do what I am trying to do.
I have two tables, one of them stores my user accounts and basic information. I then have a second table that holds a little more information about the user.
Both of these tables have primary keys (table one is id and table two is user_id) which I use to know who is who and match records between both tables.
What I am trying to do today is I want to get 10 records from table one, order by a column in table two (room_count) DESC.
Table #1's name is "users" and Table #2's name is "user_information".
What have I tried?
I'm not really sure where to start so I haven't tried anything yet.
How would I got about doing something like this?
Thank you to any answers posted.
For example, let's say I have 4 users, I'll write the username followed by the room_count column in the other table below.
Adam Sandler : 4
Jenny Hang : 9
Peter Foreign : 0
If I was to use the query with ASC it would start with Peter Foreign and end with Jenny Hang
Don't you just need a simple join?
SELECT
FROM users
INNER JOIN user_information ON users.id = user_information.user_id
ORDER BY user_information.room_count DESC
LIMIT 2
Please try this:
SELECT *
FROM users u
INNER JOIN user_info ui
ON u.id = ui.user_id
ORDER BY ui.room_count DESC
LIMIT 10
Try something basic like
select users.*
from
users, user_information
where
users.id =user_information.user_id
order by
user_information.room_count
desc
Limit 10
Edit: changed select users.id to select users.* to better fit the question asked.

MySQL, get last active users in batches but prevent duplicate users in the next batch

I'm trying to extract users contributing to a specific topic in a message board.
Each request gets a batch of 10 Unique users.
The problem is that if some users where part of a previous batch they can occur in the next batch too.
SELECT p.post_id as id, p.author as uid, a.name
FROM posts p
INNER JOIN users a
ON a.id = p.author
AND p.topic_id = __TOPIC_ID__
AND p.post_id < __OFFSET_POST_ID__
GROUP BY p.author
ORDER BY MAX(p.post_id)
DESC LIMIT 10
My question is how I'm able to prevent those possible duplicates or at least get the lowest post_id.
Let's assume a single topic with 100 contributing users and 50000 posts written by them where only one of the first posts was made by the third user.
With a LIMIT of 10 it would be possible to get all 100 users in 10 queries. But this is not the way the above queries works:
If post 10000 up to 50000 were made by only ten users my ajax queries would get these users multiple times for many many requests. AND even worse...:
I could throw away all those requests because they would only contain duplicates every time.
What would be the "best" option to reduce the amount of queries?
One possible solution would be to query the n, 10 users but get the lowest post_id matching not as here the max() id. This way I could reduce the requests a bit in some cases but only in some cases.
Another way would be to use a:
AND p.author NOT IN( list of all uids queried before )
But This would make the problem even worse I guess...^^ Like:
SELECT * FROM X WHERE author_id NOT IN(1..to..4000000)...
You're iterating over posts, not users, while you need to iterate over users. I think this might do the trick:
SELECT u.id, u.name, max(p.post_id)
FROM users u
INNER JOIN posts p ON p.author = u.id
WHERE p.topic_id = :topic_id
GROUP BY u.id
ORDER BY max(p.post_id) DESC
LIMIT 10 OFFSET :offset;
As you can see, I group over users.id (primary key), and not posts.author, which is not primary/unique key, but just foreign key to users. You get duplicates exactly because you group on posts.author

Selecting N rows from each group in MYSQL

I need to search for the updates sent by the friends of a giving user.
There is a table called friendship. It has a column called profile1 and another one called profile2. It represents the friendship between two users in this websystem, and a friendship is the presence of two giving ids, no matter in what position. So the profile with id 1 may have 2 friends, profile with id 2 and with id 3 as following:
friendship
profile1 profile2
1 2 <--
3 1 <--
2 5
...
Now I want to search for the updates sent by some user's friends. There is this table update
update
id content time profile
1 A text ... 2
2 A text ... 2
3 A text ... 3
4 A text ... 2
5 A text ... 3
6 A text ... 2
7 A text ... 10
8 A text ... 11
If my profile/user is identified by the id 1, and it has only 2 friends (the profiles identified by id 2 and 3) and also I need my search to return only 2 results by each user, my SELECT has to return updates 1,2,3 and 5.
Preferably updates should be grouped by its author and it would be great if I could set the number of different profiles to be considered in this search (for example, if profile 1 had 10 friends and I wanted only updates from 3 profiles, the most recent must appear first).
Do you know how can I achieve this??
thank you very much!
#EDIT
This returns all updates sent by friends of profile 1. But i'm not sure whether or not i'm in the right direction
SELECT u.*
FROM `update` u
INNER JOIN friendship f1 ON f1.profile1 = u.author
WHERE f1.profile2 =1
UNION
SELECT u.*
FROM `update` u
INNER JOIN friendship f2 ON f2.profile2 = u.author
WHERE f2.profile1 =1
If you are willing to do it in two queries, you can do it like this. First, get three profiles who have most recently posted based on your constraints:
-- Get the three latest updated profiles from here.
-- (we can't use a CTE because MySQL doesn't support
-- them yet).
SELECT DISTINCT p.profile FROM
(
SELECT ui.profile, ui.time FROM
(
SELECT u.profile, u.time
FROM `update` u
INNER JOIN `friendship` f ON f.profile2 = u.profile
WHERE f.profile1 = 1
UNION ALL
SELECT u.profile, u.time
FROM `update` u
INNER JOIN `friendship` f ON f.profile1 = u.profile
WHERE f.profile2 = 1
) ui ORDER BY ui.time DESC
) p LIMIT 0, 3;
From that query, get the three profile IDs out and put them in place of <id1>, <id2> and <id3> in the following query
-- Use a union to get the result set back
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id1>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id2>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id3>
ORDER BY a.time DESC
LIMIT 0, 2);
If you get less than three profiles back, either remove parts of the query in your PHP code, or set the WHERE clause to something like 0 so it always evaluates to fault (assuming you don't have a profile ID of zero)
The 2 in the limit clauses above can be changed if you want more or fewer results per profile.
Sample SQL fiddle: http://sqlfiddle.com/#!2/22e57/1 (updated fiddle to make the content more meaningful and to use times)
I would suggest doing a series of queries for each author within one transaction, that way there would not be a need for grouping - you could simply append results together outside of your SQL.
SELECT * FROM `update` WHERE
profile IN (SELECT profile2 FROM `friendship` WHERE profile1=1) OR
profile IN (SELECT profile1 FROM `friendship` WHERE profile2=1);
try this sqlFiddle
SELECT T1.profile,T1.content,T1.time
FROM
(SELECT UPD.profile,UPD.content,UPD.time,
IF (#prevProfile != UPD.profile,#timeRank:=1,#timeRank:=#timeRank+1) as timeRank,
#prevProfile := UPD.profile
FROM
(SELECT UP.profile,UP.content,UP.time
FROM
(SELECT profile,max(time) as latestUpdateTime
FROM friendship F INNER JOIN updates U
ON (F.profile1 = 1 AND U.profile = profile2) /* <-- specify profile on this line */
OR(F.profile2 = 1 AND U.profile = profile1) /* <-- specify profile on this line */
GROUP BY profile
ORDER BY latestUpdateTime DESC
LIMIT 3 /* limit to 3 friends profiles that have the most recent updates */
)as LU
INNER JOIN updates UP
ON (UP.profile = LU.profile)
ORDER BY profile,time DESC
)as UPD,(SELECT #prevProfile:=0,#timeRank:=0)variables
)T1
WHERE T1.timeRank BETWEEN 1 AND 2 /* grab 2 lastest updates for each profile */
ORDER BY T1.time DESC
in my example, profile id 1 has more than 3 friends, but i am only grabbing 3 friends that made the most recent updates.
explanation of above query.
LU grabs 3 profiles that are friends with profile id 1 that made the latest updates.
UPD grabs all contents that belong to these 3 friends.
T1 returns the contents along with a timeRank number for each content in order from 1 counting upward order by time DESCENDING for each profile
and finally the WHERE we only grab 2 content updates for each profile
then we finally ORDER these updates based on TIME starting from most recent.

PHP MySQL Top 5 Referers Function

I have a table called users which looks like:
-id
-email
-login
-admin
-coins
-cash
-premium
-IP
-pass
-ref
-signup
-online
-promote
-activate
-banned
-rec_hash
-country
-c_changes
-sex
-daily_bonus
If say user with id 81 referred 10 people then those 10 people would have "81" in their ref column.
I would like to create a top 5 referral table but I'm having trouble with the query and displaying that in PHP, would anybody be able to help?
I FORGOT TO MENTION IF THEY HAVE NO REFERRAL IT SHOWS AS 0 HOW WOULD I EXCLUDE 0 FROM BEING SHOWN AS A REFERRAL?
You can do it in a single SQL statement like this:
SELECT ref, COUNT(*) AS num FROM users
GROUP BY ref ORDER BY num DESC LIMIT 5
But that will just get you the 5 IDs, rather than their user rows. You can then perform a further query to get the actual rows. Alternatively, use the above query with a join to do it all in one.
IF THEY HAVE NO REFERRAL IT SHOWS AS 0
messy design - this should be null. Regardless...
SELECT u.login, ilv.referred
FROM
(SELECT ref, COUNT(*) AS referred
FROM users
WHERE ref IS NOT NULL
AND ref>0
GROUP BY ref
ORDER BY COUNT(*) DESC
LIMIT 0,5) ilv
INNER JOIN users u
ON ilv.ref=users.id
ORDER BY ilv.referred DESC;
Or and SQL like this:
SELECT u.*, COUNT(*) as referrers FROM users r JOIN users u ON r.ref = u.id
GROUP BY u.id ORDER BY referrers DESC LIMIT 5
It is faster to use just one statement even with a join on the same table.

In PHP + MySQL, How do I join many tables with conditions

I'm trying to get the users full activity throughout the website.
I need to Join many tables throughout the database, with that condition that it is one user.
What I currently have written is:
SELECT * FROM
comments AS c
JOIN rphotos AS r
ON c.userID = r.userID
AND c.userID = '$defineUserID';
But What it is returning is everything about the user, but it repeats rows.
For instance, for one user he has 6 photos and 5 comments
So I expect the join to return 11 rows.
Instead it returns 30 results like so:
PhotoID = 1;
CommentID = 1;
PhotoID = 1;
CommentID = 2;
PhotoID = 1;
CommentID = 3;
and so on...
What am i doing wrong?
What I'm trying to achieve (example)
If you're a facebook user, every profile has a 'wall' which states the user's activity on the website in chronological order. I'm trying to make something similar.
What am i doing wrong?
you are using one complex query when you could use two simple ones.
You should do it as follows:
SELECT * FROM user AS u
LEFT JOIN rphotos AS r ON u.userId = r.userID
LEFT JOIN comments AS c ON u.userId = c.userID
WHERE u.userId = '$defineUserID'
Updated to fix silly mistakes
What this does is select all relevant users from the user table (1 in this case) then join in the other tables where necessary and shouldnt repeat rows.
The query also makes more sense when you think about it logically.
That you get 30 results if a specific user has 6 photos and 5 comments is quite normal, since you're just fetching the cartesian product of all photos and comments based on user ID. The table structure could shed some light onto possible solutions, but if the comments are related to the photos and you want to fetch all photos and the comments a specific user posted you might use something like :
SELECT * FROM rphotos p
LEFT JOIN comments c on c.photoID = p.photoID
WHERE p.userID = '$defineUserID' OR c.userID = '$defineUserID';
Personally I would split this into 2 queries and display the results separately because mixing them doesn't make any sense to me, ie. use
SELECT * FROM rphotos p
WHERE p.userID = '$defineUserID';
and
SELECT * FROM comments c
WHERE c.userID = '$defineUserID';
edit based on comment
If the ID fields are of the same type you could use something like
select actionID, relatedID, creationDate from
(
select 1 as actionID, photoID as relatedID, creationDate from rphotos
where userID = '$defineUserID'
union
select 2 as actionID, commentID as relatedID, creationDate from comments
where userID = '$defineUserID'
) actions
order by creationDate desc;
The actionID will be 1 for a photo, 2 for a comment and using the relatedID field you could lookup the linked data (if you need it, otherwise you could just drop it from the query).
BTW You probably want to filter the results further (ie. based on date) to prevent joining lots of rows in the union that you won't display...

Categories