Select only 1 row after ordering - mysql

Is it possible to get only 1 row after ordering? I don't want to load DB so much, so I want to get only 1 row (it may be from the middle), here is an example:
ID
User
Points
1
user1
10
2
user2
60
3
user3
45
I want to get the fe. the second user after ORDER BY `points` DESC, so the table will look like
ID
User
Points
2
user2
60
3
user3
45
1
user1
10
The second user is user3, and I want to return just that one row. It is possible? Or do I need to get all and just LIMIT it?

If you are using MySql 8+ you can use rank, for example:
with r as (
select * , Rank() over(order by points desc) rnk
from t
)
select id, user, points
from r
where rnk = 2;

If you want to strictly get only one row, corresponding to the second highest points, then my guess is that on ties you want the lowest id. In that case you can use the LIMIT function accordingly.
SELECT *
FROM tab
ORDER BY points DESC,
ID
LIMIT 1,1
The so written LIMIT clause will allow you to start limiting from the second row, then takes the first row available (which is the second one).
Check the demo here.

Related

MySQL select where not in another returned data from sql statement

I have this problem where I want to first select 8 elements from a mysql database ordering by id DESC.
Then I want to select another group of results (8 items), this time order by date DESC but the results here I want to ensure that they are not already on the fisrt query the one for ordering by id.
The data is in the same table just with different columns like id,name,date,.
So far I have tried writing different queries to get the data but the data contains some similar items of which that is what I don't want.
Here are the queries I have written;
this returns 8 items sorted by id DESC
SELECT name FROM person order by id DESC LIMIT 8;
this returns 8 items also but sorted by date DESC
SELECT name FROM person order by date DESC LIMIT 8;
the returned data contain duplicate items!
You could use a nested query, first select the first 8 id's, then select the first 8 records ordered by date, excluding those id's:
SELECT name FROM person
WHERE id NOT IN
(SELECT id FROM person order by id DESC LIMIT 8) AS exc
ORDER BY date DESC LIMIT 8
The first query should return the primary key for the table. If name is the key then so be it, but probably that id field is the better choice.
Then we can write the query like this:
SELECT p.name
FROM Person p
WHERE NOT EXISTS (
SELECT 1
FROM (SELECT id FROM Person ORDER BY id DESC LIMIT 8) p0
WHERE p0.id = p.id
)
ORDER BY p.date DESC
LIMIT 8;
We could also use an exclusion join which is usually slower, but in this case reduces one level of nesting so it might do better:
SELECT p.name
FROM Person p
LEFT JOIN (
SELECT id
FROM Person
ORDER BY id DESC
LIMIT 8
) p0 ON p0.id = p.id
WHERE p0.id is null
ORDER BY p.date DESC
LIMIT 8;
One other thing to keep in mind is MySQL is strict about what kinds of subquery can use the LIMIT keyword. Specifically, you need it to be a derived table. I know the exclusion join option should qualify, but I'm less sure of the NOT EXISTS() option.
Why not generate both resultsets with a single query? We can combine window functions, order by, and limit to generate a resultset containing the top 8 rows per id and the top 8 rows per date, while avoiding duplicates:
select *
from (
select p.*,
row_number() over(order by id desc) rn_id,
row_number() over(order by date desc) rn_dt
from person p
) p
order by case when rn_id <= 8 then rn_id else 9 end, rn_dt
limit 16
In the subquery, the window functions enumerate records by descending id and date. The outer query performs a conditional sort that puts the top 8 id first, and orders the rest of the records by descending date. All that is left to do is retain the top 16 results from the query. You don't need to worry about duplicates since the table is scanned only once.
Here is a small test case:
id
date
1
2022-11-11
2
2022-11-09
3
2022-11-05
4
2022-11-06
5
2022-11-07
6
2022-11-08
7
2022-11-10
For this sample data, and given a target of 3 + 3 records (instead of 8 + 8 in our code), the query returns:
id
date
rn_id
rn_dt
7
2022-11-10
1
2
6
2022-11-08
2
4
5
2022-11-07
3
5
1
2022-11-11
7
1
2
2022-11-09
6
3
4
2022-11-06
4
6
Typically, id 7, which has both the greatest id the second latest date, shows up in the first part of the resultset (the top 3 rows are sorted by descending id), but is not repeated in the second part.
Demo on DB fiddle

How can I get the second higher number in a query

Example, Lets suppose I have a table like next one:
id | start | userId
1 3 1
2 2 2
3 5 1
Now, I want to get all the row information for the second higher value of the column start related to some userId. So, if userId equals to 1 I'm expecting to get the next result:
(id, start, userId) = (1,3,1)
I have tried this query:
SELECT id, max(start) FROM table_user WHERE userId = 1;
But this gives me the higher number.
You can do this easy using ordering by column start and the features of LIMIT, like this:
SELECT
*
FROM
table_user
WHERE
userId = 1
ORDER BY
`start` DESC LIMIT 1, 1
You can check this online also: DB-Fiddle

