Достаточно часто в строке требуется найти заданный фрагмент текста. Начиная с Oracle10g, для подобных манипуляций с текстом могут использоваться регулярные выражения. Если вы еще не перешли на Oracle10g или более позднюю версию, существует решение, совместимое со старыми версиями базы данных. Функция INSTR
возвращает позицию подстроки в большей строке. Следующий фрагмент находит позиции всех запятых в списке имен:
DECLARE
names VARCHAR2(60) := 'Anna,Matt,Joe,Nathan,Andrew,Aaron,Jeff';
comma_location NUMBER := 0;
BEGIN
LOOP
comma_location := INSTR(names,',',comma_location+1);
EXIT WHEN comma_location = 0;
DBMS_OUTPUT.PUT_LINE(comma_location);
END LOOP;
END;
Результат:
5
10
14
21
28
34
Первый аргумент INSTR
содержит строку, в которой проводится поиск. Второй аргумент задает искомую подстроку — в данном случае это запятая. Третий аргумент задает позицию, в которой начинается поиск. После каждой найденной запятой цикл продолжает поиск с символа, следующего за найденным. Если совпадения отсутствуют, INSTR
возвращает ноль, и цикл на этом завершается.
Следующий естественный шаг после обнаружения текста в строке — его извлечение. Естественно, нас интересуют не запятые, а те имена, которые они разделяют. Для извлечения строковых данных применяется функция SUBSTR
:
DECLARE
names VARCHAR2(60) := 'Anna,Matt,Joe,Nathan,Andrew,Aaron,Jeff';
names_adjusted VARCHAR2(61);
comma_location NUMBER := 0;
prev_location NUMBER := 0;
BEGIN
-- Включение запятой за последним именем
names_adjusted := names || ',';
LOOP
comma_location := INSTR(names_adjusted,',',comma_location+1);
EXIT WHEN comma_location = 0;
DBMS_OUTPUT.PUT_LINE(
SUBSTR(names_adjusted,
prev_location+1,
comma_location-prev_location-1));
prev_location := comma_location;
END LOOP;
END;
Программа выдает следующий список имен:
Anna
Matt
Joe
Nathan
Andrew
Aaron
Jeff
В приведенном коде следует обратить внимание на два ключевых момента. Во-первых, в конец строки добавляется запятая, упрощающая реализацию логики цикла. Теперь за каждым именем в names_adjusted
следует запятая; это упрощает нашу задачу. Во-вторых, каждый раз, когда цикл доходит до вызова DBMS_OUTPUT
.PUT_LINE
, в переменных prev_location
и comma_location
сохраняются позиции символов по обе стороны от выводимого имени. В этом случае все сводится к простым вычислениям и вызову функции SUBSTR
, которая получает три аргумента:
names_adjusted
— строка, из которой извлекается имя;prev_location
+1 — позиция первой буквы имени. Вспомните, что в переменнойprev_location
хранится позиция символа, непосредственно предшествующего выводимому имени (обычно запятой). Эта позиция увеличивается на единицу.comma_location
-prev_location
-1 — количество извлекаемых символов. Вычитание единицы предотвращает вывод завершающей запятой.