Wondering why I must assign to session variable in mysql? - mysql

Why do I have to assign to a session variable for it to have the right number in a query like this:
SELECT #row_number := #row_number + 1, name FROM cities;
Instead of something like:
SELECT #row_number, name FROM cities;
In the second form it returns what I'm guessing is the last row number. Maybe even the value of a COUNT(*). It's almost as if the value is somehow closed over. What is going on in these two queries?

you have #row_number variable. Everytime the below sql hits the record it shows the result and increments by one.
SELECT #row_number := #row_number + 1, name FROM cities;
if you are using mysql 8.0+, you can use row_number window function to achieve same result
select row_number() over (order by <pk>) rn, name from cities;
If we turn back to SELECT #row_number, name FROM cities;, you are not icrementing #row_number which in turns shows always same value which is assigned value for #row_number
PS: please also note that you are not using order by clause on your query which may lead to inconsistent row numbering.

You need to assign to it in order to add 1 to the value on each row. If you don't do this, you get the same value on every row, which isn't a row number. It will be whatever was left from the last time you assigned the variable, which might be the total number of rows from a previous query that was correctly incrementing.
If you're using MySQL 8.x you can replace this use of session variables with the ROW_NUMBER() function.

Instead of session variable, for MySQL version 8+ you can use ROW_NUMBER() and for below MySQL 8 you can do this
SELECT #row_number := #row_number + 1, name
FROM cities,
(SELECT #row_number:= 0) AS x;

Related

how to set unique name of each distinct values of a column in sql

I have a table testTble with column testVal which is containing duplicate value. I can find the each unique value of the column using DISTINCT(testVal). But I want to set a specific name of each unique value of the column.
Like:
I run the query in my db and found those distinct value.
SELECT DISTINCT(testVal) AS web FROM `testTble`
Output:
web
169.254.15.169
10.0.0.91
192.168.80.47
10
Now I want to set a unique name of those values like this:
169.254.15.169 as web21
10.0.0.91 as web22
So how can I set a name like this?
In MySQL 8+, you can use row_number():
select test_val, row_number() over (order by test_val)
from t
group by test_val;
In earlier versions, you can use variables:
select test_val, (#rn := #rn + 1) as seqnum
from t cross join
(select #rn := 0) params
group by test_val;
In both cases, the "name" is a numeric value, but that seems consistent with what you want to do.

How to easily get row number when using LIMIT in MySQL?

Suppose I have a database table with quite a few rows which I want to display to an user. It would make sense to LIMIT the output, and make pages of rows. In MySQL I would do this:
SELECT * FROM myTable ORDER BY myValue LIMIT 120,10
which will show 10 rows starting from row 120. So MySQL must use, internally, some kind of order, and has numbered the rows accordingly. I would like to display the row number with each row. How do I get access to these numbers, using only MySQL? To be clear, I am looking for something like this:
SELECT *,<LIMIT_ROWNO> FROM myTable ORDER BY myValue LIMIT 120,10
I looked online and in the manual, but I cannot find it. I would prefer something simple, without using variables, or functions. Isn't there a predefined expression for this?
I can solve this problem easily in PHP, but it would be more logical to get the row numbers from MySQL.
You can't do it without using variables, e.g.:
SELECT m.*, #rownum := #rownum + 1 as `num`
FROM myTable m, (SELECT #rownum := 120) a
ORDER BY myValue LIMIT 120,10;
set #rownum=120;
SELECT *,#rownum:=#rownum+1 as rn FROM myTable ORDER BY myValue LIMIT 120,10;
as of final of 2021, why not:
SELECT
t1.*,
COUNT(t1.*) OVER (PARTITION BY RowCounter) as TotalRecords
FROM (
SELECT a, b, c, 1 as RowCounter
FROM MyTable
) t1
LIMIT 120,10
using a subquery with a column marking every row with the same value, will give us the possibility to count all of the same values of the the resulted column with PARTITION BY window function's group

Temporary Variable with aggregate field and group by doesn't work in mysql

I am trying to get cumulative value of a column using temporary variable.
SELECT sum(price), #temp := #temp + sum(price) AS cumulative_price FROM `table`, (SELECT #temp := 0) B GROUP BY item
It work when there is no group by and aggregate field. However, when there is group by field, value of cumulative_price is same as sum(price), which is not a expected.
What could be the reason of this inconsistency?
It should not work. According to Doc
In a SELECT statement, each select expression is evaluated only when
sent to the client. This means that in a HAVING, GROUP BY, or ORDER BY
clause, referring to a variable that is assigned a value in the select
expression list does not work as expected:
mysql> SELECT (#aa:=id) AS a, (#aa+3) AS b FROM tbl_name HAVING b=5;
The reference to b in the HAVING clause refers to an alias for an
expression in the select list that uses #aa. This does not work as
expected: #aa contains the value of id from the previous selected row,
not from the current row.
So when you define a variable it will work for current row not set of rows

mysql show position in resultset

So I have a list of say 10 rows. Each with an key and value.
Is there a way in mysql where I can, get a rows position in a given resultset:
SELECT 'resultSetPosition' FROM table WHERE rowKey=x ORDER BY rowValue
id value
1 a
2 b
3 c
o/p required
if id 3 the i need to get position of that row is 3
EDIT: I only want to get one row out in my resultset, but the 'position' should relate to its position for example if you sort by id.
(Obviously this whole thing is easy if I just pull the entire resultset and search the array in my programming, but I wanted to see if it could be done in mysql alone.)
You can try this:-
SELECT *, #rownum := #rownum + 1 AS ROW_NUMBER
FROM TABLE
JOIN (SELECT #rownum := 0) R;
This might help you.
Try this:
SET #rank=0;
SELECT #rank:=#rank+1 AS resultSetPosition
FROM tableName
WHERE rowKey=x
ORDER BY rowValue
Also take a look at this link.
Try with this hope this will clear your problem
select count(*) as pos from table_name where id<=current_id

Numbering rows in groups with MySQL: how does it work?

There are several good posts on how to number rows within groups with MySQL, but how does the actually code work? I'm unclear on what MySQL evaluates first in the code below.
For instance, placing #yearqt := yearqt as bloc before the IF() call produces different results, and I'm unclear on the role of the s1 subquery in initializing the # variables: when are they updated as MySQL runs through the data rows? Is the order by statement run before the select?
The code below selects three random records per yearqt group. There may be other ways to do this, but the question pertains to how the code works, not how I could do this differently or whether I can do this more efficiently. Thank you.
select * from (
select customer_id , yearqt , a ,
IF(#yearqt = yearqt , #rownum := #rownum + 1 , #rownum := 1) as rownum ,
#yearqt := yearqt as bloc
from
( select customer_id , yearqt , rand(123) as a from tbl
order by rand(123)
) a join ( select #rownum := 0 , #yearqt := '' ) s1
order by yearqt
) s2
where rownum <= 3
order by bloc
This question is related to how the engine retrieves SQL SELECT query results. The order is roughly the following:
Calculate explain plan
Calculate sets and join them using plan's directives (FROM / JOIN phase)
Apply WHERE clause
Apply GROUP BY/HAVING clause
Apply ORDER BY clause
Projection phase: every row returned is ordered and can now be 'displayed'.
So, in respect to the variables, you now understand why there's subquery to initialize them. This subquery is evaluated only once, and at the beginning of the process.
After that, the project phase seems to treat each selected attribute in the order you decided which is the reason why puting #yearqt := yearqt as bloc up one attribute would changes the outcome of the next/previous IF statement. Since each row will be projected once, it means any work you're doing on the variables will be done as many times as the number of rows in the final resulset.
The purpose of this
join ( select #rownum := 0 , #yearqt := '' ) s1
is to initialize the user-defined variables at the beginning of statement execution. Because this is a rowsource for the outer query (MySQL calls it a derived table) this will be executed BEFORE the outer query runs. We aren't really interested in what this query returns, except that it returns a single row, because of the JOIN operation.
So this inline view s1 could be omitted from the query and be replaced by a couple of SET statements that are executed immediately before the query:
SET #rownum := 0;
SET #yearqt := 0;
But then we'd have three separate statements to run, and we'd get different output from the query if these weren't run, if those variables were set to some other value. By including this in the query itself, it's a single statement, and we remove the dependency on separate SET statements.
This is the query that's really doing the work, whittled down to just the two expressions that matter in this case
SELEECT IF(#yearqt = t.yearqt , #rownum := #rownum + 1 , #rownum := 1) as rownum
, #yearqt := t.yearqt as bloc
FROM ( ... ) t
ORDER BY t.yearqt
Some key points that make this "work"
MySQL processes the expressions in the SELECT list in the order that they appear in the SELECT list.
MySQL processes the rows in the order specified in the ORDER BY.
The references to user-defined variables are evaluated for each row, not once at the beginning of the statement.
Note that the MySQL Reference Manual points out that this behavior is not guaranteed. (So, it may change in a future release.)
So, the processing of that can be described as
for the first expression:
compare the value of the yearqt column from the current row with current value of #yearqt user-defined variable
set the value of #rownum user-defined variable
return the result of the IF() expression in the resultset
for the second expression:
set the value of the #yearqt user-defined variable to the value of the yearqt column from the current row
return the value of the yearqt column in the resultset
The net effect is that for each row processed, we're comparing the value in the yearqt column to the value from the "previously" processed row, and we're saving the current value to compare to the next row.