MySQL, getting all top users - php

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;

Related

MySQL JOIN ratings by ID to details by ID

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

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.

mysql searching row's based on AND

Hi guy's I've been trying to figure this out for hours, but I can't seem to wrap my head around it..
Take a look at this sql table..
I'm trying to search for a product_id that exists in all filter_cat_id's, so in this example it would be product_id 30, because it exists with filter_cat_id 1 2 and 3.. Product_id 30 exists in the same filter_cat_id twice, otherwise I'd just count the product id's, then the distinct cat id's and make sure the count's match.
Any ideas? Is this possible in mysql? another option would be to use PHP.
Thanks!
I'm trying to search for a product_id that exists in all filter_cat_id's, so in this example it would be product_id 30, because it exists with filter_cat_id 1 2 and 3.
In general, this can be done by grouping by product_id and using HAVING COUNT(product_id) >= x – where x is the number of categories,
SELECT product_id FROM table GROUP BY product_id HAVING COUNT(product_id) >= 3
If you don’t know the number of categories upfront (so that you can insert the actual number into the query), you can get that value with a subquery,
SELECT product_id FROM table GROUP BY product_id
HAVING COUNT(product_id) >= (SELECT COUNT(DISTINCT category_id) FROM table)
Edit: OK, so apparently the category_ids to search for come from some kind of search form/filter mechanism.
In that case, the filtering of items has to be added – and the number to compare to must be embedded implicitly, but that should be no problem.
SELECT product_id FROM table
WHERE category_id IN(1,2,3)
GROUP BY product_id
HAVING COUNT(product_id) >= 3
SELECT
product_id, count(i.product_id) as t
FROM
(SELECT
*
FROM
tablename
GROUP BY product_id , filter_cat_id) as i
GROUP BY product_id
HAVING t = 3
This should be smart enough to determine the number of distinct categories, and select a product_id that has that number of distinct categories.
SELECT product_id
FROM
(SELECT DISTINCT product_id, filter_cat_id
FROM test.play) a
GROUP BY product_id
HAVING count(product_id) = (SELECT count(DISTINCT filter_cat_id) FROM test.play)
;
Test Data:
Query Result:
30
40

SQL Query for TOP 10 depending on two columns

I am new to SQL (beginner programmer), so I apologize if this might seem like a simple question. I am trying to create a table on my website that displays the lowest 10 grades along with some information about the student. I am pretty comfortable from this aspect, but I am having trouble coding the write SQL query. I am using SQL Server 2008.
I have a table in my database with 10 columns and 500 rows. Two of these columns contain grades (grade1 and grade2). My goal is to display in my website table the TOP 10 lowest GRADE1, but if GRADE1 is NULL I want it to take GRADE2 into consideration and display that instead. So in context, if a student named Billy has no GRADE1 (its NULL) but his GRADE2 is the lowest of all (GRADE1's AND GRADE2's combined), he should be first in the list.
I would really appreciate help making a query capable of accomplishing this task, I have been researching for a solution but it has only confused me more.
You can use case in the order by clause
select top 10 *
from students
order by case when grade1 is null then grade2 else grade1 end desc
EDIT
Following BillyCode comment on including only those students that apears 3 or more times in the table I suggest this
select top 10 s.*
from students s
inner join (select StudentId, Count(*) as total from students) c on s.StudentId = c.StudentId
where c.total >= 3
order by case when grade1 is null then grade2 else grade1 end desc
But I'm not sure if you can join to a subquery.
In SQL Server you want to use isNull()
SELECT TOP 10 isNull(grade1,grade2) AS `Grade`
FROM mytable
ORDER BY Grade DESC
The COALESCE function does what you want. COALESCE(GRADE1, GRADE2) will display GRADE1 if not null, or GRADE2 if GRADE1 is null.
So instead of ORDER BY GRADE1, you can do ORDER BY COALESCE(GRADE1, GRADE2)
See here for more details about COALESCE.
Try this:
select Top 10
student , (case when grade1 is null then grade2 else grade1 end ) as g1 , grade2 as g2 from table order by g1 desc
Another method is to first find the 10 students with the lowest grade1. Rename grade1 into grade. And then find the 10 students with the lowest grade2. Rename grade2 into grade. After that union these two results. From there, find the 10 students with with lowest grade. The SQL is something like:
SELECT id, grade
FROM (SELECT id, grade1 AS grade
FROM students
ORDER BY grade1 DESC
LIMIT 10
UNION
SELECT id, grade2 AS grade
FROM students
ORDER BY grade2 DESC
LIMIT 10)
ORDER BY grade DESC
LIMIT 10

Get multiple GROUP BY results per group, or use separate concatenated table

