Mysql session variable emulating row_num does not increase in value - mysql

Since MySql does not provide a row_num facility, I was trying to emulate its functionality using session variables (#variable_name) in my query
the problem statement is as follows
Given a table 'product_view' having columns 'productTitle' and 'categoryName',
select the first 2 products from each category whose title matches a provided string
Going by the following query
SELECT
#row_num := CASE
WHEN #cat_name = categoryName THEN #row_num + 1
ELSE 1
END
AS num,
#cat_name := categoryName AS categoryName,
productTitle
FROM product_search
WHERE
cityId = 1 AND
mainStatus= 'Active' AND
stock_status = 'In Stock' AND
prodQuantity != 0 AND
productTitle LIKE '%apple%'
ORDER BY categoryName, LOCATE('apple', productTitle), productTitle
This is expected to give row_num values 1,2 for category1 and 1,2 for category 2 and so on (wherever ther is a match for 'Apple').
But the rownum value is always stuck at 1
Something similar on the fiddle below
FIDDLE
Can someone please help me out on this

In more recent versions of MySQL, you need to do the order by in a subquery.
In addition, you cannot set #cat_name in one expression and use it in another. MySQL does not guarantee the order of evaluation of expressions in a SELECT. So, you don't know which gets evaluated first, the assignment or the comparison.
I usually write this as:
SELECT (#rn := IF(#cat_name = categoryName, #rn + 1,
IF(#cat_name := categoryName, 1, 1)
)
) as num,
categoryName, productTitle
FROM (SELECT ps.*
FROM product_search ps
WHERE cityId = 1 AND
mainStatus= 'Active' AND
stock_status = 'In Stock' AND
prodQuantity <> 0 AND
productTitle LIKE '%apple%'
ORDER BY categoryName, LOCATE('apple', productTitle), productTitle
) ps CROSS JOIN
(SELECT #cat_name := '', #rn := 0) params

Related

MySQL SUM TOP 2 RECORDS FOR EACH CATEGORY

I have a table for students scores, am trying to sum top 2 marks for all student for a particular category.I have search for similar post but have not gotten correct answer
I have tried summing the marks but am only getting result for two students instead of all students and it does not give me correct value.
SELECT SUM(marks) as totalmarks,stdid
FROM (( select marks,stdid
from finalresult
where `subjectcategory` = 1
AND `classId`='3' AND `year`='2018'
AND `term`='2' AND `type`='23'
order by marks desc
LIMIT 2 ))t1
GROUP BY stdid
An auxiliary subquery might be used for iteration
SELECT
stdid, marks
FROM
(
SELECT stdid, marks,
#rn := IF(#iter = stdid, #rn + 1, 1) AS rn,
#iter := stdid
FROM finalresult
JOIN (SELECT #iter := NULL, #rn := 0) AS q_iter
WHERE `subjectcategory` = 1
AND `classId`='3'
AND `year`='2018'
AND `term`='2'
AND `type`='23'
ORDER BY stdid, marks DESC
) AS T1
WHERE rn <= 2
this solution ignores ties and takes only two for each Student ID.
Demo
In MySQL 8+, you would do:
SELECT stdid, SUM(marks) as totalmarks
FROM (SELECT fr.*,
ROW_NUMBER() OVER (PARTITION BY stdid ORDER BY marks DESC) as seqnm
FROM finalresult fr
WHERE subjectcategory = 1 AND
classId = 3 AND
year = 2018 AND
term = 2 AND
type = 23
) fr
WHERE seqnum <= 2
GROUP BY stdid;
Note that I removed the single quotes. Things that look like numbers probably are. And you should not mix type -- put the quotes back if the values really are stored as strings.
In earlier versions, probably the simplest method is to use variables, but you have to be very careful about them. MySQL does not guarantee the order of evaluation of variables in SELECT, so you cannot assign a variable in one expression and use it in another.
A complicated expression solves this. Also, it is best to sort in a subquery (the latest versions of MySQL 5+ require this):
SELECT stdid, SUM(marks) as totalmarks
FROM (SELECT fr.*,
(#rn := IF(#s = stdid, #rn + 1,
IF(#s := stdid, 1, 1)
)
) as seqnum
FROM (SELECT fr.*
FROM finalresult fr
WHERE subjectcategory = 1 AND
classId = 3 AND
year = 2018 AND
term = 2 AND
type = 23
ORDER BY stdid, marks DESC
) fr CROSS JOIN
(SELECT #s = '', #rn := 0) params
WHERE seqnum <= 2
GROUP BY stdid;

adding sequential number to existing data specific to an ID MySQL

New to the forum.I wonder if somebody out there might be able to help with an issue I am having?
I need to add an addition column in a query to show a sequential number 1,2,3...ect but to have it be specific to an existing ID - The s_id based on the order of the s_id. Below show an example of the data I am working with.
'p_id' 's_id'
'327' '569'
'330' '569'
'1205' '569'
'350' '1000'
I am aiming to get it to look like this.
'Seq_no' 'p_id' 's_id'
'1' '327' '569'
'2' '330' '569'
'3' '1205' '569'
'1' '350' '1000'
I have looked at adding an auto increment bur cannot manage to tie it back to the back specifically to the s_id.
Any help would be greatly appreciated.
Best Regards
DSW
You can do it with a self join :
SELECT count(s.sid) as seq_no,t.p_id,t.s_id
FROM YourTable t
JOIN YourTable s
ON(t.s_id = s.s_id AND t.p_id >= s.p_id)
GROUP BY t.p_id,t.s_id
Assuming that you're sorting the dataset by (s_id,p_id) then here's one way (untested):
SELECT x.*
, CASE WHEN #prev = x.s_id THEN #i:=#i+1 ELSE #i:=1 END i
, #prev := s_id prev
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY s_id,p_id;
A common approach is using MySQL user defined variables.
In your case you need two of them.
SELECT
#rn := IF(#sameSid = s_id, #rn := #rn + 1 ,
IF(#sameSid := s_id, 1, 1)
) AS Seq_no,
p_id,
s_id
FROM your_table
CROSS JOIN
(
SELECT #sameSid := 0, #rn := 1
) AS var
ORDER BY s_id , p_id

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;

How to get Items in order with limit in mysql

I have a table Item ( id, itemType). itemType can be from 1-5.
I want to retrieve 2 items of each type using mysql.
I tried
select * from `item` ORDER BY `itemType` limit 2
which gives me 2 items order by type but I want 2 items of each type.
http://sqlfiddle.com/#!9/ef83d/1
You can use below query, even i did not check it with data as I dont have sample data so if you get any issue then you can create a sqlfiddle, so that I can customize query as per you in #sql
SELECT x.id,x.type1 as 'Type'
FROM (SELECT t.*,
CASE
WHEN #type != t.type THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#type := t.type AS 'type1'
FROM item t
JOIN (SELECT #rownum := NULL, #type := '') r
ORDER BY t.type,t.id) X
WHERE x.rank<=2;
Even you can ordering based on top price or any other field.

Greatest n-per-group With Multiple Joins

Evening,
I am trying to get an output of rows that are limited to n per group in MySQL. I can get it to work without joins, but with it I am just shy. I've pasted a dump of the relevant tables here:
http://pastebin.com/6F0v1jhZ
The query I am using is:
SELECT
title, catRef, RowNum, pCat, tog
FROM
(
SELECT
title, catRef,
#num := IF(#prevCat=catRef,#num+1,1) AS RowNum,
#prevCat AS tog,
#prevCat := catRef AS pCat
FROM (select #prevCat:=null) AS initvars
CROSS JOIN
(
SELECT p.title, oi.catRef
FROM resources p
INNER JOIN placesRel v ON (p.resId = v.refId)
INNER JOIN catRel oi ON (p.resId = oi.refId)
WHERE p.status = 'live' AND v.type = 'res' AND oi.type = 'res'
) AS T
) AS U
WHERE RowNum <= 5
ORDER BY catRef
I just can't get the row count to go up. Or any other solution would be greatly appreciated.
I'm looking for a result like this:
title catRef RowNum
Title1 1 1
Title2 1 2
Title3 1 3
Title4 2 1
Title5 2 2
Title6 3 1
At the moment, the RowNum column is always 1.
This works:
SET #num := 1, #prevCat := 0;
SELECT title, start, end, type, description, linkOut, outType, catRef, row_number
FROM (
SELECT title, start, end, type, description, linkOut, outType, catRef,
#num := if(#prevCat = catRef, #num + 1, 1) as row_number,
#prevCat AS tog,
#prevCat := catRef AS dummy
FROM (
SELECT title, start, end, resources.type, description, linkOut, outType, catRef
FROM resources LEFT JOIN placesRel ON placesRel.refId = resId LEFT JOIN catRel ON catRel.refId = resId
WHERE status = 'live' AND placesRel.type = 'res' AND catRel.type = 'res'
ORDER BY catRef
) AS w
) AS x WHERE x.row_number <= 4;
You need to put your joined query in a sub-query and order it by the column you want to group by. Use it's parent query to add row numbers. Then, the top-level query glues it all together.
If you don't put your joined query in it's own sub-query, the results won't be ordered as you wish, but instead will come out in the order they are in the database. This means the data is not grouped, so row numbers will no be applied to ordered rows.