mysql - 如果总列总和小于 X,则更新行

标签 mysql sql database join rdbms

我试图弄清楚如何更新表中的行,如果它们的总大小小于x

这是我的设置:

create table test_limit (
       id int not null auto_increment primary key,
       folder varchar(255),
       status varchar(32) DEFAULT 'awaiting',
       size bigint unsigned default 0,
       request_id varchar(32)
)
ENGINE=InnoDB;

insert into test_limit
  (folder, status, size)
values
  ('/tmp/AAA/bar', 'awaiting', 200 ),
  ('/tmp/AAA/bar', 'awaiting', 200 ),
  ('/tmp/AAA/bar', 'awaiting', 200 ),
  ('/tmp/BBB/bar', 'awaiting', 200 ),
  ('/tmp/BBB/bar', 'awaiting', 200 );

我有一个包含 5 行的表,每行都有一个大小,我想要做的是更新一组行:

  • 具有相同的文件夹
  • 状态未处于in_progress已创建
  • 总限制为 400

我想出了以下更新命令:

SET @request_id='bbb';
UPDATE test_limit t1
  JOIN
     ( SELECT folder FROM test_limit WHERE status = 'awaiting' GROUP BY folder limit 1) t2
    ON t1.folder = t2.folder
  LEFT JOIN
     ( SELECT folder FROM test_limit WHERE status IN ('in_progress', 'created') GROUP BY folder limit 1) t3
    ON t1.folder = t3.folder
  JOIN
     ( SELECT id, @total := @total + size AS total  FROM (test_limit, (select @total := 0) t)  WHERE @total < 400 and status='awaiting') t4
    ON t1.id=t4.id
  SET t1.status = 'in_progress',
      t1.request_id = @request_id
  WHERE t1.status = 'awaiting' AND t3.folder is NULL;

但问题是它第一次可以工作,但其他时候就不起作用:

mysql> select * from test_limit;
+----+--------------+-------------+------+------------+
| id | folder       | status      | size | request_id |
+----+--------------+-------------+------+------------+
|  1 | /tmp/AAA/bar | in_progress |  200 | bbb        |
|  2 | /tmp/AAA/bar | in_progress |  200 | bbb        |
|  3 | /tmp/AAA/bar | awaiting    |  200 | NULL       |
|  4 | /tmp/BBB/bar | awaiting    |  200 | NULL       |
|  5 | /tmp/BBB/bar | awaiting    |  200 | NULL       |
+----+--------------+-------------+------+------------+
5 rows in set (0.07 sec)

更新:

以上结果对于第一次运行是正确的。我想在第二次运行中实现什么(例如 request_id = 'aaa' ):

mysql> select * from test_limit;
+----+--------------+-------------+------+------------+
| id | folder       | status      | size | request_id |
+----+--------------+-------------+------+------------+
|  1 | /tmp/AAA/bar | in_progress |  200 | bbb        |
|  2 | /tmp/AAA/bar | in_progress |  200 | bbb        |
|  3 | /tmp/AAA/bar | awaiting    |  200 | NULL       |
|  4 | /tmp/BBB/bar | in_progress |  200 | aaa        |
|  5 | /tmp/BBB/bar | in_progress |  200 | aaa        |
+----+--------------+-------------+------+------------+
5 rows in set (0.07 sec)

在第三次运行中,它不应更新任何内容,因为所有值都是“in_progress”。

我怎样才能实现这个目标?

最佳答案

我花了一段时间才想清楚其中的逻辑。这是sql fiddle http://sqlfiddle.com/#!9/227dd0/1

UPDATE test_limit u
JOIN
(
  SELECT
    t1.*
    ,f.NonAwaitingFolderTotal
    ,(@runtot := @runtot + t1.size) as RunningTotal
  FROM
    (
      SELECT
        folder
        ,SUM(CASE WHEN status <> 'awaiting' THEN size ELSE 0 END) as NonAwaitingFolderTotal
      FROM
        test_limit t
      GROUP BY
        folder
      HAVING
        SUM(CASE WHEN status <> 'awaiting' THEN size ELSE 0 END) <= 400
      ORDER BY
        NonAwaitingFolderTotal, folder
      LIMIT 1
    ) f
    INNER JOIN test_limit t1
    ON f.folder = t1.folder
    CROSS JOIN (SELECT @runtot:=0) var
  WHERE
    t1.status = 'awaiting'
)  t2
ON u.id = t2.id
AND (t2.NonAwaitingFolderTotal + t2.RunningTotal) <= 400
SET
  u.status = 'in_progress'
  ,u.request_id = @request_id
;

逻辑是这样的

  • 查找要使用的文件夹并查找该文件夹中当前的非等待总大小。然后按最小的非等待大小(in_progress、已创建)选择一个文件夹,如果按文件夹名称绑定(bind),则限制为 1。
  • 获取该文件夹中所有等待记录的运行总计,用于确定在达到允许的最大值之前可以更新哪些行。
  • 通过连接到运行总计查询的结果进行更新,其中非等待记录的总大小 + 该记录的运行总计小于 400 的最大值。

只是因为我想将其保留在某个地方,主要问题是您使用的运行总数未按正确的级别分组。以下是我思考过的一些运行总计和行号函数。

SELECT 
  *
  ,(@foldercount := IF(@prevfolder=folder,@foldercount,@foldercount+1)) as FolderNum
  ,(@rownum := @rownum + 1) as RowNum
  ,(@grouprownum := IF(@prevfolder=folder,@grouprownum+1,1)) as GroupRowNum
  ,(@total := IF(@prevfolder=folder,@total + t.size,t.size)) as GroupRunningTotal
  ,(@GroupAwaitRunningTotal := IF(
        @prevfolder=folder
        ,IF(t.status = 'awaiting',@GroupAwaitRunningTotal + t.size,@GroupAwaitRunningTotal)
        ,IF(t.status = 'awaiting',t.size,0)
      )
   ) as GroupAwaitRunningTotal
   ,(@GroupNonAwaitRunningTotal := IF(
        @prevfolder=folder
        ,IF(t.status != 'awaiting',@GroupNonAwaitRunningTotal + t.size,@GroupNonAwaitRunningTotal)
        ,IF(t.status != 'awaiting',t.size,0)
      )
   ) as GroupNonAwaitRunningTotal
  ,(@runtot := @runtot + t.size) as RunningTotal
  ,@prevfolder:=folder
FROM 
  test_limit t
  CROSS JOIN
  (SELECT @prevfolder:=NULL, @GroupAwaitRunningTotal := 0
     ,@GroupNonAwaitRunningTotal := 0
     ,@total:=0, @rownum:=0, @grouprownum:=0, @runtot:=0, @foldercount:=0) var

关于mysql - 如果总列总和小于 X,则更新行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38985123/

相关文章:

mysql - 如何加入多对多关系

mysql - 如何在 Sequelize 中使用 findAll 和关联

mysqli_stmt_bind_param 正在我的中插入零

sql - 按周计算首次订阅者

javascript - 服务器无法处理循环

mysql - RoR + XAMPP + MySQL -- Gem 安装 MySQL 问题

MYSQL 对组中的项目进行排序,然后对组进行排序

mysql - 待检索列数据精确匹配

database - 在 oracle 中插入查询显示错误 Missing Expression

java - HSQLDB:在应用程序 jar 文件中嵌入文本表