I'm ordering a recordset like this:
SELECT * FROM leaderboards ORDER BY time ASC, percent DESC
Say I have the id of the record which relates to you, how can I find out what position it is in the recordset, as ordered above?
I understand if it was just ordered by say 'time' I could
SELECT count from table where time < your_id
But having 2 ORDER BYs has confused me.
You can use a variable to assign a counter:
SELECT *, #ctr := #ctr + 1 AS RowNumber
FROM leaderboards, (SELECT #ctr := 0) c
ORDER BY time ASC, percent DESC
Does this do what you want?
SELECT count(*)
FROM leaderboards lb cross join
(select * from leaderboards where id = MYID) theone
WHERE lb.time < theone.time or
(lb.time = theone.time and lb.percent >= theone.percent);
This assumes that there are no duplicates for time, percent.
Related
I have met a situation that I have a list of IDs of a Store table and need to fetch the latest 10 files from each store.
SELECT *
FROM tblFiles
WHERE storeId in (IDs)
ORDER BY createdDate DESC
LIMIT 10
But, this limits the whole results. I found an answer to a similar SO question. But, the answer recommends using loop for each ID. This results in multiple DB hit.
Another option is to fetch all records and group them in the code. But, this will be heavy if there are large no.of records.
It'll be nice if it can be handled at the query level. Any help will be appreciated.
NB: The tables used here are dummy ones.
Pre-MySQL 8.0, the simplest method is probably variables:
select f.*
from (select f.*,
(#rn := if(#s = storeId, #rn + 1,
if(#s := storeId, 1, 1)
)
) as rn
from (select f.*
from tblfiles f
where storeId in (IDs)
order by storeId, createdDate desc
) f cross join
(select #s := 0, #rn := 0) params
) f
where rn <= 10;
In MySQL 8+ or MariaDB 10.3+, you would simply use window functions:
select f.*
from (select f.*,
row_number() over (partition by storeid order by createdDate desc) as seqnum
from tblfiles f
) f
where seqnum <= 10;
In older versions of MySQL and MariaDB, the innermost subquery may not be needed.
use select in where
SELECT * from tblFiles where storeId in (SELECT id from store ORDER BY datefield/id field desc limit 10)
You could workaround it with an UNIONed query, where each subquery searches for a particular id and enforces a LIMIT clause, like :
(SELECT *
FROM tblFiles
WHERE storeId = ?
ORDER BY createdDate DESC
LIMIT 10)
UNION
(SELECT *
FROM tblFiles
WHERE storeId = ?
ORDER BY createdDate DESC
LIMIT 10)
...
With this solution only one db hit will happen, and you are guarantee to get the LIMIT on a per id basis. Such a SQL can easily be generated from within php code.
Nb : the maximum allowed of UNIONs in a mysql query is 61.
I have the following table on my DataBase:
TimeRank(user-id, name, time)
I would like to order the table by time and get the position of an specific ID on the table, for example:
The user nÂș 68 is on the 3rd position.
I only need to do a query that returns the position of the user.
MySQL don't have the function row_number, so I don't know how to do it.
SELECT x.user-id,
x.name,
x.time,
x.position
FROM (SELECT t.user-id,
t.name,
t.time,
#rownum := #rownum + 1 AS position
FROM TABLE TimeRank t
JOIN (SELECT #rownum := 0) r
ORDER BY t.time) x
WHERE x.user-id = 123
Alternative:
SELECT user-id,
(SELECT COUNT(*) FROM TimeRank WHERE time <= (SELECT time FROM TimeRank WHERE user-id = 123)) AS position,
time,
name
FROM TimeRank
WHERE user-id = 123
You can generate a position column with a variable
set #pos=0;
select pos,user_id
from (select #pos:=#pos+1 pos,user_id from TimeRank order by time) s
where user_id=68;
If indexing is a concern, you can add a column to your table and update it with
set #pos=0;
update TimeRank set position=(#pos:=#pos+1) order by time;
For my project, i have a requirement where i have to display all the records in descending order except the first record. I am kind of messed up. Anyways, i have tried the following:
SELECT * FROM ins_nr nl WHERE nl.nl_status = '2' ORDER BY nl.nl_id DESC
Here, i have a table called ins_nr which will display all the records with status 2 and the id which is the primary key(unique). It is displaying in desc order perfectly.
I dont want the first record from the top alone. What should i do? How to modify the above query..?
Use OFFSET. Then you can skip 1 records and select the remaining ones until the end.
Example:
SELECT * FROM ins_nr nl WHERE nl.nl_status = '2'
ORDER BY nl.nl_id DESC LIMIT 99999999999 OFFSET 1;
OR ( You could also use a shorter syntax to achieve the same result: )
$sql = "SELECT * FROM table_name LIMIT 1, 999999999";
You can generate dynamic rownum and filter on it to omit the first row, e.g.:
SELECT *
FROM (
SELECT nl.*, #r := #r + 1 AS `rn`
FROM ins_nr nl, (SELECT #r := 0)
WHERE nl.nl_status = '2'
ORDER BY nl.nl_id DESC
) a
WHERE a.rn > 1;
Another way is to get the max id from subquery and put it in a where clausole
You are looking for the offset clause. This looks like:
SELECT *
FROM ins_nr nl
WHERE nl.nl_status = '2'
ORDER BY nl.nl_id DESC
LIMIT 999999999 OFFET 1;
Unfortunately, LIMIT is required. For this situation, it is traditional to just put in a very large number.
Also, if nl_status is numeric, then use nl.nl_status = 2. Don't compare strings to numbers.
Basically, I'm using this query to group a bunch of users based on the sum of numbers associated with them. I need to some how assign an index to each result. I am blanking on how to do this. I'm thinking I need to alias something with AS but not sure quite how. Any ideas?
This is the current query where I switch out the page and per dynamically:
SELECT COUNT(*) as count, user_id, SUM(earnings) as sum FROM ci_league_result
GROUP BY user_id ORDER BY sum desc LIMIT ".$page.', '.$per;
I'm lookin for it to work something like this:
SELECT COUNT(*) as count, user_id, SUM(earnings) as sum, *NEW-RESULTS-OVERALL-INDEX* AS newindex FROM ci_league_result
GROUP BY user_id ORDER BY sum desc LIMIT ".$page.', '.$per;
notice the AS newindex in the second query.
Thanks for your advice!
Try this:
SELECT user_id, count, sum, #row := #row + 1 AS newindex FROM
(SELECT
COUNT(*) as count,
user_id,
SUM(earnings) as sum
FROM ci_league_result
GROUP BY user_id ORDER BY sum desc LIMIT ".$page.', '.$per) r
CROSS JOIN (SELECT #row := 0) rr;
EDITED
You should be able to accomplish this with SQL variables
SELECT
COUNT(*) as count,
user_id,
SUM(earnings) as sum,
#rownum := #rownum+1 AS newindex
FROM ci_league_result,
(SELECT #rownum:=0) r
GROUP BY user_id
ORDER BY sum DESC
LIMIT ".$page.', '.$per;
This type of question is asked every now and then. The queries provided works, but it affects performance.
I have tried the JOIN method:
SELECT *
FROM nbk_tabl
INNER JOIN (
SELECT ITEM_NO, MAX(REF_DATE) as LDATE
FROM nbk_tabl
GROUP BY ITEM_NO) nbk2
ON nbk_tabl.REF_DATE = nbk2.LDATE
AND nbk_tabl.ITEM_NO = nbk2.ITEM_NO
And the tuple one (way slower):
SELECT *
FROM nbk_tabl
WHERE REF_DATE IN (
SELECT MAX(REF_DATE)
FROM nbk_tabl
GROUP BY ITEM_NO
)
Is there any other performance friendly way of doing this?
EDIT: To be clear, I'm applying this to a table with thousands of rows.
Yes, there is a faster way.
select *
from nbk_table
order by ref_date desc
limit <n>
Where is the number of rows that you want to return.
Hold on. I see you are trying to do this for a particular item. You might try this:
select *
from nbk_table n
where ref_date = (select max(ref_date) from nbk_table n2 where n.item_no = n2.item_no)
It might optimize better than the "in" version.
Also in MySQL you can use user variables (Suppose nbk_tabl.Item_no<>0):
select *
from (
select nbk_tabl.*,
#i := if(#ITEM_NO = ITEM_NO, #i + 1, 1) as row_num,
#ITEM_NO := ITEM_NO as t_itemNo
from nbk_tabl,(select #i := 0, #ITEM_NO := 0) t
order by Item_no, REF_DATE DESC
) as x where x.row_num = 1;