I have this kind of query that need to limit by 3 in each 'region' group but it doesnt run as I expected. The 'row_number' seems not arranged accordingly.
There must be some syntax I missed out or I didnt know. If anyone can help I dump the sql Here. MYSQL version 5.0
My query :
set #type = '';
set #num = 0;
SELECT locinvaisle.Area as ar,locinvaisle.Region as rg,custlist.CustomerName as cn,custlist.Custtype ct,
SUM(data2.quantity/1000) as mtcur,
#num := if(#type = locinvaisle.Region, #num + 1, 1) as dummy_1,
#type := locinvaisle.Region as dummy_2,
#num as row_number
FROM data2
INNER JOIN custlist ON data2.customeracc = custlist.Customeraccount
INNER JOIN locinvaisle ON data2.location = locinvaisle.Location
WHERE
date1 >= DATE_FORMAT('2018-06-11', '%Y-01-01') AND date1 <= DATE_FORMAT('2018-06-11', '%Y-%m-31')
AND
data2.unit = 'KG'
AND
data2.customeracc not in (select Customeraccount from custlist WHERE Custcat = 'bcsb')
AND
locinvaisle.Area = 'peninsular'
AND
custlist.Custtype = 'others'
GROUP BY locinvaisle.Region,custlist.CustomerName
HAVING row_number < 3
ORDER BY locinvaisle.Region,mtcur desc
Results :
Desired Results(From dummy database) :
You have to do the limiting as a full subquery ("derived table") before the group by is applied. Something along these lines:
SELECT
ar, rg, cn, ct, sum(quantity)
FROM (
SELECT
#row_num :=IF(#prev_value = locinvaisle.Region, #row_num + 1, 1)AS RowNumber
, locinvaisle.Area as ar
, locinvaisle.Region as rg
, custlist.CustomerName as cn
, custlist.Custtype ct
, data2.quantity
, #prev_value := locinvaisle.Region as dummy_2,
FROM data2
INNER JOIN custlist ON data2.customeracc = custlist.Customeraccount
INNER JOIN locinvaisle ON data2.location = locinvaisle.Location
CROSS JOIN (SELECT #row_num :=1, #prev_value :='') vars
WHERE date1 >= DATE_FORMAT('2018-06-11', '%Y-01-01') AND date1 <= DATE_FORMAT('2018-06-11', '%Y-%m-31')
AND data2.unit = 'KG'
AND data2.customeracc not in (select Customeraccount from custlist WHERE Custcat = 'bcsb')
AND locinvaisle.Area = 'peninsular'
AND custlist.Custtype = 'others'
ORDER BY locinvaisle.Region,mtcur desc
) d
WHERE rowNumber <= 3
GROUP BY ar, rg, cn, ct
ORDER BY ar, rg, cn, ct
NOTE: You need to apply an ORDER BY within the subquery to facilitate the rownumber calculation, but despite this there is no guarantee that the final output will be in the desired order unless you apply a final order by clause.
Also please note that in future versions of MySQL (V8 and above) there should be a row_number() function and over() clause that can be used instead of the variables seen above.
In MySQL, you need to be careful when using variables. Two things are important:
Variables should be assigned and referenced in the same expression.
You need to be careful about the ordering of the result set.
So, try something like this:
SELECT x.*,
(#rn := IF(#r = la.Region, #num + 1,
IF(#r := la.Region, 1, 1)
)
) as rn
FROM (SELECT la.Area as ar, la.Region as rg, c.CustomerName as cn, c.Custtype ct,
SUM(data2.quantity/1000) as mtcur
FROM data2 d INNER JOIN
custlist c
ON d.customeracc = c.Customeraccount INNER JOIN
locinvaisle la
ON d.location = la.Location
WHERE date1 >= DATE_FORMAT('2018-06-11', '%Y-01-01') AND
date1 <= DATE_FORMAT('2018-06-11', '%Y-%m-31') AND
d.unit = 'KG' AND
d.customeracc not in (select Customeraccount from custlist cl WHERE cl Custcat = 'bcsb') AND
la.Area = 'peninsular' AND
c.Custtype = 'others'
GROUP BY la.Region, c.CustomerName
) x CROSS JOIN
(SELECT #r := 0, #rn := 0) params
HAVING rn <= 3;
Related
Is there a way to get the last 3 records (red border)? Please check the screenshot.
Screenshot:
MySQL query.
SELECT icm_external_data.client_id,
icm_external_data.gsheet_tab AS source,
icm_external_data.type,
icm_external_data.name AS lead_name,
COUNT(DISTINCT icm_leads.email) AS on_month_leads,
date_format(icm_leads.date_created,'%M') AS month,
date_format(icm_leads.date_created,'%Y') AS YEAR,
#running_total := #running_total + COUNT(DISTINCT icm_leads.email) AS cumulative_sum
FROM icm_leads
LEFT JOIN icm_external_data ON icm_external_data.gsheet_id = icm_leads.gsheet_id
JOIN (SELECT #running_total := 0) r
WHERE icm_external_data.id = '29'
AND icm_leads.gsheet_id = 'xxx'
AND (icm_leads.tab_name = 'xxx' OR icm_leads.tab_name = 'xxx')
AND icm_leads.email NOT LIKE '%xxx%'
AND (icm_leads.`type` = 'forms' OR icm_leads.`type` = 'calls')
GROUP BY year(icm_leads.date_created),month(icm_leads.date_created)
You seem to want:
order by min(icm_leads.date_created) desc
limit 3 offset 1
Use the ORDER BY and LIMIT keywords.
SELECT icm_external_data.client_id,
icm_external_data.gsheet_tab AS source,
icm_external_data.type,
icm_external_data.name AS lead_name,
COUNT(DISTINCT icm_leads.email) AS on_month_leads,
date_format(icm_leads.date_created,'%M') AS month,
date_format(icm_leads.date_created,'%Y') AS YEAR,
#running_total := #running_total + COUNT(DISTINCT icm_leads.email) AS cumulative_sum
FROM icm_leads
LEFT JOIN icm_external_data ON icm_external_data.gsheet_id = icm_leads.gsheet_id
JOIN (SELECT #running_total := 0) r
WHERE icm_external_data.id = '29'
AND icm_leads.gsheet_id = 'xxx'
AND (icm_leads.tab_name = 'xxx' OR icm_leads.tab_name = 'xxx')
AND icm_leads.email NOT LIKE '%xxx%'
AND (icm_leads.`type` = 'forms' OR icm_leads.`type` = 'calls')
GROUP BY year(icm_leads.date_created),month(icm_leads.date_created)
ORDER BY icm_leads.date_created DESC
LIMIT 3 OFFSET 1
Need to find median value of time difference between sent date and click date (in seconds) for each type of emails. I found solution just for all data:
SET #rowindex := -1;
SELECT g.type, g.time_diff
FROM
(SELECT #rowindex:=#rowindex + 1 AS rowindex,
TIMESTAMPDIFF(SECOND, emails_sent.date_sent, emails_clicks.date_click) AS time_diff,
emails_sent.id_type AS type
FROM emails_sent inner join emails_clicks on emails_sent.id = emails_clicks.id_email
ORDER BY time_diff) AS g
WHERE g.rowindex IN (FLOOR(#rowindex / 2) , CEIL(#rowindex / 2));
Is it possible to add group by id_type statement?
Thanks!
First, you need to enumerate the rows for each type. Using variables, this code looks like:
select sc.*,
(#rn := if(#t = id_type, #rn + 1,
if(#t := id_type, 1, 1)
)
) as seqnum
from (select timestampdiff(second, s.date_sent, c.date_click) as time_diff,
s.id_type,
from emails_sent s inner join
emails_clicks c
on s.id = c.id_email
order by time_diff
) sc cross join
(select #t := -1, #rn := 0) as params;
Then, you need to bring in the total number for each type and do the calculation for the median:
select sc.id_type, avg(time_diff)
from (select sc.*,
(#rn := if(#t = id_type, #rn + 1,
if(#t := id_type, 1, 1)
)
) as seqnum
from (select timestampdiff(second, s.date_sent, c.date_click) as time_diff,
s.id_type,
from emails_sent s inner join
emails_clicks c
on s.id = c.id_email
order by time_diff
) sc cross join
(select #t := -1, #rn := 0) as params
) sc join
(select id_type, count(*) as cnt
from emails_sent s inner join
emails_clicks c
on s.id = c.id_email
group by id_type
) n
where 2 * seqnum in (n.cnt, n.cnt, n.cnt + 1, n.cnt + 2)
group by sc.id_type;
trying to run below query, expecting to find a match. Since my two sub-queries are same, i am expecting to find a match, which is not happening here.
select TAB_1.RUL_IDD, TAB_2.RUL_IDD
FROM
( select aaa.RUL_ID RUL_IDD from TEST_1 aaa
inner join
(
select dt,#curRank := #curRank + 1 AS rank
from (
select distinct DATE(BTCH_RUN_DTTM) dt
FROM TEST_1
where ALRT_FLG_IND = 'Y'
and PASS_IND = 'N'
and SCH_RUN_IND = 'Y'
order by DATE(BTCH_RUN_DTTM) desc
) p,
(SELECT #curRank := 0) r
order by dt desc
) zz
where DATE(aaa.BTCH_RUN_DTTM) = zz.dt
and zz.rank=1
and aaa.ALRT_FLG_IND = 'Y'
and aaa.PASS_IND = 'N'
and aaa.SCH_RUN_IND = 'Y'
) TAB_1
left outer JOIN
(
select bbb.RUL_ID as RUL_IDD from TEST_1 bbb
inner join
(
select dt,#curRank := #curRank + 1 AS rank
from (
select distinct DATE(BTCH_RUN_DTTM) dt
FROM TEST_1
where ALRT_FLG_IND = 'Y'
and PASS_IND = 'N'
and SCH_RUN_IND = 'Y'
order by DATE(BTCH_RUN_DTTM) desc
) p,
(SELECT #curRank := 0) r
order by dt desc
) zzz
on DATE(bbb.BTCH_RUN_DTTM) = zzz.dt
and zzz.rank=1
and bbb.ALRT_FLG_IND = 'Y'
and bbb.PASS_IND = 'N'
and bbb.SCH_RUN_IND = 'Y'
) TAB_2
on TAB_2.RUL_IDD = TAB_1.RUL_IDD;
Result:
enter image description here
Not getting why its not giving the expected result. TAB_2.RUL_IDD should also have same value as TAB_1.RUL_IDD. Can experts help me over here?
I have a table:
id name
1 a
2 a
3 a
4 b
5 b
6 c
I am looking for an update statement that will update name column to:
id name
1 a
2 a-2
3 a-3
4 b
5 b-2
6 c
In SQL Server I would use:
;with cte as(select *, row_number() over(partition by name order by id) rn from table)
update cte set name = name + '-' + cast(rn as varchar(10))
where rn <> 1
I am not strong in MySQL nonstandard queries.
Can I do something like this in MySQL?
You can do this:
UPDATE YourTable p
JOIN(SELECT t.id,t.name,count(*) as rnk
FROM YourTable t
INNER JOIN YourTable s on(t.name = s.name and t.id <= s.id)
GROUP BY t.id,t.name) f
ON(p.id = f.id)
SET p.name = concat(p.name,'-',f.rnk)
WHERE rnk > 1
This will basically use join and count to get the same as ROW_NUMBER() , and update only those who have more then 1 result(meaning the second,third ETC excluding the first)
In MySQL you can use variables in order to simulate ROW_NUMBER window function:
SELECT id, CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
FROM (
SELECT id, name,
#rn := IF(name = #n, #rn + 1,
IF(#n := name, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #n := '') AS vars
ORDER BY name, id) AS t
To UPDATE you can use:
UPDATE mytable AS t1
SET name = (
SELECT CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
FROM (
SELECT id, name,
#rn := IF(name = #n, #rn + 1,
IF(#n := name, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #n := '') AS vars
ORDER BY name, id) AS t2
WHERE t1.id = t2.id)
Demo here
You can also use UPDATE with JOIN syntax:
UPDATE mytable AS t1
JOIN (
SELECT id, rn, CONCAT(name, IF(rn = 1, '', CONCAT('-', rn))) AS name
FROM (
SELECT id, name,
#rn := IF(name = #n, #rn + 1,
IF(#n := name, 1, 1)) AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0, #n := '') AS vars
ORDER BY name, id) AS x
) AS t2 ON t2.rn <> 1 AND t1.id = t2.id
SET t1.name = t2.name;
The latter is probably faster than the former because it performs less UPDATE operations.
The next query will do it with less effort for the database:
UPDATE
tab AS tu
INNER JOIN
-- result set containing only duplicate rows that must to be updated
(
SELECT
t.id,
COUNT(*) AS cnt
FROM
tab AS t
-- join the same table by smaller id and equal value. That way you will exclude rows that are not duplicated
INNER JOIN
tab AS tp
ON
tp.name = t.name
AND
tp.id < t.id
GROUP BY
t.id
) AS tc
ON
tu.id = tc.id
SET
tu.name = CONCAT(tu.name, '-', tc.cnt + 1)
Output: https://www.dropbox.com/s/q9bjrzndbzj0l2i/test3.PNG?dl=0
How do I incorporate a query into the current code if I want to get only the Position (row_number) = 3 if lets say the Stud_ID = 4?
SET #row_num = 0;
SELECT #row_num := #row_num + 1 as Position, s.*
FROM
(
SELECT
Student.Stud_ID, Student.Stud_Name, Student.Stud_Class, SUM(Grade.Percentage) AS Points
FROM Student, Student_Subject, Grade
WHERE Student.Stud_ID = Student_Subject.Stud_ID
AND Student_Subject.Stud_Subj_ID = Grade.Stud_Subj_ID
AND Student.Stud_Form = '1'
AND Grade.Quarter = '1'
GROUP BY Student.Stud_ID
ORDER BY Points DESC
) AS s;
Thanks!!
Use a subselect over your query and filter it with your desired row number
SELECT *
FROM (
SELECT #row_num := #row_num + 1 AS `Position`, s.*
FROM
(
SELECT
Student.Stud_ID, Student.Stud_Name, Student.Stud_Class, SUM(Grade.Percentage) AS Points
FROM Student, Student_Subject, Grade
WHERE Student.Stud_ID = Student_Subject.Stud_ID
AND Student_Subject.Stud_Subj_ID = Grade.Stud_Subj_ID
AND Student.Stud_Form = '1'
AND Grade.Quarter = '1'
GROUP BY Student.Stud_ID
ORDER BY Points DESC
) AS s
CROSS JOIN (SELECT #row_num := 0) AS s1
) AS t
WHERE t.Position = 3