Reputation: 157
I'm using MySQL to perform database transactions and PHP to run MySQL queries. I want to fetch the query result by this sequence using value of is_free
column (0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,...). I want to fetch records by a ratio like 3:6 for the value of is_free
column.
Is there any possible way to achieve this?
I've searched for different algorithms but nothing is found as per my requirement. I've found the only query which throws result by alternative records but my requirement not fulfilled.
I expect the output like
id is_free
-- -------
1 0
2 0
3 0
4 1
5 1
6 1
7 1
8 1
9 1
10 0
11 0
12 0
Upvotes: 1
Views: 278
Reputation: 17815
select id,is_free,rank
from (
(
select id,is_free,if(@row_num = @target,@target := @target + 9,@target := @target + 0) as target,if(@target - @row_num > 3,@row_num := @row_num + 7,@row_num := @row_num + 1) as rank
from test,(select @row_num := 0,@target := 3) t2
where is_free = 0
)
union
(
select id,is_free,if(@row_num2 = @target2,@target2 := @target2 + 9,@target2 := @target2 + 0) as target,if(@target2 - @row_num2 > 8,@row_num2 := @row_num2 + 4,@row_num2 := @row_num2 + 1) as rank
from test,(select @row_num2 := 3,@target2 := 9) t3
where is_free = 1
)
) derived
order by rank,is_free,id;
Demo: https://www.db-fiddle.com/f/pc9oCwwtWjacETVeGCMtcw/0
The idea above is to give a rank
to every row in such a way that we could maintain a 3:6 ratio.
If we closely observe, the 0
's follow the series of ranks as 1 , 2 , 3 , 10 , 11 , 12 , 19 , 20 , 21
and 1
's follow the series of 4, 5, 6, 7, 8, 9, 13, 14, 15, 16, 17, 18, 22, 23 ...
as shown below:
Series:
0 0 0 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 0 0 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
So, in the above query, we first fetch all records of 0
and assign row numbers to them in the above fashion starting from 1
. We then fetch all records of is_free = 1
in the same manner by assigning row numbers starting from 4
.
In the end, we combine both results using union and order them by their ranks.
Upvotes: 0
Reputation: 272296
You need something similar to ROW_NUMBER() OVER (PARTITION BY is_free ORDER BY id)
. Once you have the row numbers for each partition (e.g. 0-0, 0-1, 0-2, 0-3, ..., 1-0, 1-1, 1-2, 1-3, ...) you can use basic math to sort the results (values 0-0...0-2 and 1-0...1-5 go first, 0-3...0-5 and 1-6...1-11 go second and so on). Unfortunately MySQL 5.x does not support window functions so you need to calculate row numbers using some kind of trick. Here is one:
SELECT *
, rn div IF(is_free = 1, 6, 3) AS gn
FROM (
SELECT id
, is_free
, (SELECT COUNT(*) FROM t AS x WHERE x.id < t.id AND x.is_free = t.is_free) AS rn
FROM t
) AS x
ORDER BY gn, is_free
Upvotes: 1
Reputation: 1270683
In MySQL 8+, you can use:
order by floor( (row_number() over (partition by is_free order by id) - 1) / 4),
is_free, id
The arithmetic on row_number()
batches them. You can do something similar with variables in earlier versions:
select t.id, t.is_free
from (select t.*,
(@rn := if(@if = is_free, @rn + 1,
if(@if := is_free, 1, 1)
)
) as rn
from (select t.* from t order by is_free, id) t cross join
(select @if := -1, @rn := 0) params
) t
order by floor((rn - 1) / 4), is_free, id;
Upvotes: 0