Answer the question
In order to leave comments, you need to log in
Complex database query
There are 2 tables:
PlayersData:
uid int PK
score int
level int
MapsScores
map_id int PK
user_id int PK
score int
The PlayersData table stores: the player's best result for all games (score), his id and level (level). The MapsScores table stores: map id (map_id), user id (user_id) and the score received by the player on the map (score). The MapsScores table also has a composite key by map id and player id.
The task is to build a top of players for a certain map, this query turned out like this (for example, we will output for a map with id = 3):
SELECT
ms.user_id uid, ms.score, pd.level
FROM
MapsScores ms
LEFT JOIN PlayersData pd ON ms.user_id = pd.uid
WHERE
ms.map_id = 3
ORDER BY ms.score DESC
LIMIT 20
Answer the question
In order to leave comments, you need to log in
If you uploaded the test data somewhere, it would be easier to check, but there is such a trick with MySQL - it's not a fact that it will work faster, and it looks creepy, so the choice to use it or not is entirely up to you :)
SELECT
t.map_id,
t.score,
pd.level
FROM
(SELECT
ms1.map_id,
ms1.score,
ms1.user_id
FROM
MapsScores ms1
LEFT JOIN MapsScores ms2
ON ms1.map_id = ms2.map_id
AND ms1.score >= ms2.score
GROUP BY ms1.map_id,
ms1.score
HAVING COUNT(*) <= 20) t
LEFT JOIN PlayersData pd
ON t.user_id = pd.uid
ORDER BY 1,
2 DESC
SELECT
p.uid, p.level, p.score, m.map_id
FROM PlayersData p
INNER JOIN MapsScores m
ON m.map_id=p.map_id
ORDER BY p.score DESC
GROUP BY p.map_id
HAVING COUNT(p.uid) <= 20
Something like this, maybe? Alas, I can't check.
Don't make a bulky request.
> map ids may not be in order
, this is not a problem, get a list of your map ids and foreach, or disinct by MapsScores and foreach
Well, if you want to get mad :)
SET @place := 0, @map_id := 0;
SELECT
IF(@map_id != map_id, @place := 1, @place := @place + 1) AS place,
IF(@map_id != map_id, @map_id:=map_id, map_id) AS map_id,
user_id, map_score , level, @place
FROM (
SELECT m.map_id, m.user_id, m.score AS map_score, p.level
FROM MapsScores AS m, PlayersData AS p
WHERE m.user_id = p.id
GROUP BY m.map_id, m.user_id
ORDER BY m.map_id ASC, m.score DESC
) AS tops
GROUP BY map_id, user_id HAVING place <= СКОЛЬКО_НАРОДУ_ХОТИТЕ_В_ТОП
ORDER BY map_id ASC, map_score DESC
This problem is many years old, and a lot has been written about it on various resources ( search ). In addition to the proposed options for queries of varying degrees of creepiness, you can use denormalization (entering data into an auxiliary top table on triggers or by cron).
this will output all but with sorting
SELECT m.map_id, m.user_id, m.score AS map_score, p.level
FROM MapsScores AS m, PlayersData AS p
WHERE m.user_id = p.id
GROUP BY m.map_id, m.user_id
ORDER BY m.map_id ASC, m.score DESC
The answer is not related to the question itself. But the problem itself would be solved well. You can cache the query result and update it in the background using cron once a minute. As a result, the frontend only works with the cache, and does not query the database at all.
In general, did as prompted.
1) I get the id of all cards.
2) For each card, I build a list with the query that is given in the question.
3) I cache the received data.
Caching had to be done in a perverse way. the server works for me on a virtual hosting and Memcached is not available.
The first thing that came to mind was to make a memory table and store the cached data in it, the data will be stored in json format. After tests, it turned out that 65536 bytes are not enough to store all (text) data. As a result, I had to make a regular table and store it with the blob data type .
For those interested, here is the code:
function CacheGet($key)
{
$query = "SELECT ";
$query .= " data ";
$query .= "FROM ";
$query .= " CacheTable ";
$query .= "WHERE ";
$query .= " name = \"".$key."\" ";
$query .= " AND (expire > ".time()." OR locked = 1)";
$result = mysql_query($query);
if ($result == false)
{
return false;
}
else
{
$data = mysql_fetch_array($result, MYSQL_ASSOC);
return $data[data];
}
}
// lock cache while one process write new updated data
function CacheLock($key)
{
$query = "INSERT INTO CacheTable (name, locked) ";
$query .= " VALUES(\"".$key."\", 1) ";
$query .= "ON DUPLICATE KEY UPDATE ";
$query .= " name = VALUES(name),";
$query .= " locked = VALUES(locked)";
mysql_query($query);
}
function CacheSet($key, $data, $expire)
{
$query = "INSERT INTO CacheTable (name, data, expire, locked) ";
$query .= " VALUES(\"".$key."\", \"".mysql_real_escape_string($data)."\", ".(time() + $expire).", 0) ";
$query .= "ON DUPLICATE KEY UPDATE ";
$query .= " name = VALUES(name), ";
$query .= " data = VALUES(data), ";
$query .= " expire = VALUES(expire), ";
$query .= " locked = VALUES(locked)";
mysql_query($query);
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question