limiting number of elements in a result set - mysql

I'm trying to limit the number of elements in my result set by setting a limit to my sql logic. I have two seperate functions to achieve what I want. The first one has a limit I've set manually e.g 0, X. The second function has two extra arguments that is min and max and these are set as the limit. But when the min and max are e.g 7, 14 it gives me more elements then 7. There are no duplications in the result set since I have unique id's on each element and they check out. Also the integers passed to the sql function have the correct intervall.
What am I doing wrong?
"SELECT table1.*, table2.user_id FROM table1 LEFT JOIN table2 ON table1.col1 = table2.col2
WHERE table1.col1 = :param1 AND table1.col2 = 1 AND table1.col3 = 0 ORDER BY table1.col4 DESC LIMIT $min, $max";

The syntax is SELECT Syntax is not
limit min, max
but
limit offset, row_count
so, limit 7, 14 says retrieve 14 rows at offset 7.

Related

Why offset. limit returns all rows?

The following SQL query return all rowsa instead from 12 to 18.
SELECT `reviews`.*,
`users`.`username` as `user_username`,
`users`.`slug` as `user_slug`
FROM `reviews`
JOIN `users` ON `users`.`id` = `reviews`.`user_id`
WHERE `reviews`.`product_id` = 2
ORDER BY `reviews`.`created_at` DESC LIMIT 12, 18
As result I have to get 6 rows instead all.
Using the OFF SET in the LIMIT query
The OFF SET value is also most often used together with the LIMIT keyword. The OFF SET value allows us to specify which row to start from retrieving data
Let’s suppose that we want to get a limited number of members starting from the middle of the rows, we can use the LIMIT keyword together with the offset value to achieve that. The script shown below gets data starting the second row and limits the results to 2.
SELECT * FROM `members` LIMIT 1, 2;
Refrence and MySQL docs
In your case to return only 6 rows starting from 12(thirteen row) it should be ORDER BY reviews.created_at DESC LIMIT 12, 6

Select last two values from two IDs

I would like to select two specific values, the first value is the last inserted row where the ID_SENSOR is 1, and the second value is the last inserted row where the ID_SENSOR is 2.
My Database table:
My Query:
SELECT DATA FROM (SELECT * FROM registovalores WHERE ID_SENSOR = '1' OR ID_SENSOR = '2' ORDER BY ID_SENSOR DESC LIMIT 2) as r ORDER BY TIMESTAMP
My Query is printing the last value just from the ID_SENSOR 1, which it means that I'm only getting the last inserted values, and not the last inserted value from both IDS.
I would like to print my values like this:
ID_SENSOR 1 = 90
ID SENSOR 2 = 800
What do I need to change on my Query?
Thank you.
One method uses a correlated subquery:
SELECT rv.*
FROM registovalores rv
WHERE rv.ID_SENSOR IN (1, 2) AND
rv.TIMESTAMP = (SELECT MAX(rv2.TIMESTAMP)
FROM registovalores rv2
WHERE rv.ID_SENSOR = rv2.ID_SENSOR
);
You have to have two separate queries, one per sensor.
select id_sensor, data
from the_table
where id_sensor = 'sensor_1'
order by timestamp desc -- the latest value is the first to come
limit 1; -- only pick the top (latest) row.
If you want to query for more than one value in a single database roundtrip, consider using union all between several such queries.
Please note that such a query may return one row or zero rows, since data for a particular sensor may not be available yet.

`UPDATE ... SET ... LIMIT` in MySQL

I would like to update a specific range of rows, say starting from 30 and ending at 50. How may I achieve that.
I have tried with:
UPDATE tab
SET col = 'somevalue'
LIMIT 30, 50
but this doesn't work. Is there any way that I can update these rows?
The error that I get is:
Check the manual ... for the right syntax to use near ' 50'
Your statement is not valid MySQL syntax and it doesn't make sense. The problem with the syntax is that offset is not supported for update statements (see here).
The problem with the logic is that you have no order by clause. MySQL doesn't guarantee the order of tables when processing them. So the "first" twenty rows and the "next" twenty" rows make no difference.
Why doesn't this do what you want?
UPDATE tab
SET col = 'somevalue'
LIMIT 20;
If you have a specific column that specifies the ordering, you can use where:
UPDATE tab
SET col = 'somevalue'
wHERE ID >= 30 and ID < 50;
I think with update you can not use limit as it is in select(offset support), you will have to try like this:-
UPDATE tab
SET col = 'somevalue'
where id between 30 and 50;
LIMIT can be used with UPDATE but with the row count only, such as
UPDATE tab
SET col = 'somevalue'
where id > 30
limit 20;
Similar question here.

Select from table if exists, else select from a totally different table

