I'm new to MySQL and I'm trying to figure out how to calculate the time passed between the rows in a log table.
The table is a basic table with ID , Hostname , Info , Timestamp, with data like:
+---+----------+-------------------+---------------------+
|ID | Hostname | Info | Timestamp |
+---+----------+-------------------+---------------------+
|445| switch1 | "port 1 inserted" | 2013-01-19 19:51:40 |
|446| switch1 | "port 2 inserted" | 2013-01-19 19:59:41 |
|447| Router2 | "alarm fan speed" | 2013-01-19 20:00:40 |
|448| switch1 | "alarm fan speed" | 2013-01-19 20:12:20 |
|449| Router2 | "alarm fan speed" | 2013-01-19 21:42:41 |
+---+----------+-------------------+---------------------+
So basically I want to get the time difference between the rows with the same HOSTNAME, in this case between row 445 and 446 it would result in 8 minutes 1 second. And between 446 and 448 it would result in 12 minutes and 39 seconds . And so on...
Any tips on this would be greatly appreciated.
This will give you the time difference in seconds between rows:
SELECT c.info,
CASE
WHEN f.`timestamp` IS NOT NULL THEN
Timestampdiff(second, f.`timestamp`,
c.`timestamp`)
ELSE NULL
end AS time_diff
FROM (SELECT #rowa := #rowa + 1 AS id,
a.hostname,
a.info,
a.`timestamp`
FROM sparkles a
JOIN (SELECT #rowa := 0) b
WHERE a.hostname = 'switch1') c
LEFT JOIN (SELECT #rowb := #rowb + 1 AS id,
d.hostname,
d.info,
d.`timestamp`
FROM sparkles d
JOIN (SELECT #rowb := 0) e
WHERE d.hostname = 'switch1') f
ON f.id = c.id - 1
Result (for switch1 as the hostname)
| INFO | TIME_DIFF |
-------------------------------
| port 1 inserted | (null) |
| port 2 inserted | 481 |
| alarm fan speed | 759 |
See the demo
Result (for Router2 as the hostname)
| INFO | TIME_DIFF |
-------------------------------
| alarm fan speed | (null) |
| alarm fan speed | 6121 |
See the demo
Try this:
SELECT id, IF(#lastdate = '', NULL, TIMESTAMPDIFF(SECOND, TIMESTAMP, #lastdate)),
#lastdate:=TIMESTAMP
FROM tablename, (SELECT #lastdate:='') a;
Related
I'm having a hard time setting up a query(select). Database is not my specialty, so I'm turning to the experts. Let me show what I need.
----companies--- ----company_server----- -----servers---- -----print------------------------
| id | name | | company | server | | id | name | | id |page|copy | date |server
|----|-------- | |---------|----------| |----|-------- | |----|----|-----|-------------
| 1 | Company1 |1--N| 1 | 1 |N*--1| 1 | Server1 |1--N| 1 | 2 | 3 | 2020-1-11 | 1
| 2 | Company2 | | 2 | 1 | | 2 | Server2 | | 2 | 1 | 6 | 2020-1-12 | 3
| 3 | Company3 | | 3 | 2 | | 3 | Server3 | | 3 | 4 | 5 | 2020-1-13 | 4
| 3 | 3 | | 4 | Server4 | | 4 | 5 | 3 | 2020-1-15 | 2
| 5 | 3 | 4 | 2020-1-15 | 4
| 6 | 1 | 2 | 2020-1-16 | 3
| 7 | 2 | 2 | 2020-1-16 | 4
What I need?
Example where date between CAST(2020-1-12 AS DATE) AND CAST(2020-1-15 AS DATE) group by servers.id
| companies | server | sum | percent
------------------------------------------------------------------------------------
| company1,company2 | server1 | sum(page*copy) = 0 or null | 0 or NULL
| company3 | server2 | sum(page*copy) = 15 | 28.30
| company3 | server3 | sum(page*copy) = 6 | 11.32
| NULL | server4 | sum(page*copy) = 32 | 60.38
Few notes:
I need this query for MYSQL;
Every Company is linked to at least one server.
I need result grouped by server. So, every company linked to that server must be concatenated by a comma.
If the company has not yet been registered, the value null should be presented.
The sum (page * copie) must be presented as zero or null (I don't care) in the case that there was no printing in the date range.
The percentage should be calculated according to the date range entered and not with all records in the database.
The field date is stored as MYSQL DATE.
Experts, I thank you in advance for your help. I currently solve this problem with at least 03 queries to the database, but I have a conviction that I could do it with just one query.
Added a fiddle. Sorry. Im still learing how to use this.
https://www.db-fiddle.com/f/dXej7QCPe9iDopfYd1SfVh/2
Follows the query that more or less represents how far I had arrived. Notice that in the middle of the way 'server4' disappeared because there are no values for it in print in the period searched for him and I am in possession of the total of the period but I cannot calculate the percentage.
i'm stuck
select
*
from
(select
sum(p.copy * p.page) as sum1,
s.name as s_name,
s.id as s_id
from
print p
join servers s on s.id = p.server
where p.date between cast('2020-1-12' as date) and cast('2020-1-15' as date)
group by s.id) as t1
join company_server cs on cs.server = t1.s_id
right join companies c on c.id = cs.company
cross join(
select
sum(p1.copy * p1.page) sum2
from
print p1
where p1.date between cast('2020-1-12' as date) and cast('2020-1-15' as date)
) as c;
I did this query before you add fiddle, so may be name of column of mine is not same as you. Anyway, this is my solution, hope it help you.
select group_concat(c.name separator ',') as name_company,
ss.name,
sum_print as sum,
(sum_print/total) *100 as percentage
from companies c
inner join company_server cs on c.id = cs.company
right join servers ss on ss.id = cs.id
left join
(
select server,sum(page*copy) as sum_print, date from print
where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)
group by server
) tmp on tmp.server = ss.id
cross join
(select sum(page*copy) as total from print where date between CAST('2020-1-12' AS DATE) AND CAST('2020-1-15' AS DATE)) tmp2
group by id
Group and concat by comma, using GROUP_CONCAT .
You can reference this image for JOIN clause.
https://i.stack.imgur.com/6cioZ.png
Part 1 of my SQL task involves restructuring data. The jist of my task is as follows: Based on the event_type, if it is "begin" I am trying to use that "time" to find it's stopping time (in another row) and add it to a column (event_end) on the same row as the start time so that all the data for an event sits nicely in one row.
pID customerID locationID event_type time event_end (new row)
1 1 a begin 12.45
2 2 a begin 11.10
3 1 a stop 1.30
4 2 b begin 9.45
5 3 b stop 8.78
I would like to add another column (event_end), and have event_end = the minimum value of event_start IF event_start = 'stop', IF locationID = locationID, and IF customerID = customerID. The final step would be to delete all event_start 'begin' rows.
I have tried UPDATE SET WHERE sequences, and a little bit of CASE, but my issue is that I cannot wrap my head around how to perform this without a loop like VBA. The following is my best stab at it:
UPDATE table
SET event_end = MIN(time)
WHERE event_type = 'stop'
WHERE customerid = customerid
WHERE locationid = locationid
WHERE time > time
SELECT *
FROM table
I'm hoping to have a table with all event data in one row, not spread out over multiple rows. If this is a handful, I appologize but am thankful in advance.
Thanks
Problem Statement:
Add event_end as an extra attribute to the existing row, data will be populated based on customer_id, location_id.
We will populate data in event_end to all events which have event type as begin
Data would be picked from rows which have the same customer_id, location_id but event type as stop.
Finally, we will remove all events with type stop.
Solution: Consider your table name is customer_events and will use self join concept for the same.
First, identify which records needs to be updated. We can use a SELECT query to identify such records.
c1 table will represent rows with begin event type.
c2 table will represent rows with stop event type.
SELECT *
FROM customer_events c1
LEFT JOIN customer_events c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.event_type = 'begin' AND c2.event_type = 'stop'
WHERE c1.event_type = 'begin'; -- As we want to populate data in events with value as `begin`
Write a query to update the records.
UPDATE customer_events c1
LEFT JOIN customer_events c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.event_type = 'begin' AND c2.event_type = 'stop'
SET c1.event_end = c2.`time`
WHERE c1.event_type = 'begin';
Now every record with event type as begin has either value in event_end column or it would be null if no records match as stop event.
For rows with event type as stop, either they are mapped with some row with event type as begin or some are not mapped. In both cases, we don't want to keep them. To remove all records with event type as stop.
DELETE FROM customer_events
WHERE event_type = 'stop';
Note: Don't run DELETE statement unless you are sure that this solution will work for you.
Updated: We can have multiple records of begin & stop events for single customer & location.
Sample Input:
| pID | customerID* | *locationID* | *event_type* | *time* | *event_end* |
| 1 | 1 | a | begin | 02:45:00 | |
| 2 | 2 | a | begin | 03:10:00 | |
| 3 | 1 | b | begin | 04:30:00 | |
| 4 | 2 | b | begin | 05:45:00 | |
| 5 | 2 | a | stop | 06:49:59 | |
| 6 | 1 | a | begin | 07:38:00 | |
| 7 | 3 | b | begin | 08:57:19 | |
| 8 | 2 | b | stop | 09:57:43 | |
| 9 | 3 | b | stop | 10:58:03 | |
| 10 | 4 | a | begin | 11:58:34 | |
| 11 | 1 | a | stop | 12:09:36 | |
| 12 | 1 | b | stop | 13:09:50 | |
| 13 | 1 | a | stop | 14:10:02 | |
Query:
SELECT *
FROM (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'begin'
ORDER BY customerId, locationID, `time`) AS c1
LEFT JOIN (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'stop'
ORDER BY customerId, locationID, `time`
) AS c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.rank = c2.rank;
Output:
| pId | customerID| locationId| event_type| Start_Time|End_Id| End_Time |
| 1 | 1 | a | begin | 02:45:00 | 11 | 12:09:36 |
| 6 | 1 | a | begin | 07:38:00 | 13 | 14:10:02 |
| 3 | 1 | b | begin | 04:30:00 | 12 | 13:09:50 |
| 2 | 2 | a | begin | 03:10:00 | 5 | 06:49:59 |
| 4 | 2 | b | begin | 05:45:00 | 8 | 09:57:43 |
| 7 | 3 | b | begin | 08:57:19 | 9 | 10:58:03 |
| 10 | 4 | a | begin | 11:58:34 | | |
Update Statement: Create two columns end_pID and event_end for migration.
UPDATE customer_events
INNER JOIN (
SELECT c1.pId, c2.pID End_Id, c2.time AS End_Time
FROM (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'begin'
ORDER BY customerId, locationID, `time`) AS c1
LEFT JOIN (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'stop'
ORDER BY customerId, locationID, `time`
) AS c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.rank = c2.rank) AS tt ON customer_events.pID = tt.pId
SET customer_events.end_pID = t.End_Id, customer_events.event_end = t.End_Time;
Finally, remove all events with event_type = 'stop'
This is example of my table :
+-----+-----+------------+--------+-------------+--------------+
| LID | AID | Created | TypeID | PaymentDate | PaymentValue |
+-----+-----+------------+--------+-------------+--------------+
| 1 | 529 | 2017-05-12 | 1 | 2017-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 2 | 529 | 2018-04-10 | 4 | 2018-04-10 | 200 |
+-----+-----+------------+--------+-------------+--------------+
| 3 | 441 | 2014-01-23 | 3 | 2014-01-23 | 300 |
+-----+-----+------------+--------+-------------+--------------+
| 4 | 324 | 2017-09-14 | 1 | 2017-09-14 | 400 |
+-----+-----+------------+--------+-------------+--------------+
| 5 | 111 | 2018-05-12 | 0 | 2018-05-12 | 340 |
+-----+-----+------------+--------+-------------+--------------+
| 6 | 529 | 2018-05-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 7 | 529 | 2018-06-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 8 | 529 | 2018-07-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 9 | 529 | 2018-08-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 10 | 529 | 2018-09-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 11 | 529 | 2018-01-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 12 | 529 | 2018-05-14 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 13 | 529 | 2018-05-21 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 14 | 529 | 2018-03-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
Here another table
+-----+-------+
| ID |caption|
+-----+-------+
| 0 | bad |
+-----+-------+
| 1 | good |
+-----+-------+
I need to get 10 latest records per AID. If there less than 10 records for some AID anyway i need to get ten rows and put "No payment date" into PaymentDate and Created fields, Null into TypeID and 0 into PaymentValue. I can get 10 or less latest records with
select *
from (select *,
(#rn := if(#c = AID, #rn + 1,
if(#c := AID, 1, 1)
)
) as rn
from history cross join
(select #rn := 0, #c := -1) params
order by AID, Created desc
) t
having rn <= 10;
But i dont know how force mysql to output 10 rows for each AID. Help me please.
Result should be in a form
AID,TypeId,Created,Caption
I have done it.
This query needs to create a row of 10 records to combine with distinct AID valies in the table. I was able to show the result for Amount and Create date and will leave it to you to continue since you will get the idea.
The critical part is to build a table with 10 rows times distinct AID so about 40 rows in table r. Then do a left join to table t which is similar to what you have done. Table t gets a rank of at most 10 records. Any missing rank up to 10 recs will be filled by table r. Coalesce will assign the default values such as 0 fro amount and 'no create date' for date.
http://sqlfiddle.com/#!9/855c21/2
SELECT coalesce(r.aid, t.aid) as aid,
coalesce(t.paymentvalue, 0) as paymentvalue,
coalesce(cast(t.created as char), 'no create date') as created
FROM (select * from (
select 1 as rw union
select 2 union select 3
union select 4 union select 5
union select 6 union select 7
union select 8 union select 9
union select 10) u
cross join (select distinct aid
from history) h
) as r
LEFT JOIN (
SELECT a.aid, a.paymentvalue,
a.created, count(*) rn
FROM history a
JOIN history b
ON a.aid = b.aid
AND a.created <= b.created
GROUP BY a.aid, a.created
HAVING COUNT(*) <= 10) t
on r.rw=t.rn and r.aid=t.aid
order by aid, created;
I have added RIGHT JOIN to bring in the null rows to top up to 10 (or n) rows per AID. Initially I use SELECT 1 UNION SELECT 2 ... to generate the 10 rows. In order to make it easier to increase the number of rows (say 100), I am trying this idea of generate_series equivalent for mysql. In order for this to work, the number of rows in history table must be equal to greater than the number of rows required per AID.
select t1.lid
,t2.aid
,coalesce(t1.created, "no created date") as created
,t1.typeID
,coalesce(t1.paymentdate, "no payment date") as paymentDate
,coalesce(t1.paymentvalue, 0) as paymentValue
,t2.rn
from
(
select *,
(#rn := if(#c = AID, #rn + 1,
if(#c := AID, 1, 1)
)
) as rn
from history cross join
(select #rn := 0, #c := -1) params
order by AID, Created desc
) t1
right join
( select *
from (select distinct aid from history ) h1
cross join
(select rn -- generate table with n rows numbered from 1 to n
from
(select
#num:= 0) init
cross join
(select #num := #num +1 rn
from history ) t -- assume history has at least 10 rows
limit
10 ) h2 -- n = 10; change it to the number of rows per aid required
) t2
on t1.aid = t2.aid and t1.rn = t2.rn
order by t2.aid, t2.rn
Problem: Get first free port for server between exists values. If there is not a free place then take the highest + 1.
Extra: If server_deleted=1 then we can take the free port. Also we should take the free port when there is a gap between values. We should include server_type in where clause. The min value is first port e.g. 9000.
Now my query looks:
SELECT server_port + 1
FROM pro_servers s
WHERE s.server_port <> 0
AND s.server_type = 'ts3'
AND s.server_deleted = 0
AND NOT EXISTS
( SELECT s1.server_port
FROM pro_servers s1
WHERE s1.server_port <> 0
AND s1.server_type = 'ts3'
AND s1.server_port = s.server_port + 1
AND s1.server_deleted = 0
)
ORDER BY server_port LIMIT 1
I think there is better way to do it. This query executes very slow.
For example the first=minimum=lowest = 9000, next is 9002, 9003. We need to get the 9001. If we add 9001 then get 9004. The first value 9000 exists in the table.
SAMPLE DATA
+-----------+-------------+----------------+-------------+
| server_id | server_port | server_deleted | server_type |
+-----------+-------------+----------------+-------------+
| 151 | 9500 | 1 | teamspeak3 |
| 8459 | 9500 | 0 | teamspeak3 |
| 183 | 9501 | 1 | teamspeak3 |
| 264 | 9502 | 1 | teamspeak3 |
| 4155 | 9502 | 1 | teamspeak3 |
| 2707 | 9503 | 1 | teamspeak3 |
| 4160 | 9503 | 1 | teamspeak3 |
| 154 | 9504 | 1 | teamspeak3 |
| 4163 | 9504 | 1 | teamspeak3 |
| 285 | 9506 | 1 | teamspeak3 |
| 4167 | 9506 | 1 | teamspeak3 |
| 8454 | 9506 | 0 | teamspeak3 |
| 241 | 9507 | 1 | teamspeak3 |
| 4169 | 9507 | 1 | teamspeak3 |
| 188 | 9509 | 1 | teamspeak3 |
| 4177 | 9509 | 1 | teamspeak3 |
+-----------+-------------+----------------+-------------+
QUERY RESULT: 9501. When we used this port then next: 9502, 9503, 9504, 9505, 9507, 9508, 9509, 9510 etc.
SELECT data.sPort
FROM
((SELECT (s.server_port + 1) sPort
FROM pro_servers s
LEFT JOIN pro_servers sp1 ON sp1.server_port = s.server_port + 1
WHERE (sp1.server_port IS NULL)
ORDER BY sPort)
UNION ALL
(SELECT s.server_port sPost
FROM pro_servers s
GROUP BY s.server_port
HAVING COUNT(s.server_port) = SUM(s.server_deleted)
ORDER BY sPort)) AS data
ORDER BY data.sPort
LIMIT 1
SqlFiddle: http://sqlfiddle.com/#!2/12ab1/2
How it works
Left Join pro_servers with pro_servers with server_port = server_port + 1 join condition and take rows, where there is null in the next port. These rows shows first ports from every gap. The port Id can be taken as server_port + 1.
Take all deleted ports.
Union 1. and 2., order by server_port and take the first one.
There is one assumption for an answer - port with the lowest number is always taken. If it's not true, check that port separately (or add another UNION ALL to the query).
Also, you can try to index the table.
CREATE INDEX indexName ON tableName(field1 [,field2...]);
I don't know if this will be faster but it will certainly help you to avoid repeating same conditions twice:
SELECT
MAX(server_port) + 1 AS first_available_port
FROM (
SELECT
server_port,
#row := #row + 1 AS row
FROM
pro_servers AS s,
(SELECT #row := 0) AS x
WHERE server_port <> 0
AND server_type = 'ts3'
AND server_deleted = 0
) AS s
GROUP BY
server_port - row
ORDER BY
server_port - row
LIMIT 1
;
The inner query enumerates existing ports matching the conditions. Now, the difference between the port numbers and the row numbers will stay the same for rows belonging to the same group of consecutive rows. The outer query groups by that difference and returns the first group's highest port plus one.
I would use this query that uses NOT EXISTS:
SELECT MIN(server_port)+1
FROM pro_servers p1
WHERE
p1.server_type = 'ts3' AND
NOT EXISTS (SELECT server_port
FROM pro_servers p2
WHERE p1.server_port=p2.server_port-1
AND p1.server_type=p2.server_type
AND p2.server_deleted=0)
or this that uses a LEFT JOIN:
SELECT MIN(p1.server_port)+1
FROM
pro_servers p1 LEFT JOIN pro_servers p2
ON p1.server_port=p2.server_port-1
AND p1.server_type=p2.server_type
AND p2.server_deleted=0
WHERE
p2.server_port IS NULL
AND p1.server_type='ts3'
Please see fiddle here.
I have a table that holds usernames and results.
When a user insert his results to the DB, I want to execute a query that will return
the top X results ( with their rank in the db) and will also get that user result
and his rank in the DB.
the result should be like this:
1 playername 4500
2 otherplayer 4100
3 anotherone 3900
...
134 current player 140
I have tried a query with union, but then I didnt get the current player rank.
ideas anyone?
The DB is MYSQL.
10x alot and have agreat weekend :)
EDIT
This is what I have tried:
(select substr(first_name,1,10) as first_name, result
FROM top_scores ts
WHERE result_date >= NOW() - INTERVAL 1 DAY
LIMIT 10)
union
(select substr(first_name,1,10) as first_name, result
FROM top_scores ts
where first_name='XXX' and result=3030);
SET X = 0;
SELECT #X:=#X+1 AS rank, username, result
FROM myTable
ORDER BY result DESC
LIMIT 10;
Re your comment:
How about this:
SET X = 0;
SELECT ranked.*
FROM (
SELECT #X:=#X+1 AS rank, username, result
FROM myTable
ORDER BY result DESC
) AS ranked
WHERE ranked.rank <= 10 OR username = 'current';
Based on what I am reading here:
Your table structure is:
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| name | varchar(50) | YES | | NULL | |
| result | int(11) | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+
Table Data looks like:
+---------+--------+
| name | result |
+---------+--------+
| Player1 | 4500 |
| Player2 | 4100 |
| Player3 | 3900 |
| Player4 | 3800 |
| Player5 | 3700 |
| Player6 | 3600 |
| Player7 | 3500 |
| Player8 | 3400 |
+---------+--------+
You want a result set to look like this:
+------+---------+--------+
| rank | name | result |
+------+---------+--------+
| 1 | Player1 | 4500 |
| 2 | Player2 | 4100 |
| 3 | Player3 | 3900 |
| 4 | Player4 | 3800 |
| 5 | Player5 | 3700 |
| 6 | Player6 | 3600 |
| 7 | Player7 | 3500 |
| 8 | Player8 | 3400 |
+------+---------+--------+
SQL:
set #rank = 0;
select
top_scores.*
from
(select ranks.* from (select #rank:=#rank+1 AS rank, name, result from ranks) ranks) top_scores
where
top_scores.rank <= 5
or (top_scores.result = 3400 and name ='Player8');
That will do what you want it to do
assuming your table has the following columns:
playername
score
calculated_rank
your query should look something like:
select calculated_rank,playername, score
from tablename
order by calculated_rank limit 5
I assume you have PRIMARY KEY on this table. If you don't, just create one. My table structure (because you didn't supply your own) is like this:
id INTEGER
result INTEGER
first_name VARCHAR
SQL query should be like that:
SELECT #i := #i+1 AS position, first_name, result FROM top_scores, (SELECT #i := 0) t ORDER BY result DESC LIMIT 10 UNION
SELECT (SELECT COUNT(id) FROM top_scores t2 WHERE t2.result > t1.result AND t2.id > t1.id) AS position, first_name, result FROM top_scores t1 WHERE id = LAST_INSERT_ID();
I added additional condition into subquery ("AND t2.id > t1.id") to prevent multiple people with same result having same position.
EDIT: If you have some login system, it would be better to save userid with result and get current user result using it.