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 &lt; 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按条件更新同一表列并按列分组的主要内容,如果未能解决你的问题,请参考以下文章

比较两个 MYSQL 表并按列返回缺失的日期和分组

R:dplyr条件汇总并按列重新编码值

Python - 导入csv文件并按列分组数字

MySQL - 根据更新其他表列值触发更新列值

Python - 读取 csv 并按列对数据进行分组

MySQL - 根据联接表列值更新列值