SQL: поиск/замена, но только в первый раз, когда значение отображается в записи
У меня есть html-содержимое в столбце post_content.
Я хочу выполнить поиск и заменить A на B, но только первый раз появляется A в записи, поскольку она может появляться более одного раза.
Следующий запрос, очевидно, заменит все экземпляры A на B
UPDATE wp_posts SET post_content = REPLACE (post_content, 'A', 'B');
Ответы
Ответ 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
Если вы используете 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
в отношении проблемы безопасности, поскольку он кажется пользователем.
Ответ 4
Я сделал следующую маленькую функцию и получил ее:
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
Ответ 5
Решение Greg Reda не работало для меня на строках длиной более 1 символа из-за того, как был написан REPLACE() (заменяя только первый символ строки, которую нужно заменить). Вот решение, которое, по моему мнению, более полно и охватывает все варианты использования проблемы, если определено как "Как заменить первое вхождение" String A "на" String B "в" String C "?
CONCAT(LEFT(buycraft, INSTR(buycraft, 'blah') - 1), '', SUBSTRING(buycraft FROM INSTR(buycraft, 'blah') + CHAR_LENGTH('blah')))
Это предполагает, что вы уверены, что запись УЖЕ СОДЕРЖИТ СТРОНУЮ ЗАМЕНУ! Если вы попробуете заменить "собаку" на "кошку" в строке "puppy", она даст вам "per", что не то, что вы хотите. Вот запрос, который обрабатывает это, сначала проверяя, существует ли строка, подлежащая замене, в полной строке:
IF(INSTR(buycraft, 'blah') <> 0, CONCAT(LEFT(buycraft, INSTR(buycraft, 'blah') - 1), '', SUBSTRING(buycraft FROM INSTR(buycraft, 'blah') + CHAR_LENGTH('blah'))), buycraft)
Конкретный вариант использования здесь заменяет первый экземпляр 'blah' внутри столбца buycraft пустой строкой ''. Я думаю, что это довольно интуитивное и естественное решение:
- Найдите индекс первого вхождения строки, которая должна быть заменена.
- Получить все слева от него, не считая самого индекса (таким образом, "-1" ).
- Конкатенация того, что вы заменяете исходную строку.
- Вычислить конечный индекс части строки, которая заменяется. Это легко сделать, если снова найти индекс первого вхождения и добавить длину замененной строки. Это даст вам индекс первого char после исходной строки
- Объединить подстроку, начинающуюся с конечного индекса строки
Пример прокрутки замены "pupper" в "lil_puppers_yay" на "dog":
- Индекс "щенка" равен 5.
- Получить слева от 5-1 = 4. Таким образом, индексы 1-4, которые являются "lil _"
- Объединить 'собаку' для 'lil_dog'
- Вычислить конечный индекс. Начальный индекс равен 5, а 5 + длина "щенка" = 11. Обратите внимание, что индекс 11 относится к 's'.
- Объедините подстроку, начинающуюся с конечного индекса, который является 's_yay', чтобы получить 'lil_dogs_yay'.
Все сделано!
Примечание. SQL имеет 1-индексированную строку (как новичок SQL, я не знал этого, прежде чем я решил эту проблему). Кроме того, SQL LEFT и SUBSTRING, похоже, работают с недопустимыми индексами как идеальный способ (корректируя его либо в начале, либо в конце строки), что очень удобно для начинающего SQLer, такого как я: P
Другое примечание: Я - новичок в SQL, и это самый сложный запрос, который я когда-либо писал, поэтому могут быть некоторые неэффективности. Он выполняет работу точно, хотя.
Ответ 6
Это проще
UPDATE table_name SET column_name = CONCAT('A',SUBSTRING(column_name, INSTR(column_name, 'B') + LENGTH('A')));
Ответ 7
Прошли годы с тех пор, как был задан этот вопрос, и MySQL 8 представил REGEX_REPLACE:
REGEXP_REPLACE (expr, pat, repl [, pos [, вхождение [, match_type]]])
Заменяет вхождения в строке expr, которые соответствуют регулярному выражению, указанному в шаблоне pat, на строку замены repl и возвращает результирующую строку. Если expr, pat или repl имеют значение NULL, возвращаемое значение равно NULL.
REGEXP_REPLACE() принимает следующие необязательные аргументы:
pos: позиция в expr, с которой начинается поиск. Если опущено, по умолчанию 1.
вхождение: какое вхождение совпадения заменить. Если опущено, по умолчанию 0 (что означает "заменить все вхождения").
match_type: строка, которая указывает, как выполнить сопоставление. Значение как описано для REGEXP_LIKE().
Итак, при условии, что вы можете использовать регулярные выражения в вашем случае:
UPDATE wp_posts SET post_content = REGEXP_REPLACE (post_content, 'A', 'B', 1, 1);
К сожалению для тех из нас, кто работает на MariaDB, в его варианте REGEXP_REPLACE отсутствует параметр вхождения. Вот версия решения Andriy M с поддержкой регулярных выражений, которую удобно хранить в виде функции многократного использования, как было предложено Лучано Сейбелем:
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
Чтобы сохранить образец gjreda немного проще, используйте это:
UPDATE wp_post
SET post_content =
CONCAT(
REPLACE(LEFT(post_content, 1), 'A', 'B'),
SUBSTRING(post_content, 2)
)
WHERE post_content LIKE 'A%';