MySQL Loop to execute query that uses incrementing variable - mysql

I have a query that uses a variable in its where clause:
SET #id = 1;
SELECT
id,
value
FROM myTable
WHERE id = #id;
I would like to run the query for #id values 1 through 100, and then union (or somehow combine) all the loop results into one result set. Is this possible in MySQL, and if so, what is a good approach?

Why would use use a variable for this? Just use a simple where clause:
select id, value
from myTable
where id between 1 and 100;
If, instead, you really want the first 100 rows by id, then use order by and limit:
select id, value
from myTable
order by id
limit 100;

Just use a subquery to get the variable.
And put the where outside
SELECT *
FROM
(
SELECT
#rownumber := if(#rownumber, #rownumber + 1, #rownumber + 1) AS id,
value
FROM myTable
CROSS JOIN (select #rownumber := 0) r
) as T
WHERE id < 100;

Related

MySQL: user-variable definition within SQL statement to create counter column

Is it possible to create a counter in mysql/mariadb in one single SELECT-statement. I've tried the following but it returns only the value 1 in the first column:
SELECT #rownr := IF(ISNULL(#rownr),0,#rownr)+1 AS rowNumber, * FROM table_x LIMIT 0,10
If I run the statement more often in the same mysql-instance it starts counting from the last number. So the second time it starts at 2, the third time at 12. This means that the variable is created but seems to be only available for modification when it was instantiated before the SQL statement was issued.
It is possible, but a bit tricky. First, you need to declare the variable outside of the select clause (in a separate set assignment, or in a derived table). Also, it is safer to sort the rows in a subquery first, and then compute the variable.
I would recommend:
set #rn := 0;
select t.*, #rn := #rn + 1 rowNumber
from (select t.* from mytable t order by id limit 10) t
Note that I added an order by clause to the inner query, otherwise it is undefined in which sequence rows will be ordered (I assumed id).
Alternatively, you can declare the variable in a derived table:
select t.*, #rn := #rn + 1 rowNumber
from (select t.* from mytable t order by id limit 10) t
cross join (select #rn := 0) x
Finally: if you are running MySQL 8.0, just use row_number():
select t.*, row_number() over(order by id) rn
from mytable t
order by id
limit 10;
You don't have an order by, so the ordering is indeterminate. But you can initialize the parameter in the statement itself:
SELECT #rownr := (#rownr + 1) AS rowNumber, x.*
FROM table_x x.CROSS JOIN
(SELECT #rownr := 0) params
LIMIT 0, 10;
If you want a particular ordering, you should use an order by in a subquery.
Also note that starting in MySQL 8, variable assignments in SELECT are deprecated. You should be using window functions (row_number()) in more recent versions.

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

Weird result in MySQL query

I have this query:
SELECT COUNT(1), name, (#i := #i + 1) AS counter FROM mytbl, (SELECT #i := 0) tmp_tbl GROUP BY counter
For this query, the counter column increases its value with 2.
But if I remove COUNT(1), such as:
SELECT name, (#i := #i + 1) AS counter FROM mytbl, (SELECT #i := 0) tmp_tbl GROUP BY counter
counter column increases its value with 1.
Can anyone explain why this behavior?
Table would be:
create table mytbl (name VARCHAR(20));
With data:
INSERT INTO mytbl VALUES
('a1'),
('a2'),
('a3');
As mentioned in MySQL document, we should not assign a value to a user variable and read the value within the same statement. We might get the expected results, but this is not guaranteed. Changing the statement (for example, by adding a GROUP BY, HAVING, or ORDER BY clause) may cause MySQL to select an execution plan with a different order of evaluation.
In your query, counter field will be evaluated in SELECT statement and then be used in GROUP BY statement. Seem when we add an aggregation function to SELECT statement, the field that be used in GROUP BY statement will be evaluated 2 times.
I've create a demo, you could check it. In the demo, I've this query
SELECT Count(1),
name,
( #i := #i + 1 ) AS counter,
( #j := #j + 1 ) AS group_field
FROM (SELECT 'A' AS name
UNION
SELECT 'B' AS name
UNION
SELECT 'C' AS name) mytable,
(SELECT #i := 0) tmp_tbl,
(SELECT #j := 0) tmp_tbl1
GROUP BY group_field;
In the execution result, counter field only be increased by 1 and group_field be increased by 2.
To make the counter field only increasing by 1, you could try this
SELECT Count(1),
name,
counter
FROM (SELECT name,
( #i := #i + 1 ) AS counter
FROM mytbl,
(SELECT #i := 0) tmp_tbl) data
GROUP BY counter;

Select only unique records

I'm trying to select all records in a table, but hide duplicate rows. So if 2 rows are completely the same (except for the auto increment ID of course), only 1 should be shown.
I thought this had to be done with distinct, but it still give me duplicate rows.
SELECT DISTINCT *
FROM tbllulog
WHERE lulogluserial = $commandlu
ORDER BY `tbllulog`.`tbllulogid` DESC
I also tried this:
SELECT DISTINCT lulogtimestamp,
lulogmoveemployee,
lulogsource,
lulogaction,
lulogluoutput0status,
lulogluinput0status
FROM tbllulog
WHERE lulogluserial = $commandlu
ORDER BY `tbllulog`.`tbllulogid` DESC
But this also give me duplicates
Anyone can point me out what i'm missing?
thanks!
Use DISTINCT and GROUP BY and add the tbllulogid to the SELECT
SELECT DISTINCT tbllulogid,
lulogtimestamp,
lulogmoveemployee,
lulogsource,
lulogaction,
lulogluoutput0status,
lulogluinput0status
FROM tbllulog
WHERE lulogluserial = $commandlu
GROUP BY `tbllulog`.`tbllulogid`
ORDER BY `tbllulog`.`tbllulogid` DESC
Try like this
DECLARE #type varchar(50);
DECLARE #num int;
SET #type = '';
SET #num = 1;
SELECT * FROM
(
SELECT lulogtimestamp,
lulogmoveemployee,
lulogsource,
lulogaction,
lulogluoutput0status,
lulogluinput0status
#num := if(#type = lulogmoveemployee, #num + 1, 1) as row_number,
#type := lulogmoveemployee As Dummy
FROM tbllulog
WHERE lulogluserial = $commandlu
ORDER BY `tbllulog`.`tbllulogid` DESC
) T WHERE row_number = 1

Select the offset of a row respecting the order without fetching all previous rows

First I want to make clear this is not a simple LIMIT x,y question. I want to know if it is possible to do a query like the following peuso query.
SELECT *, OFFSET_OF_ROW()
FROM `table`
WHERE `some_column` = someValue
ORDER BY `some_other_column`;
the pseudo function OFFSET_OF_ROW() should give the number of rows which would come before the selected row (+1) if there was no condition `some_column = someValue`
This isn't particularly efficient, but it will do what you want:
select #rownum := 0;
select * from (
select #rownum := #rownum + 1, id, some_column, sortcol
from `table`
order by `sortcol`
) all_rows
where `some_column` = someValue;