Find rows based on row number - mysql

When i want to get row number 5, i do this:
SELECT *
FROM table
ORDER BY id DESC
LIMIT 4, 1
What should my query be, if i want to get row number 5 and 7?

Here is one method:
(SELECT * FROM table ORDER BY id DESC LIMIT 4, 1)
UNION ALL
(SELECT * FROM table ORDER BY id DESC LIMIT 6, 1)
If you don't have an index on id (and even in some cases where you do), a more efficient method would use variables:
SELECT t.*
FROM (SELECT t.*, (#rn := #rn + 1) as rn
FROM table CROSS JOIN (SELECT #rn := 0) params
ORDER BY id DESC
) t
WHERE rn IN (5, 7);
This only sorts the data once.

Related

SQL query to count rows grouped by an ID, but limit count on each group

So I have a bit of an unusual request. I'm working with a table with billions of rows.
The table has a column 'id' which is not unique, and has a column 'data'
What I want to do is run a count on the number of rows grouped by the 'id', but limit the counting to only 150 entries. I only need to know if there are 150 rows by any given id.
This is in an effort to optimize the query and performance.
It doesn't have to be a count. I only need to know if a given id has 150 entries, without have MySQL continue counting entries during the query. If that makes sense.
I know how to count, and I know how to group, and I know how to do both, but the count will come back with a number in the millions which is wasted processing time and the query needs to run on hundred of thousands of ids.
You can't really optimize performance for this -- I don't think.
select id, (count(*) >= 150)
from t
group by id;
If you happen to have a separate table with one row per id and an index on t(id), then this might be faster:
select ids.id,
((select count(*)
from t
where t.id = ids.id
) >= 150
)
from ids;
Unfortunately, MySQL does not support double nesting for correlated subqueries, so this is not possible:
select ids.id,
((select count(*)
from (select 1
from t
where t.id = ids.id
limit 150
) t
) >= 150
)
from ids;
If so, this might be faster.
EDIT:
If you have an index on id and only want ids that have 150 or more, then variables might be faster:
select id,
(#rn := if(#id = id, #rn + 1,
if(#id := id, 1, 1)
)
) as rn
from (select id
from t
order by id
) t cross join
(select #id := 0, #rn := 0) params
having rn = 150;
The thinking here is that using the index to order the table, materializing, and scanning again is probably faster than group by. I don't think row_number() would have the same performance characteristics.
EDIT II:
A slight variation on the above can be used to get all ids with a flag:
select id, (max(id) = 150)
from (select id,
(#rn := if(#id = id, #rn + 1,
if(#id := id, 1, 1)
)
) as rn
from (select id
from t
order by id
) t cross join
(select #id := 0, #rn := 0) params
having rn in (1, 150)
) t
group by id;
EDIT III:
Ahh! If you have a separate table of ids, then this might be the best approach:
select ids.id,
(select id
from t
where t.id = ids.id
limit 1 offset 149
) is not null
from ids;
This will fetch the 150th row from the index. If it not there, then no row is returned.
I don't think that this is possible. You will have to scan the entire table to know which ids have at least 150 entries.
So:
select id
from mytable
group by id
having count(*) >= 150
With an index on id, this should be as efficient as it can be.

MySQL: Limiting result for WHERE IN list

Let's say there are millions of records in my_table.
Here is my query to extract rows with a specific name from list:
SELECT * FROM my_table WHERE Name IN ('name1','name2','name3','name4')
How do I limit the returned result per name1, name2, etc?
The following query would limit the whole result (to 100).
SELECT * FROM my_table WHERE Name IN ('name1','name2','name3','name4') LIMIT 100
I need to limit to 100 for each name.
This is a bit of a pain in MySQL, but the best method is probably variables:
select t.*
from (select t.*,
(#rn := if(#n = name, #rn + 1,
if(#n := name, 1, 1)
)
) as rn
from my_table t cross join
(select #n := '', #rn := 0) params
order by name
) t
where rn <= 100;
If you want to limit this to a subset of the names, then add the where clause to the subquery.
Note: If you want to pick certain rows -- such as the oldest or newest or biggest or tallest -- just add a second key to the order by in the subquery.
Try
SELECT * FROM my_table WHERE Name IN ('name1','name2','name3','name4') FETCH FIRST 100 ROWS ONLY

MYSQL SELECT BY GROUP

I have a table in which I need to query 9 returns, each of which need to be of a random order each time, they cannot duplicate except for one value of TYPE X. The problem is that if using GROUP BY, it only returns 8 results, but I require 9, with 2 results having the same TYPE value.
The draft query I have been using so far has been:
"SELECT * FROM (SELECT * FROM questions ORDER BY RAND() ) R GROUP BY type;
// TABLE REMOVED, FORMATTING NOT WORKING, IMAGE INSTEAD
If you want to fetch random rows from groups in MySQL, I think the safest way is to use variables. This is the ordering:
select q.*
from (select q.*,
(#rn := if(#q = questionid, #rn + 1,
if(#q := questionid, 1, 1)
)
) as rn
from questions q cross join
(select #q := 0, #rn := 0) params
order by questionid, rand()
) q
Then, if you know you have 8 questions but you want 2 from the second:
where rn = 1 or (questionid = 2 and rn = 2)
Or, if you want 9 random questions as non-overlapping as possible:
order by rn
limit 9

MySQL Where IN, limit output

So I have a query like
SELECT * FROM table WHERE id IN (1,2,3) LIMIT 10
Is it possible to limit the number of output for each of element in array like
SELECT * FROM table WHERE id 1 LIMIT 10
SELECT * FROM table WHERE id 2 LIMIT 10
SELECT * FROM table WHERE id 3 LIMIT 10
Yes, you can do it using variables:
SELECT *
FROM (
SELECT *,
#seq := IF(id = #id, #seq + 1,
IF(#id := id, 1, 1)) AS seq
FROM table
CROSS JOIN (SELECT #seq := 0, #id := 0) AS vars
WHERE id IN (1,2,3)
ORDER BY id) AS t
WHERE t.seq <= 10
If you're scared of MySQL variables (I am), you could also use a UNION query:
(SELECT * FROM table WHERE id = 1 LIMIT 10)
UNION ALL
(SELECT * FROM table WHERE id = 2 LIMIT 10)
UNION ALL
(SELECT * FROM table WHERE id = 3 LIMIT 10)

How to select certain numbers of groups in MySQL?

I have the table with data:
And for this table I need to create pegination by productId column. I know about LIMIT N,M, but it works with rows and not with groups. For examle for my table with pegination = 2 I expect to retrieve all 9 records with productId = 1 and 2 (the number of groups is 2).
So how to create pegination by numbers of groups ?
I will be very thankfull for answers with example.
One way to do pagination by groups is to assign a product sequence to the query. Using variables, this requires a subquery:
select t.*
from (select t.*,
(#rn := if(#p = productid, #rn + 1,
if(#rn := productid, 1, 1)
)
) as rn
from table t cross join
(select #rn := 0, #p := -1) vars
order by t.productid
) t
where rn between X and Y;
With an index on t(productid), you can also do this with a subquery. The condition can then go in a having clause:
select t.*,
(select count(distinct productid)
from t t2
where t2.productid <= t.productid)
) as pno
from t
having pno between X and Y;
Try this:
select * from
(select * from <your table> where <your condition> group by <with your group>)
LIMIT number;