SQL query to find a winner depending on winning odds - mysql

I need a SQL query to determine a random winner. Each user has it's own winning odds. The more the winning_odds value is high, the more the user has chances to win. Here's a look at the table structure:
id email winning_odds
1 test#test.com 3
2 test2#test.com 5
3 test3#test.com 2
4 test4#test.com 1
5 test5#test.com 9
MySQL database. Table has approximately 100000 rows. There's only one winner, one time. Emails are unique. Anyone has a solution?
Thanks.

Select email from user order by winning_odds*rand() limit 1

I really liked this question, I'm posting the answer for postgresql.
select
*, generate_series(1, mytable.winning_odds)
from
mytable
order by
random()
limit 1;
This is how it works. For each row of your table, we replicate the row N times as your winning odds.
So you get at first and without limiting the query results:
5 | test5#test.com | 9 | 9
2 | test2#test.com | 5 | 3
3 | test3#test.com | 2 | 1
1 | test#test.com | 3 | 1
5 | test5#test.com | 9 | 5
1 | test#test.com | 3 | 3
5 | test5#test.com | 9 | 2
2 | test2#test.com | 5 | 4
2 | test2#test.com | 5 | 5
5 | test5#test.com | 9 | 1
4 | test4#test.com | 1 | 1
5 | test5#test.com | 9 | 7
5 | test5#test.com | 9 | 4
5 | test5#test.com | 9 | 6
2 | test2#test.com | 5 | 1
5 | test5#test.com | 9 | 8
3 | test3#test.com | 2 | 2
1 | test#test.com | 3 | 2
2 | test2#test.com | 5 | 2
5 | test5#test.com | 9 | 3
Now, selecting randomly any row of the generated table will reflect the probabilities of your winning_odds field.
All you have to do is to order it randomly and get the first record.
9 | test5#test.com | 9 | 2
Regards

I am speculating that the "odds" are not integers and that you want something that has a "9" to be nine times more likely than a "1".
The proper way to do this is with a cumulative sum. Then generate a random value between the min and max of the cumulative sum and choose the record that is in that range. The following query does this in MySQL:
select t.*
from (select t.*,
coalesce((select sum(odds) from t t2 where t2.id < t.id), 0) as cumsum,
const.sumodds
from t cross join
(select rand()*sum(odds) as val from t) const
) t
where val between cumsum and cumsum + t.odds
However, this is doing a non-equijoin and would probably be prohibitively expensive in MySQL. Other databases have the ability to do a cumulative sum in a single query. MySQL does not have an efficient way of doing this.
How to optimize the query depends on certain other factors in the problem. How many different values do "odds" take on? Can you use temporary tables?
I don't have the time right now to write out the solution, but there is a more efficient way. YThe idea is to split the problem into two searches. The first will find which "odds" value wins. The second will find which row wins.
Here are the details:
(1) Summarize the data into a table by the odds. This table would have 11 rows, and contain the "odds" and the "count" for each.
(2) Calculate the sum of "count*odds" for each row, starting at 0 for the first row. You can use the above query as a guide, since this is such a small amount of data it will run quickly.
(3) Calculate a random number as rand()*<sum of all odds>. Now, locate the odds where the number is between cumsum an cumsum+odds.
(4) Now return to the original table and issue a query such as:
select *
from t
where odds = <winning odds>
order by rand()
limit 1

If I understand the question correctly, you are asking how to select a random record from the table. This should work:
SELECT *
FROM tableName
ORDER BY RAND() LIMIT 0,1;
Still now clear how are you planning to user the winning_odds value.

Related

How can I get the last row of every stage of every job? [duplicate]

