views:

95

answers:

2

Hello i have a question on picking random entries from a database. I have 4 tables, products, bids and autobids, and users.

Products
-------  
id 20,21,22,23,24(prime_key)
price...........
etc...........

users  
-------
id(prim_key)  
name user1,user2,user3  
etc  

bids  
-------
product_id  
user_id  
created  

autobids  
--------
user_id   
product_id 

Now a multiple users can have an autobid on an product. So for the next bidder I want to select a random user from the autobid table

example of the query in language:

for each product in the autobid table I want a random user, which is not the last bidder.

On product 20 has user1,user2,user3 an autobidding.
On product 21 has user1,user2,user3 an autobidding

Then I want a resultset that looks for example like this

20 – user2
21 – user3

Just a random user. I tried miximg the GOUP BY (product_id) and making it RAND(), but I just can't get the right values from it. Now I am getting a random user, but all the values that go with it don't match.

Can someone please help me construct this query, I am using php and mysql

+1  A: 

You can use the LIMIT statement in conjunction with server-side PREPARE.

Here is an example that selects a random row from the table mysql.help_category:

select @choice:= (rand() * count(*)) from mysql.help_category;
prepare rand_msg from 'select * from mysql.help_category limit ?,1';
execute rand_msg using @choice;
deallocate prepare rand_msg;

This will need refining to prevent @choice becoming zero, but the general idea works.

Alternatively, your application can construct the count itself by running the first select, and constructing the second select with a hard-coded limit value:

select count(*) from mysql.help_category;
# application then calculates limit value and constructs the select statement:   
select * from mysql.help_category limit 5,1;
Martin
Wow, I don't understand this answer at all, I am still kind of a beginner. What is the table mysql.help_category? And is this used in a loop?
Saif Bechan
mysql.help_category is a table that most installations will have that I used as an example. Substitute your table for that. I will clarify the answer. Whether this is used in a loop depends on what your application wants to do.
Martin
"depends on what my application wants to do"? My application wants to do what is asked in the question, that is get a random item per group from the autobids table. Is there a mothod of getting the whole as a result set, i don't want to loop trough the DB.And are functions like rand() and count() not a bad idea for the database. I think getting the whole set from mysql and looping trough it with php is the same as this option performance wise.
Saif Bechan
Sorry - I misunderstood. You would indeed need to repeat the process for each product. I have added a new answer that would provide the solution for all products in the autobids table in one go.
Martin
+1  A: 

The first part of the solution is concerned with identifying the latest bid for each product: these eventually wind up in temporary table "latest_bid".

Then, we assign randon rank values to each autobid for each product - excluding the latest bid for each product. We then choose the highest rank value for each product, and then output the user_id and product_id of the autobids with those highest rank values.

create temporary table lastbids (product_id int not null, 
                                 created datetime not null, 
                                 primary key( product_id, created ) );

insert into lastbids 
select product_id, max(created)
from bids
group by product_id;

create temporary table latest_bid ( user_id int not null, 
                                    product_id int not null, 
                                    primary key( user_id, product_id) );

insert into latest_bid
select product_id, user_id 
from bids b
join lastbids lb on lb.product_id = b.product_id and lb.created = b.created;

create temporary table rank ( user_id int not null, 
                              product_id int not null, 
                              rank float not null, 
                              primary key( product_id, rank ));

# "ignore" duplicates - it should not matter
# left join on latest_bid to exclude latest_bid for each product

insert ignore into rank 
select user_id, product_id, rand() 
from autobids a
left join latest_bid lb on a.user_id = lb.user_id and a.product_id = lb.product_id 
where lb.user_id is null;

create temporary table choice 
as select product_id,max(rank) choice 
   from rank group by product_id;

select user_id, res.product_id from rank res
join choice on res.product_id = choice.product_id and res.rank = choice.choice;
Martin
Wow this looks like a very good solution to the problem. This most certainly get the result that i am looking for. I have questions about the performance tho. This query has to run every second in my system, as there could be a new auto bidding to be fired every second. Will it not be faster to get all the auto biddings into php, and then just with a loop filter out the unwanted. Creating a temporary table seems to be a bit slow to me, and i think functions as rand() and max() are too. I can be mistaken. I do have to say i am using inooDB tables.
Saif Bechan
The database approach could be improved by tracking the user_id and product_id of the latest bids for each product in a "latest_bid" table. A good way to maintain that table would be to add a trigger on the bids table so that every time a new bid is added, the latest_bid table is updated. You could also cache the state of the bidding in a similar way - so the current bidder for a product is only recalculated when a new bid for that product arrives.
Martin