A view with internal variables - mysql

I have a query where I want to fetch the top x for each value of another column. For doing this I have the following query
SELECT *,
(#num := IF(#job = job_id, #num + 1, IF(#job := job_id, 1, 1))) row_number
FROM job_user_rank t
CROSS JOIN (SELECT #num := 0, #job := null) c
where is_match = 1
ORDER BY job_id, is_match DESC, rank DESC
I then wrap this query and add a where row_number <= ?, however I want to make that inner query into a view instead, but I get the following error [HY000][1351] View's SELECT contains a variable or parameter, how can I get around that?

Related

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;

referring select output as a table in where clause

I am having a query and I want to perform the operation like
select *
from (query which i wrote) as x
where
(select count(*)
from x as y
where x.location=y.location
and x.count>=y.count)<=3;
It was giving error
instead of x, I can add the query which I wrote. but the query is pretty much big. when I tried the above query it is giving table doesn't exist error. is there a way to perform the above operation? kindly help me.
You cannot re-use a table alias like that. Instead, you need to copy the subquery. Or use variables:
select q.*
from (select q.*,
(#rn := if(#l = location, #rn + 1,
if(#l := location, 1, 1)
)
) as rn
from (query which i wrote) q cross join
(select #l := '' , #rn := 0) params
order by location, count desc
) q
where rn <= 3;

MariaDB/MySQL RANK() implementation

I am working on migration from MS SQL Server to MariaDB 10.0. I have query which use RANK() PARTITION BY. I already created some kind of RANK() implementation on my table but it's not working properly.
The original query was:
RANK() OVER (PARTITION BY visits.id_partner ORDER BY visits.updated_at DESC) AS rank
My implementation for MariaDB/MySQL:
SELECT
...,
(
CASE visits.id_partner
WHEN #currId THEN
#curRow := #curRow + 1
ELSE
#curRow := 1 AND #currId := visits.id_partner
END
) AS rank
FROM
records rec
JOIN visits ON visits.id = rec.id_visit,
(
SELECT
#curRank := 0,
#currId := NULL
) r
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
I want to select row ranked by id_partner order by updated_at field. When id_partner is same as on row before RANK should increase by 1. When is different than before, it should reset to 1.
But my query is not working at all. I have still rank 1 on all rows. Can you help me find mistake?
Thank you for help!
It is tricky to use variables in MySQL/MariaDB. A variable should only be used and assigned in one statement (as you do correctly). However, AND can short-circuit variable assignment.
I use a construct like this for ROW_NUMBER(). RANK() is actually a bit of a pain . . . DENSE_RANK() and ROW_NUMBER() are simpler. However, this seems to be the code that you are aiming for:
SELECT ...,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM records rec JOIN
visits
ON visits.id = rec.id_visit CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC;
EDIT:
In MySQL (and presumably in MariaDB), sometimes variables don't work quite right unless you use a subquery. So, try this:
SELECT . . .,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM (SELECT ...
FROM records rec JOIN
visits
ON visits.id = rec.id_visit
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
) t CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params;

Getting the rank of a record using two where clauses

I am trying to get the rank of specified record, and I have some success with this code:
SELECT `rank`
FROM
(
select #rownum:=#rownum+1 `rank`, p.*
from TableName p, (SELECT #rownum:=0) r
order by point DESC
) s
WHERE names = 'house'
(See the schema here.)
This SQL query works, but if I want to get the result according to city_id and name, I must use two where clauses, and then the code doesn't work.
I want to get rank of house only for its city. How can I do this?
I want to get rank of "house" only for its city
You can do this by introducing another variable to keep track of the city:
SELECT `rank`
FROM (select p.*,
(#rn := if(#c = city, #rn + 1,
if(#c := city, 1, 1)
)
) as rank
from TableName p CROSS JOIN
(SELECT #rn := 0, #c := -1) params
order by city_id, point DESC
) s
WHERE names = 'house' ;
You can also use your original query, with a tweak, if you know that "house" only appears once in the data:
SELECT `rank`
FROM (select p.*, (#rn := #rn + 1) as rank
from TableName p CROSS JOIN
(SELECT #rn := 0) params
where city_id = (select city_id from TableName t2 where t2.names = 'house')
order by point DESC
) s
WHERE names = 'house' ;

generate serial number which resets for a particular condition using mysql query

I have a table named browsekot in mysql database.This table contains menu items that have been ordered from different outlets(restaurants) along with their quantites and price during dine-in. I need to generate a report.
On using below query:
rec.Open "select Outlet,ItemName,id,sum(Quantity) as Quantity, sum(Value) as Value,#i:= #i + 1 as result from (SELECT #i := 0) h , browsekot group by ItemName,Outlet order by #i ", adoconn
gives my this Output:
Sr.No.....Outlet.....Name
1.............Taj...........x
2.............Taj...........y
3.............Mez..........t
4.............Mez..........z
But i want to reset the count #i for each outlet and want my output to be:
Sr.No.....Outlet.....Name
1.............Taj...........x
2.............Taj...........y
1.............Mez..........t
2.............Mez..........z
I want to reset the count in the above query itself as i will be using this query later with SHAPE Command for displaying in datareport. how do i change the above query to reset the count for each outlet?
Use the following SQL:
select Outlet,ItemName,id, Quantity, `Value`,
#i:= IF(Outlet = #last_outlet, #i + 1, 1) as result,
#last_outlet := Outlet
from (SELECT #i := 0, #last_outlet := NULL) h
JOIN (SELECT Outlet, ItemName, id, SUM(Quantity) AS Quantity, SUM(`Value`) as `Value`
FROM browsekot
GROUP BY ItemName,Outlet
ORDER BY Outlet) i
FIDDLE