我试图弄清楚如何更新表中的行,如果它们的总大小小于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/