SQL:搜索/替换,但仅在记录中第一次出现值

时间:2012-08-25 15:58:22

标签: mysql sql sql-update replace

我在post_content列中有html内容。

我想搜索并用A替换A,但只有第一次A出现在记录中,因为它可能出现不止一次。

以下查询显然会替换A与B的所有实例

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

9 个答案:

答案 0 :(得分:14)

这实际上应该是你想要的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

答案 1 :(得分:8)

或者,您可以使用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');

答案 2 :(得分:1)

如果您使用的是Oracle DB,您应该能够编写类似的内容:

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,因为它似乎是用户输入。

答案 3 :(得分:1)

关于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不变。

示例:将“测试”替换为“任何”:

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 :(得分:0)

由于REPLACE()的编写方式(仅替换要替换的字符串的第一个字符),Greg Reda的解决方案对于长度超过1个字符的字符串不起作用。这是一个我认为更完整的解决方案,涵盖问题的每个用例,定义为如何替换第一个出现的"字符串A"用"字符串B"在"字符串C"?

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

这假设您确定条目 ALREADY包含要替换的字符串!如果您尝试更换“狗”。与猫#39;在字符串' pupper'中,它会给你' per'这不是你想要的。这是一个查询,通过首先检查要替换的字符串是否存在于完整字符串中来处理它:

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

这里的具体用例是替换&#39; blah&#39;的第一个实例。内栏&#39; buycraft&#39;用空字符串&#39;&#39;我认为这是一个非常直观和自然的解决方案:

  1. 查找要替换的字符串第一次出现的索引。
  2. 获取左侧的所有内容,不包括索引本身(因此&#39; -1&#39;)。
  3. 将您用原始字符串替换为。
  4. 计算要替换的字符串部分的结束索引。通过再次查找第一个匹配项的索引并添加替换字符串的长度,可以轻松完成此操作。这将为您提供原始字符串
  5. 之后的第一个字符的索引
  6. 从字符串
  7. 的结束索引开始连接子字符串

    替换&#34; pupper&#34;的示例演练in&#34; lil_puppers_yay&#34;和“狗狗”:

    1. &#39; pupper&#39;是5。
    2. 左转5-1 = 4.所以索引1-4,这是&#39; lil _&#39;
    3. 连接&#39; dog&#39;为了&#39; lil_dog&#39;
    4. 计算结束指数。开始索引是5,并且5 +长度的&#39; pupper&#39; = 11.请注意,索引11指的是&#39;。
    5. 从结束索引开始连接子字符串,即&#39; s_yay&#39;,以获得&#39; lil_dogs_yay&#39;。
    6. 全部完成!

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

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

答案 5 :(得分:0)

我做了以下小功能并得到了它:

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

答案 6 :(得分:0)

更简单

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

答案 7 :(得分:0)

自提出此问题以来,已经过去了多年,而MySQL 8引入了REGEX_REPLACE

  

REGEXP_REPLACE(expr,pat,repl [,pos [,出现[,match_type]]])

     

替换字符串expr中与常规匹配的内容   由模式pat用替换字符串指定的表达式   repl,并返回结果字符串。如果expr,pat或repl为NULL,   返回值为NULL。

     

REGEXP_REPLACE()采用以下可选参数:

     

排名:expr中开始搜索的位置。如果省略,则默认值为1。
  出现:要替换的匹配项。如果省略,则默认值为0(表示“替换所有匹配项”)。
   match_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 ;

答案 8 :(得分:-1)

为了使gjreda的样本更加简单,请使用:

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