Как посчитать количество вхождений символа в значении varchar Oracle?
Заголовок: Как посчитать количество вхождений символа -
в строке типа varchar2?
Тело вопроса:
Здравствуйте!
Я пытаюсь выяснить, как можно подсчитать количество вхождений символа -
в строке типа varchar2 в базе данных Oracle.
Например, у меня есть следующая строка:
select XXX('123-345-566', '-') from dual;
Я хотел бы получить результат:
----------------------------------------
2
Кто-нибудь может подсказать, как это сделать? Заранее спасибо!
5 ответ(ов)
Вот как можно перевести ваш ответ на русский язык в стиле ответа на StackOverflow:
Для подсчета количества вхождений определенного символа в строке в Oracle можно использовать следующий запрос:
select length('123-345-566') - length(replace('123-345-566','-',null))
from dual;
Однако стоит отметить, что если строка содержит только тот символ, который вы хотите посчитать, данный запрос вернет значение NULL. Чтобы избежать этого и получить правильный ответ в любых случаях, стоит воспользоваться следующим вариантом:
select coalesce(length('123-345-566') - length(replace('123-345-566','-',null)), length('123-345-566'), 0)
from dual;
В этом запросе функция coalesce
позволяет обработать ситуацию, когда вы выполняете подсчет для пустой строки (то есть NULL, потому что length(NULL)
возвращает NULL в Oracle). Конечный 0 в coalesce
гарантирует, что в случае отсутствия символов в строке результат будет равен 0, а не NULL.
Вот идея: попробуйте заменить все символы, которые не являются дефисом, на пустую строку. Затем посчитайте, сколько дефисов осталось.
Вот пример запроса на SQL, который осуществляет это:
select length(regexp_replace('123-345-566', '[^-]', '')) from dual
Этот запрос заменяет все символы, кроме дефисов, на пустую строку, а затем считает длину оставшейся строки, которая соответствует количеству дефисов в исходной строке.
Вы столкнулись с проблемой, которая довольно распространена при использовании функции REGEXP_COUNT
в Oracle. Давайте разберёмся, почему ваша функция возвращает неверное количество совпадений.
Функция REGEXP_COUNT
действительно может не учитывать повторяющиеся совпадения, особенно в случае перекрывающихся подстрок. Например, в строке 'bbaaaacc' подстрока 'aa' встречается три раза, но из-за того, что REGEXP_COUNT
не считает пересекающиеся совпадения, он возвращает 2.
Ваше решение с использованием пользовательской функции EXPRESSION_COUNT
выглядит многообещающе. Давайте кратко рассмотрим его.
Объяснение функции
Функция EXPRESSION_COUNT
проходит через строку, сравнивая каждую подстроку заданной длины с искомой фразой. Если совпадение найдено, счётчик увеличивается. Таким образом, вы обрабатываете строку в цикле, сдвигая её на один символ в каждой итерации.
Возможные улучшения
Сокращение количества операций: Вместо двукратного вызова
LENGTH
можно сохранить длину строки в переменной.Условие выхода: Ваше условие выхода можно упростить, проверяя только одно условие в конце цикла.
Прямое использование
INSTR
: Можно оптимизировать функцию используя функциюINSTR
, чтобы находить позицию следующего вхождения строки.
Вот пример улучшенной версии вашей функции:
CREATE OR REPLACE FUNCTION EXPRESSION_COUNT( pEXPRESSION VARCHAR2, pPHRASE VARCHAR2 ) RETURN NUMBER AS
vRET NUMBER := 0;
vSTART NUMBER := 1;
BEGIN
LOOP
vSTART := INSTR(pEXPRESSION, pPHRASE, vSTART);
EXIT WHEN vSTART = 0; -- Если не найдено, выходим из цикла
vRET := vRET + 1; -- Увеличиваем счётчик
vSTART := vSTART + 1; -- Сдвигаем на 1 символ для поиска следующего вхождения
END LOOP;
RETURN vRET;
END;
Теперь ваша функция должна корректно считывать количество перекрывающихся совпадений. Используя INSTR
, вы избегаете необходимости в цикле с подстрокой, что значительно упрощает код.
Если вы используете эту функцию, проверьте результаты:
SELECT EXPRESSION_COUNT('336,14,3,3,11,0,', ',3,') FROM dual;
SELECT EXPRESSION_COUNT('bbaaaacc', 'aa') FROM dual;
Это должно вернуть Вам 2 и 3 соответственно, что соответствует ожидаемым значениям.
Ваш запрос для подсчета количества символов '-' в строке '123-345-566' выглядит корректно. Давайте разберем его по частям.
Вы используете функцию LENGTH
для определения длины исходной строки и длины строки без символов '-'. Вычитая вторую длину из первой, вы получаете количество символов '-', которые были удалены.
Вот ваш запрос:
SELECT LENGTH('123-345-566') - LENGTH(REPLACE('123-345-566', '-', '')) FROM DUAL;
LENGTH('123-345-566')
возвращает длину исходной строки, которая равна 11.REPLACE('123-345-566', '-', '')
заменяет все символы '-' на пустую строку, в результате чего получаем '123345566'.LENGTH(REPLACE('123-345-566', '-', ''))
возвращает длину строки без '-' — в данном случае это 9.- Вычитая 9 из 11, мы получаем 2.
Таким образом, ваш запрос вернет значение 2, что и является количеством символов '-' в строке '123-345-566'.
Если у вас есть дополнительные вопросы или вам нужна помощь с другими SQL-запросами, не стесняйтесь задавать!
Вы можете попробовать следующий запрос:
select count(distinct pos) from
(select instr('123-456-789', '-', level) as pos from dual
connect by level <= length('123-456-789'))
where nvl(pos, 0) != 0
Этот запрос корректно подсчитывает количество вхождений разделителя '-' в строке '123-456-789'.
Для подсчета количества вхождений подстроки 'aa' в строке 'bbaaaacc' можно использовать аналогичный запрос:
select count(distinct pos) from
(select instr('bbaaaacc', 'aa', level) as pos from dual
connect by level <= length('bbaaaacc'))
where nvl(pos, 0) != 0
Этот запрос подсчитает количество уникальных позиций, где встречается 'aa' в строке 'bbaaaacc'.
Получить строки с максимальным значением в одном столбце для каждого уникального значения другого столбца
Как выполнить сброс вывода из PL/SQL в Oracle?
Тип данных Oracle Timestamp
Как сравнить строки в SQL, игнорируя регистр?
Как использовать константу пакета в SQL-запросе SELECT?