简体   繁体   中英

Limit number of MySQL results per user

I have a site where users can list items for sale. The front of the site displays random items on sale using the following SQL.

SELECT * FROM auctions 
WHERE closed = 0 AND suspended = 0 AND starts <= 1390990443
ORDER BY RAND() LIMIT 30

The problem is that one users has a lot of sales to the extent that the front of the site is mostly that one user. I would like to limit the items displayed by the user using the auction.user column to five items per user.

I am hoping that there is SQL to do this in one line, if not then advice or a link on who to build up results on multiple queries would be appreciated. Coded language is PHP.

Edit: database structure is here. http://pastebin.com/3ua18k4h

Personally I would try and form some solution pulling results into PHP and filtering them there, but that's because I feel more comfortable in PHP than MySQL. However you could do something like this without the PHP filtering and just via your MySQL query, no idea how it would do scaling up to lots of users though (not sure on your full table schema, so I improvised)

SELECT ID, USERNAME, AUCTION_ID
FROM
(
  SELECT *, @row:=IF(username=@username,@row,0)+1 AS auctioncount, @username:=username FROM 
  (SELECT *,RAND() AS trand FROM table1) t1,
  (SELECT @row:=0,@username:='') tm2 
  ORDER BY username,trand
) t2
WHERE auctioncount<=5 LIMIT 30

SQL fiddle: http://sqlfiddle.com/#!2/9bd47/1

You can change auctioncount to the maximum number of listings you want per user.

You can get more results with MYSQL with a bigger LIMIT and then create an array for every items for sale by user and finally take only 5 items per user, so you will only use 1 request to Mysql and more CPU usage, could be a good idea if you have a lot of traffic on your web site.

1) get more results :

SELECT * FROM auctions 
WHERE closed = 0 AND suspended = 0 AND starts <= 1390990443
ORDER BY RAND() LIMIT 1000

2) Loop and store per items per user :

foreach($results as $item) {
   array_push($itemsPerUser[$item['userId']], $item);
}

3) Filter only 5 items per user:

foreach($itemsPerUser as $user => $items) {
   $fiveItemsPerUser = array_slice($items, 0, 5);
}

PS: this is pseudo-code, you should add more check on array length etc ...

Not quite a direct answer to your problem, but perhaps following is enough:

Select 30 random users with running auctions, and display the oldest auction of each of them:

SELECT * FROM auctions WHERE id IN (
    SELECT MIN(id) FROM auctions
    WHERE closed = 0 AND suspended = 0 AND starts <= 1390990443
    GROUP BY userid ORDER BY RAND() LIMIT 30
)

At least you can remove the newlines and have it all in a single line sql query, which can't be done with the exact query you requested.

Here's something to think about, although I appreciate that the intellectual leap from this to a (fair and) working solution might be a step too far!...

SELECT RAND(@i:=RAND()*1000);
+-----------------------+
| RAND(@i:=RAND()*1000) |
+-----------------------+
|    0.7903550134881911 |
+-----------------------+

SELECT RAND(@i);
+--------------------+
| RAND(@i)           |
+--------------------+
| 0.7903550134881911 |
+--------------------+

SELECT RAND(@i:=RAND()*1000);
+-----------------------+
| RAND(@i:=RAND()*1000) |
+-----------------------+
|    0.9555754568014065 |
+-----------------------+

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM