Need help in database query for contest winners - php

I'm creating a web application where the user will be able to participate in a contest and based on their rank, the user will be rewarded.
(table name contest)
id | contest_name | status
------------------------------------
1 | Test Contest | active
(table participants)
id | user_id | contest_id | score | time_taken
----------------------------------------------------------
1 | 123 | 1 | 10 | 2332 --> in milliseconds
My contest table prize distribution (table name price_distribution)
id | contest_id | rank_start | rank_end | price
-------------------------------------------------------
1 | 1 | 1 | 10 | 50
-------------------------------------------------------
2 | 1 | 11 | 20 | 25
-------------------------------------------------------
Meaning if all the users that rank between 1 to 10, they will get 50 points and rank between 11-20, 20 points so on.
I've used this query to get all the list of users in the contest with their rank.
SELECT participants.score,
participants.time_taken,
contest.name as contest_name,
user.name as username,
user.image as userimage,
FIND_IN_SET( participants.score, (SELECT GROUP_CONCAT( participants.score ORDER BY participants.score DESC, participants.time_taken ASC )
FROM participants )) AS rank
FROM participants
LEFT JOIN contest
ON participants.contest_id = contest.id
LEFT JOIN user ON user.id = participants.user_id
WHERE participants.contest_id = '1'
ORDER BY participants.score DESC, participants.time_taken ASC
LIMIT 50
The above query results is this
score | time_taken | contest_name | username | userimage | rank
-----------------------------------------------------------------------
10 | 2356 | test_contest | abc | image | 1
-----------------------------------------------------------------------
The above query only lists the user based on rank and does nothing else.
I want to reward the user based on rank.. How to achieve this query.
I want to know the query which when executed will reward the user based on their rank and will take the value from the prize distribution table.
Any help will be greatly appreciated.

You can join the table price_distribution based on the user's score:
SELECT participants.score,
participants.time_taken,
contest.name as contest_name,
user.name as username,
user.image as userimage,
FIND_IN_SET( participants.score, (SELECT GROUP_CONCAT( participants.score ORDER BY participants.score DESC, participants.time_taken ASC )
FROM participants )) AS rank,
pd.price
FROM participants
LEFT JOIN contest ON participants.contest_id = contest.id
LEFT JOIN user ON user.id = participants.user_id
LEFT JOIN price_distribution pd
ON participants.contest_id = pd.contest_id and participants.score BETWEEN pd.rank_start AND pd.rank_end
WHERE participants.contest_id = '1'
ORDER BY participants.score DESC, participants.time_taken ASC
LIMIT 50

Related

Find max value with specific criteria and check if there is higher/lower or any value for same group

I am trying to get game... and max score for user and information is there higher/lower value(score of other players) than user's max value. I will explain situation.
User should get his games and max score for each game WHERE game is active. That works fine with JOIN and GROUP BY but I have no idea how to get information is there more scores and are they higher or lower than users max score.
Table 1: scores
+----+--------+------+--------+-------+
| id | gameID | game | player | score |
+----+--------+------+--------+-------+
| 1 | 1 | pang | lea | 50 |
| 2 | 1 | pang | lea | 60 |
| 3 | 1 | pang | paola | 70 |
| 4 | 2 | pong | lea | 100 |
| 5 | 2 | pong | paola | 90 |
+----+--------+------+--------+-------+
Table 2: games
+----+------+--------+
| id | name | active |
+----+------+--------+
| 1 | pang | yes |
| 2 | pong | yes |
| 3 | pung | yes |
+----+------+--------+
Code:
$loggedUser = 'lea';
$sql =
"
SELECT s.gameID
, s.game
, COUNT(s.id) c
, MAX(s.score) Max
FROM scores s
RIGHT
JOIN games g
ON s.gameID = g.id
WHERE g.active = 'yes'
AND s.player = '$loggedUser'
GROUP
BY s.gameID
ORDER
BY c
";
Output should look like this:
Hello Lea, your top scores:
pang played x 2, max score 60 pts (top score is 70)....
pong played x 1, 100 pts , you have higher score....
In MySQL 8.0, you can use window functions to:
rank the scores of a player for each game: ROW_NUMBER() OVER(PARTITION BY player, game ORDER BY score DESC)
count how many times a player played each game: COUNT(*) OVER(PARTITION BY player, game)
compute the top score of a given game amongst all players : MAX(score) OVER(PARTITION BY game ORDER BY score DESC)
Consider the following query:
SELECT game_name, cnt games_played, score max_score, top_score
FROM (
SELECT
g.name game_name,
s.player,
s.score,
ROW_NUMBER() OVER(PARTITION BY player, game ORDER BY score DESC) rn,
COUNT(*) OVER(PARTITION BY player, game) cnt,
MAX(score) OVER(PARTITION BY game ORDER BY score DESC) top_score
FROM scores s
INNER JOIN games g ON g.id = s.gameID AND g.active = 'yes'
) x WHERE rn = 1 AND player = 'lea';
In this db fiddle with your sample data, the query yields:
| game_name | games_played | max_score | top_score |
| --------- | ------------ | --------- | --------- |
| pang | 2 | 60 | 70 |
| pong | 1 | 100 | 100 |
In earlier versions of MySQL, one solution would be to use an aggregated subquery to compute the top score for each game amongst all players, and the JOIN it with the main aggregated query:
SELECT g.name game_name, COUNT(*) games_played, MAX(s.score) max_score, ts.top_score
FROM scores s
INNER JOIN games g
ON g.id = s.gameID AND g.active = 'yes'
INNER JOIN (SELECT gameID, MAX(score) top_score FROM scores GROUP BY gameID) ts
ON s.gameID = ts.gameID
WHERE s.player = 'lea'
GROUP BY s.player, g.id, g.name;
Demo on DB Fiddle.

SQL: Order by Messages doesn't work

why does this SQL Code not run?
user_chats
id | user_id | to_user_id | ad_id | timestamp
----------------+---------+------------+---------+-----------
1 | 1 | 6 | 13 | 1513516133
user_messages
id | chat_id | text | user_id | timestamp
----------------+---------+------------+---------+-----------
1 | 1 | Hello | 1 | 1513516133
2 | 1 | Hi! | 6 | 1513516754
I want to get the Chats and order them by user_messages.timestamp.
My SQL Code is:
SELECT user_chats.id,
user_chats.timestamp,
ad_id,
title,
user_chats.user_id
FROM user_chats
INNER JOIN ads
ON ads.id = ad_id
WHERE user_chats.user_id = "1"
OR user_chats.to_user_id = "1"
ORDER BY (SELECT id
FROM user_messages
WHERE chat_id = user_chats.id
ORDER BY user_messages.id DESC)
The issue is that you've used a subquery in your Order By clause: as this returns multiple results for each record in the main query it cannot be used to order the results of the main query.
I think you're trying to order the results by the latest message in each chat, but simply joining the user_messages table will mean you'll get duplicates (each chat being returned once per message). You can get around this by joining to an inline view:
SELECT DISTINCT user_chats.id,
user_chats.timestamp,
ad_id,
title,
user_chats.user_id
FROM user_chats
INNER JOIN ads
ON ads.id = ad_id
LEFT JOIN
--in line view aliased 'UM' returns one row per chat_id in user_messages, with the last timestamp for that ID
(SELECT max(timestamp) LastMessage,
chat_id
FROM user_messages
GROUP BY chat_id) um
ON um.chat_id = user_chats.id
WHERE user_chats.user_id = 1
OR user_chats.to_user_id = 1
ORDER BY um.LastMessage desc

Group by 2 columns regardless of order

The following query
select a.message, a.sender_id, a.rec_id, a.id, a.is_seen, b.total_msg, b.last_id, users.name
from tbl_message a left join users on (users.id=a.sender_id)
inner join
(select sender_id, rec_id, max(id) last_id, count(*) total_msg
from tbl_message group by sender_id,rec_id
)b on a.id=b.last_id
order by a.id desc
gives the result as below:
+----------------------------+-----------+--------+----+---------+-----------+---------+------+
| message | sender_id | rec_id | id | is_seen | total_msq | last_id | name |
+----------------------------+-----------+--------+----+---------+-----------+---------+------+
| latest testing l5 aug | 2 | 1 | 12 | 0 | 5 | 12 | B |
| testing | 1 | 2 | 11 | 1 | 3 | 11 | A |
| this msg of A | 1 | 3 | 9 | 0 | 1 | 9 | A |
| this is again 3rd msg of C | 3 | 1 | 6 | 1 | 3 | 6 | C |
+----------------------------+-----------+--------+----+---------+-----------+---------+------+
I want the result as:
For sender_id/rec_id = 1 or 2 id = 12 and for sender_id/rec_id = 1 or 3 id = 9
It sounds like you want to group rows by the sender_id,rec_id participants pair regardless of which order they appear in (i.e. sender_id,rec_id or rec_id,sender_id should be part of the same group).
If so, change your group by from
group by sender_id, rec_id
to
group by least(sender_id,rec_id), greatest(sender_id,rec_id)
Using greatest and least will ensure that each conversation will be grouped by the participants regardless of which order they appear in.
Looks like you need to join with all the grouped columns
Try this
SELECT a.message
,a.sender_id
,a.rec_id
,a.id
,a.is_seen
,b.total_msg
,b.last_id
,users.NAME
FROM tbl_message a
LEFT JOIN users ON (users.id = a.sender_id)
INNER JOIN (
SELECT sender_id
,rec_id
,max(id) last_id
,count(*) total_msg
FROM tbl_message
GROUP BY sender_id
,rec_id
) b ON a.sender_id=b.sender_id and a.rec_id=b.rec_id and a.id = b.last_id
ORDER BY a.id DESC
I think you need to remove one column from the GROUP BY clause of the derived table (subquery)
select a.message, a.sender_id, a.rec_id, a.id, a.is_seen, b.total_msg, b.last_id, users.name
from tbl_message a left join users on (users.id=a.sender_id)
inner join
(select sender_id, rec_id, max(id) last_id, count(*) total_msg
from tbl_message group by sender_id
)b on a.id=b.last_id
order by a.id desc
I would expect this to result in rows only for the following
SenderID ID
2 12
1 9
3 6

combine two tables in mysql for recent activity functionality

In my page I have a tab named Recent activity, In which I have to display the two different types of actions
Recently added choices
Recently voted choices
The table stucture for je_addchoice
je_addpoll table
poll_id | user_id | poll_name | category_id | start_date | end_date
1 | 20 |Naturalflrs| 18 | 2012-12-03 | 2095-12-25
je_addchoice table
choice_id | poll_id | choice_creator_id | choice_name | choice_image | description | ctime
1 | 1 | 20 | Greenish | forest.jpg | forest |135453
je_user_vote table
vote_id | user_id | poll_id | choice_id | datetime_voted | user_type
12 | 31 | 1 | 1 |12-12-2606:23:17| normal
Already I have the result page displays as shown below
The above result is displayed using the query
$result=mysql_query("SELECT * FROM je_addchoice, je_addpoll where je_addpoll.start_date <= '$check_date' AND je_addpoll.end_date >='$check_date' AND je_addpoll.poll_id=je_addchoice.poll_id order by je_addchoice.choicecreationtime desc");
The two tables
1) je_addpoll (Main table for polls)
2) je_addchoice (adding choices for the polls)
But what I want to do here is If any user votes for the poll, It will store into
je_user_vote table as shown above.
I want to display the recently voted choices in the same tab
Try this
use desc order for datetime_voted just before je_addchoice.choicecreationtime desc
like this.
$result=mysql_query("SELECT * FROM (SELECT P.poll_id,P.user_id,P.poll_name,P.category_id,P.start_date,P.end_date,C.choice_id,C.choice_creator_id,C.choice_name,C.choice_image,C.description,C.ctime FROM je_addchoice C, je_addpoll P where P.start_date <= '$check_date' AND P.end_date >='$check_date' AND P.poll_id=C.poll_id order by C.choicecreationtime desc) N,je_user_vote U WHERE U.poll_id=N.poll_id order by U.datetime_voted desc,N.choicecreationtime desc");
OR
$result=mysql_query("SELECT N.poll_id,N.user_id,N.poll_name,N.category_id,N.start_date,N.end_date,N.choice_id,N.choice_creator_id,N.choice_name,N.choice_image,N.description,N.ctime FROM (SELECT P.poll_id,P.user_id,P.poll_name,P.category_id,P.start_date,P.end_date,C.choice_id,C.choice_creator_id,C.choice_name,C.choice_image,C.description,C.ctime FROM je_addchoice C, je_addpoll P where P.start_date <= '$check_date' AND P.end_date >='$check_date' AND P.poll_id=C.poll_id order by C.choicecreationtime desc) N,je_user_vote U WHERE U.poll_id=N.poll_id order by U.datetime_voted desc,N.choicecreationtime desc");

