使用另一个 table 的模数更新一个 table 的多行

Update multiple rows of one table using modulas of another table

create table queue(queue_id int primary key auto_increment, tele bigint);
insert into queue values
  (null, null)
, (null, null)
, (null, null)
, (null, null)
, (null, null)
, (null, null)
, (null, null)
, (null, null)
, (null, null)
, (null, null)

create table container (container_id int primary key auto_increment, queue_container_id int, source bigint);
insert into container values
  (null, 1, 1000000001)
, (null, 1, 1000000002)
, (null, 1, 1000000003)
, (null, 1, 1000000004)
, (null, 1, 1000000005)
, (null, 2, 1000000003)

我的目标: 我想用适当的来源更新 tele 专栏 #.

我想将源#s 均匀地分配到队列中。一种约束,它必须只使用给定的queue_container_id。例如,如果我将 queue_container_id 设为 1,它只会使用那些源编号。队列应该填满所有电话号码。重复应首先尝试均匀分布。

困难的方法是获取所有 queue_ids 并一次更新每一行。我想知道这是否可以通过一个查询实现,因为一次可能要更新多达 100 万行。

一个选项使用 window 函数和算术。假设 queue_id 总是无间隙递增:

select *
from queue q
inner join (
    select c.*, 
        row_number() over(order by container_id) rn,
        count(*) over() cnt
    from container c
    where queue_container_id = 1
) c on (c.rn - 1) = (q.queue_id - 1) % c.cnt

这会将给定 queue_container_id 的容器均匀分布到队列中。

如果queue_id不可靠,我们可以用row_number()生成我们自己的序列:

select *
from (
    select q.*, 
        row_number() over(order by queue_id) rn 
    from queue 
) q
inner join (
    select c.*, 
        row_number() over(order by container_id) rn,
        count(*) over() cnt
    from container c
    where queue_container_id = 1
) c on (c.rn - 1) = (q.queue_id - 1) % c.cnt

在早期版本中,选项有限。如果您有大量行,使用子查询模拟 row_number() 和 window 计数将无法很好地扩展。一种替代方法是用户变量:

select *
from queue q
inner join (
    select c.*, @rn := @rn + 1 rn
    from (
        select * 
        from container c 
        where queue_container_id = 1 
        order by container_id
    ) c
    cross join (select @rn := 0) x      
) c on (c.rn - 1) = (q.queue_id - 1) % @rn

用户变量使用起来很棘手,并且(最终!)计划在 MySQL 的未来版本中弃用。虽然上述解决方案适用于 MySQL 5.x,但我强烈建议升级到 MySQL 8.0,并使用 window 函数解决方案。这才是正确的做法。

Demo on DB Fiddle