MySQL reset variable after every row - mysql

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.

Related

How to get row number using sql stored procedure

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.

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.

How to get unsorted result from a select query that uses an 'in'?

below is the query i have.
select * from tblQuestionTable where Paper='HTML' and QuestionId in (select QuestionId from tblTestOverview where TestId=1)
The sub query gives an unsorted result set, but the after querying the second select the result is sorted. How can i get the result in the same order as the subquery.
Any dataset your query is working with is by default unordered, whether it is a physical table or a derived one. Whatever order the server uses to read rows from it while actually executing the query is out of your control. That means you cannot reliably specify the order to be "same as in that subquery". Instead, why not just have a specific order in mind and specify it explicitly in the main query with an ORDER BY? For instance, like this:
SELECT *
FROM tblQuestionTable
WHERE Paper='HTML'
AND QuestionId IN (SELECT QuestionId FROM tblTestOverview WHERE TestId=1)
ORDER BY QuestionId
;
Having said that, here's something that might be close to what you are looking for. The ROW_NUMBER function assigns row numbers to the derived dataset in an undetermined order (ORDER BY (SELECT 1)). It may or may not be the order in which the server has read the rows, but you can use the assigned values to order the final result set by:
SELECT q.*
FROM tblQuestionTable AS q
INNER JOIN (
SELECT
QuestionId,
rn = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM tblTestOverview
WHERE TestId = 1
) AS o
ON o.QuestionId = q.QuestionId
ORDER BY o.rn ASC
;
select result for tblQuestionTable will be sorted based on its primary index by default unless specified. tblTestOverview select result also does the same. So you need to include the primary index key feild from tblTestOverview table in the select query for tblQuestionTable and specify an order by clause based on that field.

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.

How to determine the place of a row in ordered SQL table?

I have a table of users and a 'points' column. I would like to determine the number/place of the row across all the users ordered by 'points'.
I could just get result of all users data and then do while loop, and stop when id equals necessary user. But I believe there is a more efficient way do to that because my table will contain ~100 000 rows.
Try this:
SET #rownum = 0;
Select sub.*, sub.rank as Rank
FROM
(
Select *, (#rownum:=#rownum+1) as rank
FROM users
ORDER BY points
) sub
WHERE rank = 15
You need the row number of your record
Here's a good way to do it in MySQL
It would look like this
SELECT rank_user.*
FROM
(
SELECT #rownum:=#rownum+1 ‘rank’, p.*
FROM user u, (SELECT #rownum:=0) r
ORDER BY points DESC
) rank_user
WHERE rank BETWEEN 2 AND 4;