MySQL+PHP: optimise ranking query and count subquery

This is raw data, and want to rank them according to score (count(tbl_1.id)).
[tbl_1]
===========
id | name
===========
1 | peter
2 | jane
1 | peter
2 | jane
3 | harry
3 | harry
3 | harry
3 | harry
4 | ron
So make temporary table (tbl_2) to count score for each id.
SELECT id, name, COUNT( id ) AS score
FROM tbl_1
GROUP BY id
ORDER BY score DESC;
LIMIT 0, 30;
Then result is;
[tbl_2]
===================
id | name | score
===================
3 | harry | 4
1 | peter | 2
2 | jane | 2
4 | ron | 1
Then query this;
SELECT v1.id, v1.name, v1.score, COUNT( v2.score ) AS rank
FROM votes v1
JOIN votes v2 ON v1.score < v2.score
OR (
v1.score = v2.score
AND v1.id = v2.id
)
GROUP BY v1.id, v1.score
ORDER BY v1.rank ASC, v1.id ASC
LIMIT 0, 30;
Then result is;
==========================
id | name | score | rank
==========================
3 | harry | 4 | 1
1 | peter | 2 | 2
2 | jane | 2 | 2
4 | ron | 1 | 4
Is it possible to do this in one transaction (query) nicely?
Yes, it's possible to do this in a single query. But it's a total hairball in MySQL, because MySQL doesn't have a simple ROWNUM operation, and you need one for the rank computation.
Here's your vote query with the rank shown. The #ranka variable is used to number the rows.
SELECT #ranka:=#ranka+1 AS rank, id, name, score
FROM
(
SELECT id,
name,
COUNT( id ) AS score
FROM tbl_1
GROUP BY id
ORDER BY score DESC, id
) votes,
(SELECT #ranka:=0) r
As you have discovered already, you need to self-join this thing to get a proper ranking (which handles ties correctly). So, if you take your query and replace the two references to your votes table each with their own version of this subquery, you get what you need.
SELECT v1.id,
v1.name,
v1.score,
COUNT( v2.score ) AS rank
FROM (
SELECT #ranka:=#ranka+1 AS rank,
id,
name,
score
FROM
(
SELECT id,
name,
COUNT( id ) AS score
FROM tbl_1
GROUP BY id
ORDER BY score DESC, name
) votes,
(SELECT #ranka:=0) r) v1
JOIN (
SELECT #rankb:=#rankb+1 AS rank,
id,
name,
score
FROM
(
SELECT id,
name,
COUNT( id ) AS score
FROM tbl_1
GROUP BY id
ORDER BY score DESC, name
) votes,
(SELECT #rankb:=0) r) v2
ON (v1.score < v2.score) OR
(v1.score = v2.score AND v1.id = v2.id)
GROUP BY v1.id, v1.score
ORDER BY v1.rank ASC, v1.id ASC
LIMIT 0, 30;
Told you it's a hairball. Notice that you need different #ranka and #rankb variables in the two versions of the subquery that you're self-joining, to make the row numbering work correctly: these variables have connection scope, not subquery scope, in MySQL.
http://sqlfiddle.com/#!2/c5350/1/0 shows this working.
Edit: It's far easier to do this using PostgreSQL's RANK() function.
SELECT name, votes, rank() over (ORDER BY votes)
FROM (
SELECT name, count(id) votes
FROM tab
GROUP BY name
)x
http://sqlfiddle.com/#!1/94cca/18/0

Categories