PHP MySQL Top 5 Referers Function - php

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.

Related

Getting ordered rows partly

Hello developers out there,
I want to create a ranking list in my program, and for that want to get the users of the mysql database ordered by their points. I'm doing this with:
select `name`,`points` from users order by `points` desc
For better performance i don't want to get all users but just 10+ and 10- from a given user, because i don't want to show the whole ranking list in the program anyways. Could somebody help me with this?
One method uses subqueries and union all:
(select name, points
from users
where points <= (select u2.points from users u2 where u2.name = ?)
order by points desc
limit 11
) union all
(select name, points
from users
where points > (select u2.points from users u2 where u2.name = ?)
order by points asc
limit 10
) ;
This returns 21 rows, with the specified user included.

Mysql display random 4 users with more than 5 articles

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

MySQL Ordering from another table

I have been looking for a solution for this for over an hour now and decided to resort to asking here.
I am creating a "Twitter-like", following system for users of my Website and I wanted to be able to display each and every one of the users that a specific user follows or is followed by, I also want to then order this by the timestamp on the follow table, descending so that the latest follower is at the top.
The solutions I have come across seem to use inner joins etc. which is all well and good, but I was wondering whether there is a logical solution for my current query to do this.
Table structures:
users:
id | username
follows:
id | follower_id | following_id | timestamp
My current query:
SELECT * FROM users WHERE id IN (SELECT follower_id FROM follows WHERE following_id = $user_id) ORDER BY id ASC
Of course this will simply order by the user ID, how would I (using the current query structure), be able to add the order to list by the follows timestamp?
MySQL INNER JOIN
"SELECT users.* FROM users
INNER JOIN follows ON follows.follower_id = users.id
WHERE follows.following_id = $user_id
ORDER BY follows.timestamp DESC";
You can sort using multiple columns like this:
ORDER BY [column1] [ASC|DESC], [columm2] [ASC|DESC], ...
Therefore, edit your query's order by clause to include the second column and sort it descending.
You must use a join to add the column; here's the basic syntax of a join:
SELECT [table_name].[column_name], ...
FROM [table1]
JOIN [table2] ON [join condition]
...
Your code should look somewhat like this:
SELECT users.*
FROM users
JOIN follows ON users.id = follows.following_id
WHERE follows.following_id = $user_id
ORDER BY users.id ASC, follows.timestamp DESC
As far as I know, there is no way to do this without joining the tables; perhaps its possible to sort the returned list, but no guarantees):
SELECT * FROM users
WHERE id IN (SELECT follower_id FROM follows
WHERE following_id = $user_id
ORDER BY timestamp DESC)
ORDER BY id ASC;
The above may or may not work (I didn't test it); if it does not, you must use a join query.

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.

Ordering a mysql query?

I have 2 tables, Pages and LinkPages.
Within Pages i have the fields:
pageID (the identifier of the page),
startmemberID (the id of the member that created the page),
startDate (date the page was created).
In LinkPages I have:
pageID (to link with the page),
linkmemberID (member linking with the page),
joinDate (date member linked with the page).
What sql query would i use to get all information on the pages with a particular id and then order it by the date the page was started.
I got this far:
SELECT * FROM LinkPages WHERE linkmemberID='MEMBERID' LIMIT 5
but obviously i haven't ordered them here, would i need to use a join?
Many Thanks,
Jai
Try this:
SELECT * FROM LinkPages
INNER JOIN Pages ON Pages.pageID = LinkPages.pageID
WHERE linkmemberID='MEMBERID'
ORDER BY startDate DESC
LIMIT 5
SELECT lp.pageID, lp.linkmemberID, lp.joinDate
FROM LinkPages lp, Pages p
WHERE lp.linkmemberID='MEMBERID' AND lp.pageID = p.pageID
ORDER BY p.startDate DESC
LIMIT 5
You have two options, either you can JOIN or you can use a subquery:
SELECT * FROM LinkPages WHERE linkmemberID='MEMBERID'
ORDER BY
(SELECT startDate FROM Pages WHERE Pages.pageID = LinkPages.PageID) DESC
LIMIT 5
For good measure, here's the join:
-- be sure to use L.* here, otherwise you get all of the columns from
-- pages as well
SELECT L.* FROM LinkPages L
INNER JOIN Pages P ON P.pageID = L.pageID
WHERE linkmemberID='MEMBERID'
ORDER BY P.startDate DESC LIMIT 5
SELECT lp.*
FROM LinkPages lp, Pages p
WHERE lp.pageId = p.pageId
AND lp.linkmemberID='MEMBERID'
ORDER BY p.startDate
LIMIT 5
sorry - forgot the ORDER BY ...

Categories