find first row count and second row count using mysql

I have a table in mySql. I need to find how much entry in table which have entered only one time and another records which are enter for second time. please see the screenshot. count is based on shg_id.
if I correctly understand, you need this:
select entered, count(*) from (
select shg_id, count(*) as entered
FROM mytable
group by shg_id
having count(*) between 1 and 2
)t
group by entered
The following should do if it has an id attribute:
SELECT * FROM Table
HAVING COUNT(shg_id) = 1 -- Record equal to 1
Or
SELECT * FROM Table
HAVING COUNT(shg_id) = 2 -- Record equal to 2
Updated - This works well on my side:
SELECT COUNT(shg_id) AS Total
FROM Table
WHERE shg_id= 4
GROUP BY shg_idHAVING COUNT(shg_id) = 1
Another one - Slightly taken from OTARIKI:
SELECT shg_id, COUNT(*) AS Total FROM Table
GROUP BY shg_id
HAVING COUNT(shg_id) BETWEEN 1 and 2

Mysql Ranking Query on 2 columns

Table
id user_id rank_solo lp
1 1 15 45
2 2 7 79
3 3 17 15
How can I sort out a ranking query that sorts on rank_solo ( This ranges from 0 to 28) and if rank_solo = rank_solo , uses lp ( 0-100) to further determine ranking?
(If lp = lp, add a ranking for no tie rankings)
The query should give me the ranking from a certain random user_id. How is this performance wise on 5m+ rows?
So
User_id 1 would have ranking 2
User_id 2 would have ranking 3
User_id 3 would have ranking 1
You can get the ranking using variablesL
select t.*, (#rn := #rn + 1) as ranking
from t cross join
(select #rn := 0) params
order by rank_solo desc, lp;
You can use ORDER BY to sort your query:
SELECT *
FROM `Table`
ORDER BY rank_solo, lp
I'm not sure I quite understand what you're saying. With that many rows, create a query on the fields you're using to do your selects. For example, in MySQL client use:
create index RANKINGS on mytablename(rank_solo,lp,user_id);
Depending on what you use in your query to select the data, you may change the index or add another index with a different field combination. This has improved performance on my tables by a factor of 10 or more.
As for the query, if you're selecting a specific user then could you not just use:
select rank_solo from table where user_id={user id}
If you want the highest ranking individual, you could:
select * from yourtable order by rank_solo,lp limit 1
Remove the limit 1 to list them all.
If I've misunderstood, please comment.
An alternative would be to use a 2nd table.
table2 would have the following fields:
rank (auto_increment)
user_id
rank_solo
lp
With the rank field as auto increment, as it's populated, it will automatically populate with values beginning with "1".
Once the 2nd table is ready, just do this when you want to update the rankings:
delete from table2;
insert into table2 select user_id,rank_solo,lp from table1 order by rank_solo,lp;
It may not be "elegant" but it gets the job done. Plus, if you create an index on both tables, this query would be very quick since the fields are numeric.

Advanced mysql query which sort down specific records on result set irrespective of its default sorting?

I have a query which actually have a sorting using order by clause. i have a table like following...
user_id user_name user_age user_state user_points
1 Rakul 30 CA 56
2 Naydee 29 NY 144
3 Jeet 40 NJ 43
.....
i have following query...
select * from users where user_state = 'NY' order by user_points desc limit 50;
This gives me the list of 50 people with most points. I wanted to give least preference to few people who's id's were known. Incase if i do not have enough 50 records then those id's should come in the last in the list. I do not want the users 2 and 3 to come on top of the list even though they have higher points... those people should come on the last of the list from the query. Is there any way to push specific records to last on result set irrespective of query sorting ?
If you want to move specific records (like user_id = 2 and 3) down to the list; Then you can run below Query:
mysql> select *,IF(user_id=2 or user_id=3,0,1) as list_order from users where user_state = 'NY' order by list_order desc, user_points desc limit 50;
select * from (
select *
from users
where user_state = 'NY'
-- this order by ensures that 2 and 3 are included
order by case when user_id in (2,3) then 1 else 2 end, user_points desc
limit 50
) as top48plus2n3
-- this order by ensures that 2 and 3 are last
order by case when user_id in (2,3) then 2 else 1 end, user_points desc
Edit: changed id by user_id and corrected outside order by (sorry about that)
On the inner select:
By using this case calculation, what you do is ensuring that records with ids equal to 2 and 3 are "important" (firstly ordered in the order by). Those receive 1 while the others receive 2 as order value, only after that points are relevant.
On the outer select:
Records with ids 2 and 3 recieve 2 as order value, while the rest recieve 1. So they go last irrespective of its "default"
Here you have a reduced fiddle http://sqlfiddle.com/#!9/377c1/1