【问题标题】:SQL: search/replace but only the first time a value appears in recordSQL:搜索/替换,但仅在值第一次出现在记录中时
【发布时间】:2012-08-20 20:22:21
【问题描述】:

我在 post_content 列中有 html 内容。

我想搜索 A 并将其替换为 B,但仅在 A 第一次出现在记录中时,因为它可能出现多次。

下面的查询显然会将 A 的所有实例替换为 B

UPDATE wp_posts SET post_content = REPLACE (post_content, 'A', 'B');

【问题讨论】:

  • mysql 数据库 @gjreda 感谢您的解释。
  • 我正在使用 mysql 数据库。 @gjreda 谢谢,我尝试了该查询,但出现错误。 #1064 - 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“UPDATE wp_posts SET post_content = REPLACE(SUBSTRING_INDEX(post_content, 'A',') 附近使用正确的语法

标签: mysql sql sql-update replace


【解决方案1】:

这实际上应该是你在 MySQL 中想要的:

UPDATE wp_post
SET post_content = CONCAT(REPLACE(LEFT(post_content, INSTR(post_content, 'A')), 'A', 'B'), SUBSTRING(post_content, INSTR(post_content, 'A') + 1));

这比我之前的回答稍微复杂一些 - 您需要找到“A”的第一个实例(使用 INSTR 函数),然后使用 LEFT 结合 REPLACE 来替换该实例,而不是使用 SUBSTRING 和 INSTR找到您要替换的同一个“A”并将其与前一个字符串 CONCAT。

请看下面我的测试:

SET @string = 'this is A string with A replace and An Answer';
SELECT @string as actual_string
, CONCAT(REPLACE(LEFT(@string, INSTR(@string, 'A')), 'A', 'B'), SUBSTRING(@string, INSTR(@string, 'A') + 1)) as new_string;

生产:

actual_string                                  new_string
---------------------------------------------  ---------------------------------------------
this is A string with A replace and An Answer  this is B string with A replace and An Answer