I am working on an auction web application. Now i have a table with bids, and from this table i want to select the last 10 bids per auction.
Now I know I can get the last bid by using something like:
SELECT bids.id FROM bids WHERE * GROUP BY bids.id ORDER BY bids.created
Now I have read that setting an amount for the GROUP BY results is not an easy thing to do, actually I have found no easy solution, if there is i would like to hear that.
But i have come up with some solutions to tackle this problem, but I am not sure if i am doing this well.
Alternative
The first thing is creating a new table, calling this bids_history. In this table i store a string of the last items.
example:
bids_history
================================================================
auction_id bid_id bidders times
1 20,25,40 user1,user2,user1 time1,time2,time3
I have to store the names and the times too, because I have found no easy way of taking the string used in bid_id(20,25,40), and just using this in a join.
This way i can just just join on auction id, and i have the latest result.
Now when there is placed a new bid, these are the steps:
insert bid into bids get the lastinserteid
get the bids_history string for this
auction product
explode the string
insert new values
check if there are more than 3
implode the array, and insert the string again
This all seems to me not a very well solution.
I really don't know which way to go. Please keep in mind this is a website with a lot of bidding's, they can g up to 15.000 bidding's per auction item. Maybe because of this amount is GROUPING and ORDERING not a good way to go. Please correct me if I am wrong.
After the auction is over i do clean up the bids table, removing all the bids, and store them in a separate table.
Can someone please help me tackle this problem!
And if you have been, thanks for reading..
EDIT
The tables i use are:
bids
======================
id (prim_key)
aid (auction id)
uid (user id)
cbid (current bid)
created (time created)
======================
auction_products
====================
id (prim_key)
pid (product id)
closetime (time the auction closses)
What i want as the result of the query:
result
===============================================
auction_products.id bids.uid bids.created
2 6 time1
2 8 time2
2 10 time3
5 3 time1
5 4 time2
5 9 time3
7 3 time1
7 2 time2
7 1 time3
So that is per auction the latest bids, to choose by number, 3 or 10
Using user variable, and control flow, i end up with that (just replace the <=3 with <=10 if you want the ten auctions) :
SELECT a.*
FROM
(SELECT aid, uid, created FROM bids ORDER BY aid, created DESC) a,
(SELECT #prev:=-1, #count:=1) b
WHERE
CASE WHEN #prev<>a.aid THEN
CASE WHEN #prev:=a.aid THEN
#count:=1
END
ELSE
#count:=#count+1
END <= 3
Why do this in one query?
$sql = "SELECT id FROM auctions ORDER BY created DESC LIMIT 10";
$auctions = array();
while($row = mysql_fetch_assoc(mysql_query($sql)))
$auctions[] = $row['id'];
$auctions = implode(', ', $auctions);
$sql = "SELECT id FROM bids WHERE auction_id IN ($auctions) ORDER BY created LIMIT 10";
// ...
You should obviously handle the case where, e.g. $auctions is empty, but I think this should work.
EDIT: This is wrong :-)
You will need to use a subquery:
SELECT bids1.id
FROM ( SELECT *
FROM bids AS bids1 LEFT JOIN
bids AS bids2 ON bids1.created < bids2.created
AND bids1.AuctionId = bids2.AuctionId
WHERE bid2.id IS NULL)
ORDER BY bids.created DESC
LIMIT 10
So the subquery performs a left join from bids to itself, pairing each record with all records that have the same auctionId and and a created date that is after its own created date. For the most recent record, there will be no other record with a greater created date, and so that record would not be included in the join, but since we use a Left join, it will be included, with all the bids2 fields being null, hence the WHERE bid2.id IS NULL statement.
So the sub query has one row per auction, contianing the data from the most recent bid. Then simply select off the top ten using orderby and limit.
If your database engine doesn't support subqueries, you can use a view just as well.
Ok, this one should work:
SELECT bids1.id
FROM bids AS bids1 LEFT JOIN
bids AS bids2 ON bids1.created < bids2.created
AND bids1.AuctionId = bids2.AuctionId
GROUP BY bids1.auctionId, bids1.created
HAVING COUNT(bids2.created) < 9
So, like before, left join bids with itself so we can compare each bid with all the others. Then, group it first by auction (we want the last ten bids per auction) and then by created. Because the left join pairs each bid with all previous bids, we can then count the number of bids2.created per group, which will give us the number of bids occurring before that bid. If this count is < 9 (because the first will have count == 0, it is zero indexed) it is one of the ten most recent bids, and we want to select it.
To select last 10 bids for a given auction, just create a normalized bids table (1 record per bid) and issue this query:
SELECT bids.id
FROM bids
WHERE auction = ?
ORDER BY
bids.created DESC
LIMIT 10
To select last 10 bids per multiple auctions, use this:
SELECT bo.*
FROM (
SELECT a.id,
COALESCE(
(
SELECT bi.created
FROM bids bi
WHERE bi.auction = a.id
ORDER BY
bi.auction DESC, bi.created DESC, bi.id DESC
LIMIT 1 OFFSET 9
), '01.01.1900'
) AS mcreated
COALESCE(
(
SELECT bi.id
FROM bids bi
WHERE bi.auction = a.id
ORDER BY
bi.auction DESC, bi.created DESC, bi.id DESC
LIMIT 1 OFFSET 9
), 0)
AS mid
FROM auctions a
) q
JOIN bids bo
ON bo.auction >= q.auction
AND bo.auction <= q.auction
AND (bo.created, bo.id) >= (q.mcreated, q.mid)
Create a composite index on bids (auction, created, id) for this to work fast.

Categories