This question already has an answer here:
Mysql group by two columns and pick the maximum value of third column
(1 answer)
Closed 2 years ago.
I've been taking too long trying to solve this, I need to filter this table:
+----+-------+-------+
| id | jobID |stageID|
+----+-------+-------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 2 |
| 4 | 1 | 1 |
| 5 | 2 | 2 |
| 6 | 2 | 1 |
| 7 | 2 | 1 |
| 8 | 2 | 2 |
+----+-------+-------+
You see every job has many rows with different stages, I need to get the last row of every stage of every job.
For example, look at job 1. It has 4 rows, each one with a given stage. I'd need to get the last entry of a stage for that job, which means, rows 3 and 4.
So for the full table I need to get rows No. 3, 4, 7 and 8, like this
+----+-------+-------+
| id | jobID |stageID|
+----+-------+-------+
| 3 | 1 | 2 |
| 4 | 1 | 1 |
| 7 | 2 | 1 |
| 8 | 2 | 2 |
+----+-------+-------+
I think I'll go nuts. I try with GROUP_BY but it groups the stages without taking in count the jobs.
Can you help me?
This is simply:
select max(id) as id, jobID, stageID
from yourtable
group by jobID, stageID
If you need additional information selected, use that as a subselect:
select yourtable.id, yourtable.jobID, yourtable.stageID, yourtable.other
from (
select max(id) as id
from yourtable
group by jobID, stageID
) max_job_stage_ids
join yourtable using (id)
or use IN (I find this less helpful in visualizing the query plan, but some people prefer it):
select id, jobID, stageID, other
from yourtable
where id in (select max(id) from yourtable group by jobID, stageID)

MySQL Query to get only one entry per interval from database

I have a table with this structure and some sample values:
ID | created | value | person
1 | 1 | 5 | 1
2 | 2 | 2 | 2
3 | 3 | 3 | 3
4 | 4 | 5 | 1
5 | 5 | 1 | 2
6 | 6 | 32 | 3
7 | 7 | 9 | 1
8 | 8 | 34 | 2
10 | 9 | 25 | 3
11 | 11 | 53 | 1
12 | 12 | 52 | 2
13 | 13 | 15 | 3
... etc
The created column will have timestamps. I.e. A number like "1555073978". I just made it incremental to demonstrate that the timestamps will rarely be the same.
So values are stored per person with creation times. Values are added every minute. After a week, this table is quite big. So when I do a query to draw a graph, PHP run's out of memory because the dataset is so huge.
So what I am looking for, is an easy way to do a query on a table like this, so that I get values in smaller intervals.
How would I query this table, so that I get:
- only one value per person per interval
- where interval should be 15 mins, 30 mins, 60 mins etc (i.e. a parameter in the query)
I've started with an approach but don't want to spend too much time, in case i am missing a much easier way. My way involves converting the timestamp to YEAR-MONTH-DAY-HOUR, but this will only work for hourly. I am also struggling to make sure that the query returns the MOST RECENT entry PER PERSON for that hour.
Any help would be greatly appreciated.
assuming your created column is a timestamp and you want the max value for person every each 15 minutes you could try
select person, max(value)
from my_table
group by person, FLOOR(UNIX_TIMESTAMP(created )/(15 * 60))
but if you dont need unix_timestamp
then
group by person, FLOOR(created /(15 * 60))
If you want the most recent values for person and interval then you could use
select * from my_table m
inner join (
select person, max(created) max_created
from my_table
group by person, FLOOR(UNIX_TIMESTAMP(created )/(15 * 60))
) t on t.person = m.person and t.max_created = m.created

Retrieving the most recent entry per user

If I have a table with the following structure and data:
id | user_id | created_at
-------------------------
1 | 7 | 0091942
2 | 3 | 0000014
3 | 6 | 0000890
4 | 6 | 0029249
5 | 7 | 0000049
6 | 3 | 0005440
7 | 9 | 0010108
What query would I use to get the following results (explanation to follow):
id | user_id | created_at
-------------------------
1 | 7 | 0091942
6 | 3 | 0005440
4 | 6 | 0029249
7 | 9 | 0010108
As you can see:
Only one row per user_id is returned.
The row with the highest created_at is the one returned.
Is there a way to accomplish this without using subqueries? Is there a name in relational algebra parlance that this procedure goes by?
The query is known as a groupwise maximum, which (in MySQL, at least) can be implemented with a subquery. For example:
SELECT my_table.* FROM my_table NATURAL JOIN (
SELECT user_id, MAX(created_at) created_at
FROM my_table
GROUP BY user_id
) t
See it on sqlfiddle.
You can just get the max and group by the user_id:
select id,user_id,max(created_at)
from supportContacts
group by user_id
order by id;
Here is what it outputs:
ID USER_ID MAX(CREATED_AT)
1 7 91942
2 3 5440
3 6 29249
7 9 10108
See the working demo here
Note that the example on the fiddle uses the created_at field as int, just use your format it should make no difference.
EDIT: I will leave this answer as a referece but note that his query will produce undesired results as Gordon stated, please do not use this in production.

If column is greater than "int", appear more than once