【讨论】:

  • 这似乎不适用于长度超过一个字符的字符串:(
【解决方案2】:

或者,您可以像这样使用函数LOCATE()INSERT()CHAR_LENGTH()

INSERT(originalvalue, LOCATE('A', originalvalue), CHAR_LENGTH('A'), 'B')

完整查询:

UPDATE wp_posts
SET post_content = INSERT(originalvalue, LOCATE('A', originalvalue), CHAR_LENGTH('A'), 'B');

【讨论】:

    【解决方案3】:

    参考https://dba.stackexchange.com/a/43919/200937这里是另一种解决方案:

    UPDATE wp_posts 
    SET post_content = CONCAT( LEFT(post_content , INSTR(post_content , 'A') -1),
                               'B',
                               SUBSTRING(post_content, INSTR(post_content , 'A') +1))
    WHERE INSTR(post_content , 'A') > 0;
    

    如果你有另一个字符串,例如testing 那么你需要将上面的+1 更改为相应的字符串长度。为此,我们可以使用LENGTH()。顺便说一句,不要动-1

    示例:将“testing”替换为“whatever”:

    UPDATE wp_posts 
    SET post_content = CONCAT( LEFT(post_content , INSTR(post_content , 'testing') -1),
                               'whatever',
                               SUBSTRING(post_content, INSTR(post_content , 'testing') + LENGTH("testing"))
    WHERE INSTR(post_content , 'testing') > 0;
    


    顺便说一下,看看有多少行会受到影响:

    SELECT COUNT(*)
    FROM post_content 
    WHERE INSTR(post_content, 'A') > 0;
    

    【讨论】:

      【解决方案4】:

      如果您使用的是 Oracle 数据库,您应该能够编写如下内容:

      UPDATE wp_posts SET post_content = regexp_replace(post_content,'A','B',1,1)
      

      更多信息请看这里:http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions130.htm

      注意:您确实应该注意post_content 的安全问题,因为它似乎是用户输入。

      【讨论】:

        【解决方案5】:

        Greg Reda 的解决方案不适用于长度超过 1 个字符的字符串,因为 REPLACE() 是如何编写的(仅替换要替换的字符串的第一个字符)。这是一个我认为更完整的解决方案,它涵盖了问题的每个用例,定义为如何在“String C”中将“String A”的第一次出现替换为“String B”?

        CONCAT(LEFT(buycraft, INSTR(buycraft, 'blah') - 1), '', SUBSTRING(buycraft FROM INSTR(buycraft, 'blah') + CHAR_LENGTH('blah')))
        

        这假设您确定条目已经包含要替换的字符串!如果您尝试在字符串 'pupper' 中将 'dog' 替换为 'cat',它会给你 'per',这不是你想要的。这是一个通过首先检查要替换的字符串是否存在于完整字符串中来处理该问题的查询:

        IF(INSTR(buycraft, 'blah') <> 0, CONCAT(LEFT(buycraft, INSTR(buycraft, 'blah') - 1), '', SUBSTRING(buycraft FROM INSTR(buycraft, 'blah') + CHAR_LENGTH('blah'))), buycraft)
        

        这里的具体用例是将“buycraft”列中的第一个“blah”实例替换为空字符串“”。我认为一个非常直观和自然的解决方案:

        1. 查找要替换的字符串第一次出现的索引。
        2. 获取左侧的所有内容,不包括索引本身(因此为“-1”)。
        3. 将其与要替换原始字符串的任何内容连接起来。
        4. 计算要替换的字符串部分的结束索引。这很容易通过再次找到第一次出现的索引并添加替换字符串的长度来完成。这将为您提供原始字符串之后的第一个字符的索引
        5. 从字符串的结束索引开始连接子字符串

        将“lil_puppers_yay”中的“pupper”替换为“dog”的示例演练:

        1. “pupper”的索引为 5。
        2. 从 5-1 向左 = 4。因此索引 1-4,即“lil_”
        3. 为“lil_dog”连接“dog”
        4. 计算结束索引。起始索引为 5,并且 5 + 'pupper' 的长度 = 11。请注意,索引 11 指的是 's'。
        5. 连接从结束索引开始的子字符串,即“s_yay”,得到“lil_dogs_yay”。

        全部完成!

        注意: SQL 有 1 索引字符串(作为一个 SQL 初学者,在我发现这个问题之前我并不知道这一点)。此外,SQL LEFT 和 SUBSTRING 似乎以理想的方式使用无效索引(将其调整为字符串的开头或结尾),这对于像我这样的初学者 SQLer 来说非常方便:P

        另一个注意事项:我是 SQL 的初学者,这几乎是我写过的最难的查询,因此可能存在一些效率低下的问题。但它可以准确地完成工作。

        【讨论】:

        • 在您的第一个查询中。字符串 A、B 和 C 在哪里?哪个是主字符串,搜索替换字符串,以及新的替换字符串?
        【解决方案6】:

        我做了以下小功能,搞定了:

        CREATE DEFINER=`virtueyes_adm1`@`%` FUNCTION `replace_first`(
            `p_text` TEXT,
            `p_old_text` TEXT,
            `p_new_text` TEXT
        
        )
        RETURNS text CHARSET latin1
        LANGUAGE SQL
        NOT DETERMINISTIC
        CONTAINS SQL
        SQL SECURITY DEFINER
        COMMENT 'troca a primeira ocorrencia apenas no texto'
        BEGIN
            SET @str = p_text;
            SET @STR2 = p_old_text;
            SET @STR3 = p_new_text;
            SET @retorno = '';
        
            SELECT CONCAT(SUBSTRING(@STR, 1 , (INSTR(@STR, @STR2)-1 ))
                        ,@str3
                          ,SUBSTRING(@STR, (INSTR(@str, @str2)-1 )+LENGTH(@str2)+1 , LENGTH(@STR)))
            INTO @retorno;
        
            RETURN @retorno;    
        END
        

        【讨论】:

          【解决方案7】:

          比较简单

          UPDATE table_name SET column_name = CONCAT('A',SUBSTRING(column_name, INSTR(column_name, 'B') + LENGTH('A')));
          

          【讨论】:

            【解决方案8】:

            问这个问题已经过去好几年了,MySQL 8 引入了REGEX_REPLACE

            REGEXP_REPLACE(expr, pat, repl[, pos[,occurrence[, match_type]]])

            替换字符串 expr 中匹配正则的匹配项 由模式 pat 指定的表达式和替换字符串 repl,并返回结果字符串。如果 expr、pat 或 repl 为 NULL, 返回值为NULL。

            REGEXP_REPLACE() 采用这些可选参数:

            pos:expr 中开始搜索的位置。如果省略,则默认值为 1。
            occurrence:要替换的匹配项。如果省略,则默认值为 0(表示“替换所有匹配项”)。
            ma​​tch_type:指定如何执行匹配的字符串。含义如 REGEXP_LIKE() 所述。

            所以,假设您可以在您的情况下使用正则表达式:

            UPDATE wp_posts SET post_content = REGEXP_REPLACE (post_content, 'A', 'B', 1, 1);
            

            不幸的是,对于我们这些使用 MariaDB 的人来说,它的 REGEXP_REPLACE 风格缺少 occurrence 参数。这是Andriy M 解决方案的正则表达式版本,方便地存储为Luciano Seibel 建议的可重用函数:

            DELIMITER //
            DROP FUNCTION IF EXISTS replace_first //
            CREATE FUNCTION `replace_first`(
                `i` TEXT,
                `s` TEXT,
                `r` TEXT
            )
            RETURNS text CHARSET utf8mb4
            BEGIN
                SELECT REGEXP_INSTR(i, s) INTO @pos;
                IF @pos = 0 THEN RETURN i; END IF;
                RETURN INSERT(i, @pos, CHAR_LENGTH(REGEXP_SUBSTR(i, s)), r);
            END;
            //
            DELIMITER ;
            

            【讨论】:

              【解决方案9】:

              要使 gjreda 的示例更简单,请使用以下代码:

              UPDATE wp_post
                  SET post_content =
                          CONCAT(
                              REPLACE(LEFT(post_content, 1), 'A', 'B'),
                              SUBSTRING(post_content, 2)
                          ) 
                  WHERE post_content LIKE 'A%';
              

              【讨论】:

                猜你喜欢
                • 2021-10-31
                • 1970-01-01
                • 1970-01-01
                • 2016-12-15
                • 2020-04-12
                • 2014-01-28
                • 1970-01-01
                • 2023-01-08
                • 2014-07-08
                相关资源
                最近更新 更多