There is a query in MySQL 5.7:
SELECT * FROM
(
SELECT (#rowNum:=#rowNum+1) AS rowNo,t.* FROM table_target t,(SELECT (#rowNum :=0)) AS b
WHERE p_d = '2020-11-08'
ORDER BY kills DESC
) t
WHERE t.uid= '8888'
Running this query, there is no exception but column B disappears and if using select b from in the outter query, it returns unknown column exception.
I have 2 questions:
Why the (SELECT (#rowNum :=0)) does not appear?
Is the (#rowNum:=#rowNum+1) equivelent to row_number() over () in Oracle? If so, how to understand it...
Thanks for your help in advance.
In addition, I just found if I put the (SELECT (#rowNum :=0) ) in the left:
...
SELECT (SELECT (#rowNum :=0) ) AS b, (#rowNum:=#rowNum+1) AS rowNo , t.* FROM table_target t
...
Then the row number column does not increase any more, why could this happen?
You have asked 3 questions here:
Question 1: Why the (SELECT (#rowNum :=0)) does not appear?
Answer: You have used (SELECT (#rowNum :=0)) as B as a table joining it but not calling it in column list after select. That's why it is not showing it in output. You have called it as (#rowNum:=#rowNum+1) which is showing the value after increment means starting from 1.
Question 2: Is the (#rowNum:=#rowNum+1) equivalent to row_number() over () in Oracle? If so, how to understand it
Answer: Yes, it is equivalent. MySql 8.0 and above also support this (known as window function). It works as:
At the time of initialization of the query (SELECT (#rowNum :=0)) variable #rowNum will be initialized with value 0.
When we are calling (#rowNum:=#rowNum+1) in select then it will add 1 in #rowNum and assign it to itself for every row returned by select query.
This is how it will print the row number.
Question 3: if I put the (SELECT (#rowNum :=0) ) in the left:
Answer: If you put the (SELECT (#rowNum :=0) ) as field list after select then it will initialize the value of #rownum to 0 in every row returned by select. This is why you will not get incremented value.
The column "disappears" because the value is NULL. MySQL does not guarantee the order of evaluation of expressions in the SELECT, the initialization might not work.
Second, you code does not do what you intend, even if that worked, because variables may not respect the ORDER BY. I think you intend:
select k.*
from (select (#rownum := #rownumn + 1) as rownum, k.*
from (select k.*
from kills k
where k.p_d = '2020-11-08'
order by kills desc
) k cross join
(select #rownum := 0) params
) k
where t.uid = '8888';
There are probably better ways to do what you want. But your question is specifically about variables and MySQL 5.7.
Related
I am trying to fetch first 50% of records from a MySQL Table User. I know we can use limit or top for finding them but the total number of records are not fixed so hard coding the actual number in the limit or top doesn't gives me first 50% of records. How can I achieve this?
If you are running MySQL 8.0, you can use window functions for this: ntile() does exactly what you ask for. Assuming that your ordering column is id:
select *
from (select t.*, ntile(2) over(order by id) nt from mytable) t
where nt = 1
In earlier versions, one option is a user variable and a join with an aggregate query:
select *
from (
select t.*, #rn := #rn + 1 rn
fom (select * from mytable order by id) t
cross join (select #rn := 0) x
cross join (select count(*) cnt from mytable) c
) t
where rn <= cnt / 2
Mysql directly not supports this. You can try with two queries or use subqueries
Something like this.
find the count of total records/2
that value has to be applied in the limit clause.
SET #count = (SELECT COUNT(*)/2 FROM table);
SET #sql = CONCAT('SELECT * FROM table LIMIT ', #count);
SELECT * FROM table name LIMIT (select COUNT(*)/2 from table name);
I am having trouble finding a solution. I need to make a column that rank from the most to the least value (column bedrag). If the values are the same then the value with the highest number ( column spelersnr ) 'wins'.
I hope you can help me out.
This is what I got so far.
SELECT s.spelersnr,
naam ,
(select max(bedrag) from boetes b where b.spelersnr = s.spelersnr) as mbedrag,
#curRank := #curRank + 1 AS POSITIE
FROM spelers s, (SELECT #curRank := 0) r
ORDER BY mbedrag ;
Given that you are using MySQL 8+, you may try using the ROW_NUMBER function here. Also, we can rewrite your query using a join, to eliminate the correlated subquery in the select clause:
SELECT s.spelersnr, s.naam,
ROW_NUMBER() OVER (ORDER BY b.mbedrag DESC, s.spelersnr DESC) rn
FROM spelers s
INNER JOIN
(
SELECT spelersnr, MAX(bedrag) AS mbedrag
FROM boetes
GROUP BY spelersnr
) b
ON s.spelersnr = b.spelersnr
ORDER BY
b.mbedrag;
I would like to calculate the MODE of a single column in SQL. This is done easily enough with:
SELECT v AS Mode
FROM Data
GROUP BY v HAVING COUNT(*) >= ALL (SELECT COUNT(*) FROM Data GROUP BY v);
However, I would like to do this without sorting, i.e. without using GROUP BY or any similar construct. Is there a quick and easy way to do this?
group by doesn't do sorting. It does partitioning. So instead of 1 aggregate result, you get 1 result per group in which all values that you group by are the same.
For MySQL the best I could come up with is this:
select distinct v from(
select v,
#cnt := (select count(*) from Data d1 where d1.v=d.v) as cnt_,
case when #cnt>=#max then #max:=#cnt end as max_
from Data d,
(select #max:=1, #cnt:=1) c) a
where cnt_ = #max
For SQL Server, Oracle or Postgres you can use a window function:
with a as (
v, select row_count() OVER(PARTITION BY v) rn
from Data
)
select v as Mode
FROM a
where rn = (select max(rn) from a)
I have two tables. one is local and other one is foreign. so what I want to do is to give row numbers after joining two tables using stored procedure.
First I want to get same number of column from two tables and after that I want combine as a one table and give row numbers.
below is my query.
set #row_number=0;
select (#row_number:=#row_number + 1) as number,
(
select a.*
from
(select ID,title,last_name,first_name
from local
)
a
union all
select b.*
from
(select ID,title ,last_name,first_name
from foreign
)
b
)
;
Could anyone please tell me what the wrong with it?
Use ROW_NUMBER window function in SQL SERVER
SELECT Row_number()
OVER(
ORDER BY (SELECT NULL))AS number,a.*
FROM (SELECT ID,
title,
last_name,
first_name
FROM local
UNION ALL
SELECT ID,
title,
last_name,
first_name
FROM FOREIGN) a
Note : replace (SELECT NULL) with the column you want order by row number generation. Now the row number generation is arbitrary
It seems that you are using MySQL, not SQL Server, and try to emulate row numbers, as shown eg in this duplicate question. This is trivial to do in SQL Server using the ROW_NUMBER function, as shown by #Prdp's answer.
MySQL though doesn't have the ranking, analytic or windowing functions found in other databases. Such functions can be emulated in a very limited fashion by using non-standard SQL tricks, as shown in the linked question.
Such tricks are very limited though. A typical use of ROW_NUMBER is to rank records inside a group, eg top 10 salesmen by region. It's not possible to do that with the #curRow := #curRow + 1 trick. There are performance implications as well. This trick will only work if the rows are processed sequentially.
In the question's case, a MySQL query would probably look like this:
SELECT l.ID,
l.title,
l.last_name,
l.first_name,
#curRow := #curRow + 1 AS row_number
FROM ( select ID,title,last_name,first_name
from local
UNION ALL
select ID,title ,last_name,first_name
from foreign
) l
JOIN (SELECT #curRow := 0) r
The trick here is that JOIN (SELECT #curRow := 0) creates the variable #curRow with an initial value of 0 and returns its value. The database will take the query results and for each row, it will increase the variable and return the increased value. This can only be done at the end and forces sequential processing of the results.
By using JOIN (SELECT #curRow :=0) r you just avoid creating the variable in a separate statement.
I am running following query to get rank of business in all categories in terms of total number of likes.
SET #rownum = 0;
SELECT b.*
, (
SELECT f4.rank from business as b2 INNER JOIN (
select count(*) count, #rownum:=#rownum + 1 as rank, f3.* from favourites as f3 GROUP BY f3.business_id ORDER BY count DESC ) as f4 ON b2.id = f4.business_id WHERE b2.id = 8 && f4.category_id=c.id
)
as rank FROM business as b, category c where b.id=8
rank give NULL after first row, what should I do to reset #rownum to 0 for next row?
To reset the #rownum user variable, you could try including an inline view (i.e. a derived table) in the FROM clause.
It looks like you would need that within the inner correlated subquery. That correlated subquery should get re-executed for every row from category c, or at least every distinct value of c.id. (I'm going to assume that the id column in each table is the primary key.)
e.g.
FROM ...
JOIN (SELECT #rownum := 0) r
WHERE ...
BUT... I am hesitant to recommend this approach to you, because I am having difficulty unwinding your SQL statement. It's not clear what resultset you want returned. It looks like that query should be throwing an exception, if that subquery returns more than one row. I just don't see anything explicit or implied that would give you that guarantee.
An example of the desired output would go a long ways to getting some useful help.
I am pretty sure you want ROW_NUMBER, RANK or DENSE_RANK partitioned by business_ID but I cannot penetrate your SQL
Some inputs & outputs would be helpful.
select * from
business as f4 inner join
(
select business_id, count(*),rank() over (partition by business_id order by count desc ) as rank) as counts
on f4.business_id=counts.business_id
might be close
It seems to me that your code should increment #rownum for every row in the result because the first subquery and therefore the joined subquery should be executed once for every row.
In my opinion, your query is equivalent to the following:
SELECT b.*, #rownum:=#rownum + 1 AS rank
FROM business AS b, category c
WHERE b.id=8
Edit: If the problem is that you need to reset #rownum in a subquery but you're limited to a single column in the result, use something like this construct:
SELECT IF(#rownum:=0, NULL, f4.rank) AS rank FROM ...
The condition #rownum:=0 is always evaluated, resetting rownum, and because it evaluates to 0, value of f4.rank is always returned.