【问题标题】:Mysql: replace string in one table based on information on another tableMysql:根据另一张表上的信息替换一个表中的字符串
【发布时间】:2014-06-01 05:51:02
【问题描述】:

我有以下问题,这会很长,我想在我的问题中准确地说出我所知道的关于我的问题的所有信息。

我有一个表 field_body_value,其中包含两个字段 body_value 和 body_summary,其中包含“/webfm_send/#”形式的字符串,其中 # 是一个数字。

我有另一个名为 webfm_file 的表,其中有两个包含字符串替换信息的字段:第一个称为 fid,它是我之前提到的数字 #,第二个称为 fpatch,它给了我一个包含路径的字符串(例如 /data/html/files/file1.pdf),该路径必须替换第一个表中的 /webfm_send/#。 #数字在webfm_file的记录上上升但有跳跃,即增加但有缺失#所以最后的#不等于webfm_file中的记录数

所以我认为策略是设置一个循环遍历第二个表的过程,并在序列的每一步检索fid/fpath对,在第一个表中搜索“/webfm_send/fid”,然后替换这是第一个表中的 fpath。

所以这是我的编码所能达到的:

BEGIN
  DECLARE v1 INT DEFAULT 0;
  SELECT COUNT(*) INTO @numrec FROM `webfm_file`;
  WHILE v1 < @numrec DO
    SELECT fpath,fid INTO @path,@file FROM `webfm_file` LIMIT v1,1;
    SET @webfm = concat('/webfm_send/',@file);
    SET @cpath = concat('/',@path);
    UPDATE `field_data_body`
    SET body_value = replace(body_value, @webfm, @cpath),  
    body_summary = replace(body_summary, @webfm, @cpath)
    WHERE body_value LIKE concat('%',@webfm,'%') OR 
    body_summary LIKE concat('%',@webfm,'%');
    SET v1 = v1 + 1;
  END WHILE;
END

让我解释一下我认为我在用上面的代码做什么:

1) 我为循环检索 webfm_file 中的记录数。

2) 第一个 SELECT 从 webfm_file 获取 fpath/fid 中的一对,使用 LIMIT v1,1 我一次只检查一条记录,我检查了它是否有效,while 循环遍历 webfm_file 的每条记录,记录是正确检索。

3)接下来的两个“set”修复了@file/@path这对字符串以创建@webfm,这是它在field_body_value的body_value中写入的方式,并在@cpath前面放置一个斜杠,这是这种方式我需要这个字符串最终出现。

4) 然后是 UPDATE,如果它在 field_body_data 的 body_value 或 body_summary 中找到该字符串,它将实际替换该字符串。

预期:/webfm_send/# 的每个实例都被 webfm_file 中的 # (fid) 对应的 fpath 对替换

我实际得到的是:/webfm_send/# 的所有外观,无论 # 的值如何,都被 webfm_file 的记录 1 中的 fpath 的值替换。

我尝试过的事情:

1) 删除 UPDATE 语句中的“WHERE”子句,我认为这不是绝对必要的,因为替换函数已经负责查找匹配但可以加快速度。结果一样

2) 将循环限制为仅在 webfm_file 的单个记录上循环。在这里,它可以在 field_body_data 中的 body_value 和 body_summary 的两个实例中替换相应的单个检索到的对 fid/fpath,其中 fid=# 出现在字符串 webfm_send/# 中

感谢您在此之前遵循我的解释,并提前感谢您的任何提示。

【问题讨论】:

    标签: mysql sql string sql-update str-replace


    【解决方案1】:

    您可以使用游标来遍历替换字符串。 (使用 group_concat 有更快的方法,用通用语言而不是存储过程更容易做到这一点)。一般的光标方法是:

    drop procedure if exists proc;
    delimiter //
    
    create procedure proc()
    begin
      declare done boolean default 0;
      declare path varchar(255);
      declare id int;
      declare cur cursor for select fpath, fid from webfm_file order by fid desc;
      declare continue handler for sqlstate '02000' set done = 1;
    
      open cur;
    
      block: loop
        fetch cur into path, id;
    
        if done then
          leave block;
        end if;
    
        set @from = concat('/webfm_send/', id);
    
        update field_data_body set
          body_value = replace(body_value, @from, path),
          body_summary = replace(body_summary, @from, path);
      end loop;
    
      close cur;
    end//
    
    delimiter ;
    
    call proc();
    

    【讨论】:

    • 感谢 jspcal,但您的例程与我的完全相同。它用 webfm_file 中 path 的第一条记录的值替换 field_data_body 中的所有 @from 实例。如果我把 SELECT @from;选择路径;然后我看到例程迭代了正确的值,但不知何故在UPDATE 子句中@from 被正确迭代(所有实例webfm_send\# 都找到了)但path 没有。
    • 这很奇怪 - 它对我有用。您的问题可能是字符串 /webfm_send/1 也匹配任何以 1 开头的 ID(10、11 等),因此最终也会替换这些 ID。要解决这个问题,请按降序fid 顺序选择路径条目。
    • 再次感谢 jspcal,就是这样!愚蠢到没有看到例如webfm_send/1 也匹配webfm_send/11webfm_send/111webfm_send/1111。但是由于这些字符串出现在 field_body_data 中并被引号包围,因此只需将匹配更改为 webfm_send\#" 就可以了。无论如何,结论是您的代码和我的代码的工作方式完全相同,但您的代码更优雅,但速度并不快(它们都有完全相同的循环)。想知道最后是否可以通过两个表的 JOIN 而没有循环来实现相同的效果。
    猜你喜欢
    • 2015-06-27
    • 2023-01-25
    • 2021-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多