Mysql按条件更新同一表列并按列分组
Posted
技术标签:
【中文标题】Mysql按条件更新同一表列并按列分组【英文标题】:Mysql update same table column by condition and group by column 【发布时间】:2016-09-17 16:48:54 【问题描述】:这里是mysql表我想设置版本列如下。版本列需要更新数百万条记录
File_id |file_name | type | flag | original_file_id | version
1 |abc.jpg | FILE | RENAMED | 1 | 1
3 |folder1 | FOLDER | RENAMED | 3 | null
5 |folder1 | FOLDER | null | 3 | null
7 |abc1.jpg | FILE | null | 1 | 2
9 |abc1.jpg | FILE | DELETED | 1 | 2
11 |abc.jpg | FILE | MOVED | 1 | 3
13 |abc.jpg | FILE | null | 1 | 4
15 |xyz.jpg | FILE | null | 6 | 1
17 |xyz.jpg | FILE | DELETED | 6 | 1
19 |xyz.jpg | FILE | null | 6 | 2
我想像上面设置的那样设置版本。对于第一个文件,其中 file_id=original_file_id 然后版本=1。如果标志被删除,然后设置以前的版本,如版本 2。对于其他重命名,MOVED 增量版本。但它应该只更新用于文件而不是文件夹。
【问题讨论】:
你的表中的主键列是什么? @JimMacaulay 抱歉,我已经更新了我的问题 file_id 是主键 @GordonLinoff 否,版本取决于标志和 original_file_id。并且版本与 original_file_id 不一样现在检查。 【参考方案1】:您可以使用以下查询:
update files
join
( select f1.file_id,
count(f2.file_id) + count(distinct forg.file_id) as version
from files f1
left outer join files forg
on f1.original_file_id = forg.file_id
and f1.original_file_id = forg.original_file_id
and forg.flag = 'DELETED'
left outer join files f2
on f1.original_file_id = f2.original_file_id
and f1.file_id >= f2.file_id
and coalesce(f2.flag,'') <> 'DELETED'
and coalesce(f2.type,'') <> 'FOLDER'
where coalesce(f1.type,'') <> 'FOLDER'
group by f1.file_id
) fileversions
on fileversions.file_id = files.file_id
set files.version = fileversions.version
where coalesce(files.type, '') <> 'FOLDER';
它将增加每个original_file_id
-group 中的版本,除非它有flag = 'Deleted'
。所有带有type = 'Folder'
的行都将被忽略且不会更新。
第一个带有file_id = original_file_id
的文件将获得版本1,即使它有flag = 'deleted'
,在这种情况下,下一个未删除的文件将获得版本2。
查询不会检查您的数据是否已损坏,例如如果带有file_id = original_file_id
的文件存在,或者如果存在带有file_id < original_file_id
的id 的文件。
如果您希望索引在合理的时间内完成,您需要一个索引original_file_id
(或original_file_id, file_id
)。
【讨论】:
感谢您的帮助。但我有一些疑问。1)(如果组的第一个文件具有该标志,则它的版本设置为 0)这不应该发生,因为第一个文件总是获得版本 1 而不是还要别的吗。 2)如果某处原始文件记录已损坏,那么它如何处理?像 originalfileid 50 并且该组有 3 个文件,文件 ID 为 51,52,53,原始文件 ID 为 50,但原始文件 ID 和文件 ID 为 50 的文件已损坏。 1) 如果第一个条目被删除会怎样?你说一个“删除”的文件应该得到“以前的版本”,它不存在 2)我不明白你的意思,你能举个例子和你想要的结果吗?但一般来说:如果你的数据不干净,你就不能指望一个干净的结果。你的意思是像original_file_id = 6
这样的东西吗?即使file_id = 6
不存在,我的查询也会在这种情况下准确地为您提供问题的输出(file_id = 15
的版本 1)。
忘记损坏数据的第二个条件。但第一个条件必须存在,即具有 fileid=originalfileid 且版本始终为 1 的文件。例如,在我的示例表文件中,文件 id 为 1 和15 是第一个文件,原始文件 id 始终为 1,与标志无关。之后的任何内容都需要考虑标志。
一切都很完美,除了这个条件版本必须以 1 开头。它不应该是零。
因此假设在您的示例数据中,file_id 的 1、7 和 9 将具有“已删除”标志,file_ids 1、7、9 和 11 的版本将是:1、1、1, 2?对吗?【参考方案2】:
你可以使用这个查询:
UPDATE FILES
SET version = CASE
WHEN original_file_id = File_id THEN File_id
WHEN flag = 'DELETED' THEN (SELECT F1.version-F2.version FROM FILES F1, FILES t2 WHERE F1.File_id = F2.File_id -1)
WHEN flag = 'RENAMED' THEN ((SELECT F1.version-F2.version FROM FILES F1, FILES t2 WHERE F1.File_id = F2.File_id -1)+1)
WHEN flag = 'MOVED' THEN ((SELECT F1.version-F2.version FROM FILES F1, FILES t2 WHERE F1.File_id = F2.File_id -1)+1);
【讨论】:
收到了吗? 我明白了,但是我通过更改表列添加类型列再次更新了我的问题,请立即检查。我只需要更新文件而不是文件夹。所以对于 file_id,它并不总是递增 1 . 在每个 where 条件之后提供 **AND F1.type='FILE' **。你能做到吗?还是您需要再次完整查询? 没关系,我可以添加条件,其中 type="FILE" 但 F1.File_id = F2.File_id -1 这有效吗?因为你不能确保下一个 file_id.suppose abc.jpg file_id 3 那么下一个 file_id 可以是 60.so 对于这个 WHERE F1.File_id = F2.File_id -1 我该怎么办? 您的查询依赖于每个组的连续 file_id(没有间隙,并且中间没有其他文件组)。我不认为这是一个有效的假设。以上是关于Mysql按条件更新同一表列并按列分组的主要内容,如果未能解决你的问题,请参考以下文章