How to get row number using sql stored procedure - mysql

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.

Related

How to query a table created by another query?

So I have a table, users, with user Balances and IDs.
With the below query, I get the table I need – which sorts the users by their balance.
SET #row_num=0; SELECT (#row_num:=#row_num+1) AS serial_num, ID, Balance FROM users ORDER BY Balance DESC; - which returns the following table:
Resulting MYSQL table
How would I find the serial_num of a specific user from the above table by ID?
I've tried SELECT * FROM ( the query above ) WHERE ID = "..."; but I must be getting something wrong with the syntax and I don't quite understand how I would implement a sub-query here.
Cheers
You had actually just 1 like mistake which lead to an uninitialized variable. Replace
SET #row_num=0;
with
SET #row_num:=0;
A little shorter version which can be run in one query would be:
SELECT *
FROM
(
SELECT ID, Balance, #row := #row + 1 AS serial_num
FROM users
CROSS JOIN (SELECT #row := 0) r
ORDER BY Balance DESC
) tmp
WHERE serial_num = 2
SQLFiddle demo

SQL: A column in a subquery does not appear

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.

How to combine rows with same values in mysql

How do I combine rows with same values in the first row and put null or space in the rows instead without affecting GROUP BY subject in the select statement? Have a look at what I am trying to achieve and help me.
My attempted query is:
SELECT regd, GROUP_CONCAT(name order by name SEPARATOR ' ') as name,
subject, sc, fm FROM table GROUP BY regd, subject
Firstly, I would suggest that you handle this in code rather than at the DB level!
But, if you absolutely must do it all in a query, you could try ranking over partition with the regd column being the partition. Your expected output has rather arbitrarily ordered rows within each regd.
This query will order by subject within each regd:
select t.regd,
case when r=1 then t.name else null end as name,
t.subject,
t.sc,t.fm
from
(
select tt.*,
case when regd = #curRegd then #rank := #rank +1 else #rank:=1 end as r,
#curRegd := tt.regd
from table tt
join (SELECT #curRegd := 0,#rank:=0) r
order by regd,subject
) t
Finally, based on your stored data example, it seems like no aggregation i.e. GROUP BY clause, is necessary here.

I want row number in view Or variable in View

I know it is not possible directly.
But I want to achieve this by any indirect method if possible.
Actually I wanted to add below query to view which throws error , Sub query not allowed in view.
select T1.Code,
T1.month,
T1.value,
IfNull(T2.Value,0)+IfNull(T3.value,0) as value_begin
from (select *,#rownum := #rownum + 1 as rownum
from Table1
Join (SELECT #rownum := 0) r) T1
left join (select *,#rownum1 := #rownum1 + 1 as rownum
from Table1
Join (SELECT #rownum1 := 0) r) T2
on T1.code = T2.code
and T1.rownum = T2.rownum + 1
left join (select *,#rownum2 := #rownum2 + 1 as rownum
from Table1
Join (SELECT #rownum2 := 0) r) T3
on T1.code = T3.code
and T1.rownum = T3.rownum + 2
Order by T1.Code,T1.rownum
So, I thought I will make Sub query as separate view but that again throws error that variables not allowed in view. Please Help to overcome this situation.
Thanx in advance
You could try the method of triangle join + count for assigning row numbers. It will likely not perform well on large datasets, but instead you should be able to implement everything with a couple of views (if you think there's no other way to do what you want to do than with a view). The idea is as follows:
The dataset is joined to itself on the condition of master.key >= secondary.key, where master is the instance where detail data will actually be pulled from, and secondary is the other instance of the same table used to provide the row numbers.
Based on that condition, the first* master row would be joined with one secondary row, the second one with two, the third one with three and so on.
At this point, you can group the result set by the master key column(s) as well as the columns that you need in the output (although in MySQL it would be enough to group by the master key only). Count the rows in every group will give you corresponding row numbers.
So, if there was a table like this:
CREATE TABLE SomeTable (
ID int,
Value int
);
the query to assign row numbers to the table could look like this:
SELECT m.ID, m.Value, COUNT(*) AS rownum
FROM SomeTable AS m
INNER JOIN SomeTable AS s ON m.ID >= s.ID
GROUP BY m.ID, m.Value
;
Since you appear to want to self-join the ranked rowset (and twice too), that would require using the above query as a derived table, and since you also want the entire thing to be a view (which doesn't allow subqueries in the FROM clause), you would probably need to define the ranking query as a separate view:
CREATE RankingView AS
SELECT m.ID, m.Value, COUNT(*) AS rownum
FROM SomeTable AS m
INNER JOIN SomeTable AS s ON m.ID >= s.ID
GROUP BY m.ID, m.Value
;
and subsequently refer to that view in the main query:
CREATE SomeOtherView AS
SELECT ...
FROM RankingView AS t1
LEFT JOIN RankingView AS t2 ON ...
...
This SQL Fiddle demo shows the method and its usage.
One note with regard to your particular situation. Your table probably needs row numbers to be assigned in partitions, i.e. every distinct Code row group needs its own row number set. That means that your ranking view should specify the joining condition as something like this:
ON m.Code = s.Code AND m.Month >= s.Month
Please note that months in this case are assumed to be unique per Code. If that is not the case, you may first need to create a view that groups the original dataset by Code, Month and rank that view instead of the original dataset.
* According to the order of key.

MySQL reset variable after every row

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.