I have a query to fetch two values:
string query = #"SELECT price, weight
FROM map
WHERE width = #width AND height = #height LIMIT 1";
if (_connSource.State != ConnectionState.Open)
_connSource.Open();
MySqlCommand cmd = new MySqlCommand(query, _connSource);
cmd.Parameters.AddWithValue("width", width);
cmd.Parameters.AddWithValue("height", height);
r = cmd.ExecuteReader();
if (!r.Read())
{
r.Close();
query = #"SELECT retail_price, 0
FROM globe
WHERE PK_Id = #PK_Id LIMIT 1"
cmd = new MySqlCommand(query, _connSource);
cmd.Parameters.AddWithValue("PK_Id ", 1);
r = cmd.ExecuteReader();
}
What I need is to get price and weight according to a condition, but if it is not present in the table, then I need to get another two fields retail_price, and a constant 0 (doesnt matter what it is) from a totally new table with no constraints from the previous table. Can I get the two in a single query?
Note: Kindly give me optimized queries which doesn't force reading the same values more than once (this function gets executed thousands of times in one single operation, so speed is very critical - a reason why I'm trying to get it in one query). Thanks..
The syntax gets a little ugly, but I think you can do it:
(SELECT price, weight
FROM map
WHERE width = #width AND height = #height LIMIT 1)
UNION ALL
(SELECT retail_price as price, 0 as weight
FROM globe
WHERE PK_Id = #PK_Id LIMIT 1)
This returns 0 to 2 rows. If 2 rows are returned, pick the 1st one.
EDIT You can try avoiding the cost of the second query by using this monstrous construct. I am not sure if MySQL is going to handle it well, but it has a decent chance of avoiding the second query:
select
ifnull(price, (select retail_price from globe where PK_Id = #PK_Id LIMIT 1))
, ifnull(weight, 0)
from map
WHERE width = #width AND height = #height LIMIT 1
If there is no Resultrow to the map WHERE then
the DBNull will be replaced by globe values
SELECT
price = IFNULL(map.price,gl.price),
weight = IFNULL(map.weight,0)
FROM map
LEFT JOIN globe gl ON PK_Id = #PK_Id
WHERE width = #width AND height = #height
LIMIT 1
I did get a pretty hacky solution to this, accidentally. Here is how one can do it:
SELECT price, weight
FROM map
WHERE width = #width AND height = #height LIMIT 1
UNION ALL
SELECT retail_price, 0
FROM globe
WHERE PK_Id = #PK_Id LIMIT 1
The LIMIT 1 clause actually limits it from reading the second value if I get an answer in first select itself. Notice that I haven't added parentheses anywhere so that MySQL doesn't treat this as normal UNION ALL. Read more relevant info here and here
Apparently you can do this to make it more meaningful with this, but I do not think it performs any better:
SELECT price, weight
FROM map
WHERE width = #width AND height = #height LIMIT 1
UNION ALL
(SELECT retail_price, 0
FROM globe
WHERE PK_Id = #PK_Id LIMIT 1) LIMIT 1
Here since I have added brackets, it works like normal UNION ALL, that is I should get both records if possible, but the last LIMIT 1 clause at the end of the query limits the result to the first set.
Since your "limit" clause guarantees a maximum of one row from each query, you could use a left join, and deal with existence of the "map" record using "IF()":
select ifnull(m.price,g.price), ifnull(m.weight,g.weight) from
(SELECT retail_price as price, 0 as weight
FROM globe
WHERE PK_Id = #PK_Id LIMIT 1) g
LEFT JOIN
(SELECT price, weight
FROM map
WHERE width = #width AND height = #height LIMIT 1) m
ON 1=1
The above query assumes that price and weight in the map table cannot be null.

Select nth percentile from MySQL

I have a simple table of data, and I'd like to select the row that's at about the 40th percentile from the query.
I can do this right now by first querying to find the number of rows and then running another query that sorts and selects the nth row:
select count(*) as `total` from mydata;
which may return something like 93, 93*0.4 = 37
select * from mydata order by `field` asc limit 37,1;
Can I combine these two queries into a single query?
This will give you approximately the 40th percentile, it returns the row where 40% of rows are less than it. It sorts rows by how far they are from the 40th percentile, since no row may fall exactly on the 40th percentile.
SELECT m1.field, m1.otherfield, count(m2.field)
FROM mydata m1 INNER JOIN mydata m2 ON m2.field<m1.field
GROUP BY
m1.field,m1.otherfield
ORDER BY
ABS(0.4-(count(m2.field)/(select count(*) from mydata)))
LIMIT 1
As an exercise in futility (your current solition would probably be faster and prefered), if the table is MYISAM (or you can live with the approximation of InnoDB):
SET #row =0;
SELECT x.*
FROM information_schema.tables
JOIN (
SELECT #row := #row+1 as 'row',mydata.*
FROM mydata
ORDER BY field ASC
) x
ON x.row = round(information_schema.tables.table_rows * 0.4)
WHERE information_schema.tables.table_schema = database()
AND information_schema.tables.table_name = 'mydata';
There's also this solution, which uses a monster string made by GROUP_CONCAT. I had to up the max on the output like so to get it to work:
SET SESSION group_concat_max_len = 1000000;
MySql wizards out there: feel free to comment on the relative performance of the methods.