why this iteration happens between every 2 club name - mysql

ok i have this query from (Mr.Austin):
SELECT
IF(is_real, '**ANY WORD**', full_name) AS full_name,
IF(is_real, '', club_name) AS club_name
FROM
(
SELECT
full_name,
club_name,
(#row_num2:= #row_num2 + 1) AS row_num
FROM
(
SELECT p3.*
FROM
(
SELECT
p2.*,
(#row_num := #row_num + 1) AS row_num
FROM
(
SELECT *
FROM players AS p1
WHERE y_of_birth = 2000
) AS p2
CROSS JOIN
(
SELECT
#row_num := 0,
#count := (SELECT COUNT(*) FROM players WHERE y_of_birth = 2000)
) AS vars
ORDER BY club_name
) AS p3
ORDER BY row_num % FLOOR(#row_num / 2), row_num
) AS p4
CROSS JOIN
(
SELECT
#row_num2 := -1,
#extra := GREATEST(2, POW(2, CEIL(LOG2(#count)))) - #count) AS vars
) AS data
LEFT JOIN
(
(SELECT 1 AS is_real)
UNION ALL
(SELECT 0 AS is_real)
) AS filler
ON
MOD(row_num, FLOOR(#count / #extra)) = 0 AND
row_num / FLOOR(#count / #extra) < #extra
ORDER BY row_num, is_real
it works well and there are no adjacency between two similar club_name but the problem is when i have large result of query for example 214 player it gave me many iterative between the same different club_name for example El-ahly vs Ismaily then again El-ahly vs Ismaily then again El-ahly vs Ismaily then repeat again with wadi dgla vs Cocorico then again wadi dgla vs Cocorico then again wadi dgla vs Cocorico why this iteration happened and how can i fix this problem ? can i make this iteration happens is there anyway to solve this problem to be one or two maximum times the native query of no adjacency between two similar club name is from (Mr.Gordon Linoff):
select p.*
from (select p.*,
(#rn := if(#c = club_name, #rn + 1,
if(#c := club_name, 1, 1)
)
) as rn
from players p cross join
(select #rn := 0, #c := '') params
order by club_name
) p join
(select club_name, count(*) as cnt
from players p
group by club_name
) pc
on p.club_name = pc.club_name
order by rn * (#rn / cnt);
sqlfiddle:
sqlfiddle

Related

How to get TopN query group by month MYSQL

There's a table like:
months contact COUNT
202007 asdas 45
202007 madhouse 1
202007 RORC YANG 1
202007 RORG 2
202007 ROR 5
202008 SARINA 1
202008 SMB 1
How can I get top 4 query result each month?
Expected result:
months contact COUNT
202007 asdas 45
202007 ROR 5
202007 RORG 2
202008 SARINA 1
202008 SMB 1
I'm working with mysql5.6
Here are 2 choices. The first uses rank() over() which does not guarantee only 4 rows per month (there could be more) and the second uses row_number() over() which will limit number of rows to a max of 4 per month
select
*
from (
select
* , rank() over(partition by months order by c desc) as cr
from (
select months, contact, count(*) as c
from mytable
group by months, contact
) as g
) as d
where cr <= 4
;
select
*
from (
select
* , row_number() over(partition by months order by c desc) as rn
from (
select months, contact, count(*) as c
from mytable
group by months, contact
) as g
) as d
where rn <= 4
;
see demo
for older MySQL try a row number hack:
select
*
from (
select
#row_num :=IF(#prev_value=g.months,#row_num+1,1)AS RowNumber
, g.months
, g.contact
, g.c
, #prev_value := g.months
from (
select months, contact, count(*) as c
from mytable
group by months, contact
) as g
CROSS JOIN (SELECT #row_num :=1, #prev_value :='') vars
ORDER BY g.months, g.contact
) as d
where RowNumber <= 4
see that in demo
TOP5
SELECT z.months, z.contact, z.count
FROM
(SELECT
x.*,
#rownum := #rownum + 1,
IF(#part = x.months,#r := #r + 1,#r := 1) AS rank,
#part := x.months
FROM
(
SELECT
*
FROM
my_table e
ORDER BY
e.months ASC,e.count DESC) X,
(
SELECT
#rownum := 0,
#part := NULL,
#r := 0) rt)z
WHERE z.rank <=5

How to find median value with group by (MySQL)

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;

Create a Progressive Sum Column in MySQL

I need to display progressive_total using mysql.
I have a table look like this:
id value cumulative_total
1 100 100
2 150 250
3 200 450
4 300 750
I want to add a new column called progressive_total, so the table would look like this:
id value Progressive_total cumulative_total
1 100 - 100
2 150 100 250
3 200 250 450
4 300 450 750
I use the below mysql query to display the Cumulative total:
SELECT t.id, t.value,
(SELECT SUM(x.value) FROM house_details x WHERE x.id <= t.id) AS cumulative_total
FROM house_details t
WHERE t.income IS NOT NULL AND t.income!= '' AND YEAR(t.cdate) = YEAR(CURRENT_DATE())
ORDER BY t.id
The above query works fine and getting cumulative_total. But i want to display progressive_total also, like i have given in the example.
SELECT t.id, t.value,
IFNULL((SELECT SUM(x.value) FROM house_details x WHERE x.id < t.id), '-') AS progressive_total,
(SELECT SUM(x.value) FROM house_details x WHERE x.id <= t.id) AS cumulative_total
FROM house_details t
WHERE t.income IS NOT NULL AND t.income!= '' AND YEAR(t.cdate) = YEAR(CURRENT_DATE())
ORDER BY t.id
You can use variables:
SELECT t.id, t.value,
#s := #s +IF(#temp := #prev,
IF(#prev := t.value, #temp, #temp),
IF(#prev := t.value, #temp, #temp)) AS progressive_total,
(SELECT SUM(x.value)
FROM house_details x
WHERE x.id <= t.id) AS cumulative_total
FROM house_details t
CROSS JOIN (SELECT #s := 0, #prev := 0, #temp := 0) AS v
WHERE t.income IS NOT NULL and t.income!= '' AND
YEAR(t.cdate) = YEAR(CURRENT_DATE())
order by t.id
Variable #prev is set to the value of the immediately preceding row. We have to use an intermediate variable like #temp, to hold the value of #prev, before #prev is set to its new value.
Demo here
Edit: The above query can be simplified to:
SELECT id, value,
#ps := #ps + prev AS progressive_total,
#cs := #cs + value AS cumulative_total
FROM (
SELECT t.id, t.value,
IF(#temp := #prev,
IF(#prev := t.value, #temp, #temp),
IF(#prev := t.value, #temp, #temp)) AS prev
FROM house_details t
CROSS JOIN (SELECT #s := 0, #prev := 0, #temp := 0) AS v
WHERE t.income IS NOT NULL and t.income!= '' AND
YEAR(t.cdate) = YEAR(CURRENT_DATE())
ORDER BY t.id) AS x
CROSS JOIN (SELECT #ps := 0, #cs := 0) AS u
ORDER BY id
Demo here

How to join each record of table 1 with few records of table 2?

I have two tables named report_instance and report_history (report_instance has many report_history). I want to join each report_instance with first 10 records of report_history.
Example:
report_instance r1 has 20 report_history
report_instance r2 has 5 report_history
The query should give me the result of joining r1 with first 10 records of 20 report_history and r2 with 5 report_history.
My Query:
select *
from report_instances ri, report_history rh
where ri.id in (select rhh.id
from report_history
where rhh.report_instance_id=ri.id limit 10);
I got the error:
This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME
subquery'
You can use variables, as follows, in order to get the latest-per-report_instance_id records:
select *,
#rn := IF(#id = report_instance_id, #rn + 1,
IF(#id := report_instance_id, 1, 1)) AS rn
from report_history
cross join (select #rn := 0, #id := 0) as vars
order by report_instance_id, id desc
You can use the above query as a derived table joined to report_instances table:
select ri.*, rhd.*
from report_instances as ri
join (
select *,
#rn := IF(#id = report_instance_id, #rn + 1,
IF(#id := report_instance_id, 1, 1)) AS rn
from report_history
cross join (select #rn := 0, #id := 0) as vars
order by report_instance_id, id desc
) as rhd on ri.id = rhd.report_instance_id
where rhd.rn <= 10
Try get bellow query,
SELECT TOP 10 {column-name} FROM {Table-name};
Use for your example below,
select *
from report_instances ri, report_history rh
where ri.id in (select TOP 10 rh.id
from report_history
where rh.report_instance_id=ri.id);
Or,
select *
from report_instances ri, report_history rh
where ri.id in (select rh.id
from report_history
where rh.report_instance_id=ri.id
order by rh.id desc limit 0,10);
You have any error tell me.

Update duplicate rows

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)