【问题标题】:Update multiple rows of one table using modulas of another table使用另一个表的模数更新一个表的多行
【发布时间】:2021-01-08 01:10:48
【问题描述】:
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_id 并一次更新每一行。我想知道这是否可以通过一个查询来实现,因为一次更新可能多达 100 万行。

【问题讨论】:

  • 能否提供一个案例场景?
  • 使用 queue_container_id 0 的示例,将是一个完全填充的队列表,并按此顺序包含以下“tele”列:1000000001、1000000002、1000000003、1000000004、1000000005、1000000001、1000 1000000004, 1000000005.

标签: mysql sql count subquery window-functions


【解决方案1】:

一个选项使用窗口函数和算术。假设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() 和带有子查询的窗口计数将无法很好地扩展。一种替代方法是用户变量:

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,并使用窗口函数解决方案。这是正确的做法。

Demo on DB Fiddle

【讨论】:

  • 我应该提到这一点:我使用的是 MySQL 5.7
  • 你也是对的。 queue_id 总是递增 1。
  • 顺便说一句-我尝试了您的解决方案,它适用于 MySQL 8.0,但我如何将其翻译为适用于 5.7?
  • 我试过: select * from queue q inner join ( select c.* from container c where queue_container_id = 1 ) c ( 它分配正确,但这可靠吗?)
  • 我收到您的解决方案的两个警告:警告 |第1287章不推荐在表达式中设置用户变量,并将在未来的版本中删除。考虑替代方案:“SET variable=expression, ...”或“SELECT expression(s) INTO variables(s)”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 2019-12-27
  • 1970-01-01
  • 2018-12-02
  • 2015-10-30
相关资源
最近更新 更多