I've got a table which contains week numbers and id's
| id | week |
| 1 | 1 |
| 2 | 2 |
| 3 | 6 |
| 4 | 8 |
What I need to do is retrieve based on the value of week. But I also need to return the row more than once if the row is greater than a certain number.
For instance (pseudo),
if week > 2
and week > 6
| id | week |
| 2 | 2 |
| 3 | 6 |
| 3 | 6 |
| 4 | 8 |
| 4 | 8 |
ID 3 & ID 4 appear twice, because they're greater than both 2 and 6. ID 2 will only appear once because it is only greater than 2.
Unfortunately looped queries are sort of out of the question as this query will be return 2k+ rows.
Any help will be greatly appreciated, even if the solution is PHP based.
Split the query into multiple queries and then merge the result using UNION.
The following should give you the result you want.
(SELECT * FROM yourTable WHERE week > 2)
UNION ALL
(SELECT * FROM yourTable WHERE week > 6)
edited included the hint by Lily - thx.

mysql Select question

I can't get on the right track with this, any help would be appreciated
I have one table
+---+----------+---------+-----------+
|id | match_id | team_id | player_id |
+---+----------+---------+-----------+
| 1 | 9 | 10 | 5 |
| 2 | 9 | 10 | 7 |
| 3 | 9 | 10 | 9 |
| 4 | 9 | 11 | 12 |
| 5 | 9 | 11 | 15 |
| 6 | 9 | 11 | 18 |
+---+----------+---------+-----------+
I want to select these with a where on the match_id and both team id's so the output will be
+---------+-------+------+---------+---------+
| MATCHID | TEAMA | TEAMB| PLAYERA | PLAYERB |
+---------+-------+------+---------+---------+
| 9 | 10 | 11 | 5 | 12 |
| 9 | 10 | 11 | 7 | 15 |
| 9 | 10 | 11 | 9 | 18 |
+---------+-------+------+---------+---------+
It's probably very simple, but i'm stuck..
thanks in advance
p.s. seemed to forgot a column on my first post, sorry
I think you should redesign your table though, maybe the format that you want as output should be your table design.
With your design, it's possible to have three or more teams playing against each other...
So. I gave this another try (coming from Oracle myself, I really miss ROWNUM here).
The following query should give you the result you want to have, but I'm not sure if you should really do that in pure SQL. Maybe you could just combine the teams in your client?
SELECT m1.match_id, m1.team_id, m2.team_id, m1.player_id, m2.player_id
FROM (
SELECT match_id, team_id, player_id,
-- get ranking
( SELECT 1 + count(*)
FROM matches m1b
WHERE m1b.match_id = m1a.match_id
AND m1b.team_id = m1a.team_id
AND m1b.player_id < m1a.player_id) rank
FROM matches m1a
WHERE m1a.team_id = (SELECT MIN(team_id) -- first team
FROM matches
WHERE match_id = m1a.match_id)
) m1,
(
SELECT match_id, team_id, player_id,
-- get ranking
( SELECT 1 + count(*)
FROM matches m2b
WHERE m2b.match_id = m2a.match_id
AND m2b.team_id = m2a.team_id
AND m2b.player_id < m2a.player_id) rank
FROM matches m2a
WHERE m2a.team_id = (SELECT MAX(team_id) -- second team
FROM matches
WHERE match_id = m2a.match_id)
) m2
WHERE m1.match_id = m2.match_id
AND m1.rank = m2.rank
What I do here is:
Select all ROWs from the teams with lower team_id per match and give them a ranking (1 to 3 per match)
Select all ROWs from the teams with higher team_id per match and give them a ranking (1 to 3 per match)
Combine those two queries in one result, where the match_id and the ranking match
match is a reserve word in mysql. table name used here is matchs
select match_id, sum(if(id=1, team_id,0))team_A, sum(if(id=2,team_id,0)) team_b
from matchs
group by match_id;
+----------+--------+--------+
| match_id | team_A | team_b |
+----------+--------+--------+
| 5 | 9 | 10 |
+----------+--------+--------+
1 row in set (0.00 sec)
I'm not sure if the previous answers will give you what you're looking for, at least I took your question to mean something else - perhaps you could clarify the purpose of the table and the query. If the table associates teams with matches and you want a query to show you all the teams associated with one match, then your query should be
select team_id as teams from table where match_id = id_here
which would give you back (for id_here being 5)
teams
-----
9
10
Take a look at the url below, It is exactly what you want but is in t-sql. It can merge any number of rows.
Converting fields into columns