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.
Related
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
I have a table named items which has 3 columns : id, user_id, item_name.
I want to select and show all users that have most submitted items in that table.
For instance :
User-1 has 3 items,
User-2 has 8 items,
User-3 has 5 items,
User-4 has 8 items, and
User-5 has 8 items too.
Based on what I need, the query should be outputting User-2, User-4 and User-5.
My knowledge of MySQL is not thorough unfortunately and I can't get this done by myself.
Your help is much appreciated.
EDIT #1 :
Here's the query that I tried and didn't output my desired result :
SELECT COUNT(id) AS total_count
, user_id
FROM ".DB_PREFIX."items
GROUP
BY user_id
It shows all users and their total number of items submitted. As I mentioned earlier, I need all top users.
E.g.:
SELECT a.*
FROM
( SELECT user_id
, COUNT(*) total
FROM my_table
GROUP
BY user_id
) a
JOIN
( SELECT COUNT(*) total
FROM my_table
GROUP
BY user_id
ORDER
BY total DESC
LIMIT 1
) b
ON b.total = a.total;
hoping someone can help. MySQL JOIN statements have never been my strong point and they just hurt my brain trying to figure them out.
I have table which stores ratings of an item in another table, tracked by ID.
What I need to do now though is display a list of items ordered by the ratings, high to low and low to high.
I can get a list of ratings per item, grouped by ID from the ratings table easily enough, but it's getting it JOINed to the items I get stuck on. Not all items have ratings either (yet), and so it would also be beneficial if the combined list didn't just stop at the end of the ratings that do exist.
OK, so here's my grouping statement:
SELECT `themeID` , SUM( `rating` ) AS ratings
FROM `votes`
GROUP BY `themeID`
ORDER BY `ratings` DESC
outputs
themeID ratings
1 6
3 3
2 2
6 2
Then the details table consists of various info, such as id, filename, name, date etc
Between the two tables, themeID and id are the same which links them. I've looked at some of the other answers to similar queries on SO, but I couldn't get any of the answers to work with my tables/queries (probably because I don't fully grasp JOIN's)
ANy help would be saving me a massive headache!
Just join the two tables and add the aggregation function.
SELECT d.id, d.filename, d.name, IFNULL(SUM(v.rating), 0) AS ratings
FROM details AS d
LEFT JOIN votes AS v ON d.id = v.themeID
GROUP BY d.id
I used LEFT JOIN so this will show the details even if there are no votes.
I have tested in sql server same u can get in Mysql
DROP TABLE #Item
create table #Item (ID int identity(1,1),ItemNAme varchar(10))
INSERT INTO #Item(ItemNAme)
SELECT 'A'
UNION ALL
SELECT 'B'
UNION ALL
SELECT 'C'
DROP TABLE #ItemRating
create table #ItemRating (ItemID int ,rating int)
INSERT INTO #ItemRating(ItemID,rating)
SELECT 3,2
UNION ALL
SELECT 2,11
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 2, 4
union all
SELECT 1,5
UNION ALL
SELECT 3,12
UNION ALL
SELECT 1, 4
UNION ALL
SELECT 2, 1
SELECT m.ItemNAme,SUM(I.rating) as Rating
FROM #Item m INNER JOIN #ItemRating I ON m.ID=I.ItemID
group by m.ItemNAme
ORDER BY SUM(I.rating) asc
--OR same thing can achive
SELECT ItemNAme, Rating
FROM (
SELECT m.ItemNAme,SUM(I.rating) as Rating
FROM #Item m INNER JOIN #ItemRating I ON m.ID=I.ItemID
group by m.ItemNAme
)P
ORDER BY Rating Desc
SELECT * FROM conversation_1
LEFT JOIN conversation_2
ON conversation_1.c_id = conversation_2.c_id
LEFT JOIN user
ON conversation_2.user_id = user.user_id
LEFT JOIN message
ON conversation_1.c_id = message.c_id
WHERE conversation_1.user_id=1
GROUP BY message.c_id
conversation_1 conversation_2
c_id user_id c_id user_id
1 1 1 2
2 1 2 3
3 2
I have a message DB build in Mysql
I make 4 tables user, conversation_1, conversation_2, message
when user try to open his message box, it will fetch out all conversations(conversation_1)
than join to user conversation_2 and use conversation_2 to find out which user
than join to the message.
c_id user_id user_name message
1 2 Alex Hi user_1, this is user_2
2 3 John hi user_3, user_2 don't talk to me
it works fine, however I want to display the message from last row GROUP BY
currently it display the 1st row in this group.
ps.conversation_1.c_id is auto increment and the c_id will insert to conversation_2 who has join this conversation
select * from (SELECT * FROM conversation_1
LEFT JOIN conversation_2
ON conversation_1.c_id = conversation_2.c_id
LEFT JOIN user
ON conversation_2.user_id = user.user_id
LEFT JOIN message
ON conversation_1.c_id = message.c_id
WHERE conversation_1.user_id=1
order by conversation_1.c_id desc) finalData
GROUP BY message.c_id
Beware that, as documented under MySQL Extensions to GROUP BY:
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. This means that the preceding query is legal in MySQL. You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values within each group the server chooses.
This is what is happening to select the message (and potentially other columns) in your existing query.
Instead, you want the groupwise maximum:
SELECT messages.* FROM messages NATURAL JOIN (
SELECT c_id, MAX(m_id) m_id FROM messages GROUP BY c_id
) t
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.