[ORACLE] 향상된 데이터 조회를 위한 정규표현식과 계층화 쿼리 2
향상된 데이터 조회를 위한 정규표현식과 계층화 쿼리 2
정규 표현식(Regular Expression)
- 정규 표현식
- 주어진 문장 패턴과 매치시킬 수 있는 어떤 표현을 기술할 수 있는 기호와 원소들의 집합으로 매우 강력하고 실용적인 패턴 매치 기법
- POSIX 표준 문법을 따름
- 다양한 매치와 검색을 정의하는 메타기호(메타캐릭터)들의 조합으로 구성
- 대소문자를 구분
When?
- ETL (Extract, Transform, Load)
- Data Mining(누적된 정보를 통한 인과관계/통계 분석)
- Data Cleansing(정제)
- Data Validation(검증)
- 제약조건을 이용한 테이블 Data 유효성 검증
What?
- 중복 단어의 확인
- 특별한 상태에서 공백의 제거
- 문자의 파싱
- Text에서 전화번호, 우편번호, 이메일 주소, 주민등록번호, IP주소, 파일이름, 경로명 등을 검증 및 추출
- HTML 태그, 숫자, 날짜, 기타 특정 텍스트 데이터와 일치하는 패턴을 확인하고 다른 패턴으로 대체하는 것이 가능
정규표현식 함수
함수 명 | 함수 설명 |
---|---|
REGEXP_LIKE | Like 연산과 유사하며 정규식 패턴을 검색 |
REGEXP_REPLACE | 정규식 패턴을 검색하여 대체 문자열로 변경 |
REGEXP_INSTR | 정규식 패턴을 검색하여 위치 반환 |
REGEXP_SUBSTR | 정규식 패턴을 검색하여 부분 문자 추출 |
REGEXP_COUNT | 정규식 패턴을 검색하여 발견된 횟수 반환 |
REGEXP_INSTR
과 REGEXP_SUBSTR
을 조합하면 특정 위치의 데이터를 뽑을 수도 있다.
정규표현식 메타 캐릭터
메타 캐릭터 | 기능 |
---|---|
^ | 문자열의 시작 부분과 일치 |
$ | 문자열의 끝 부분과 일치 |
* | 0개 이상(zero or more) 일치 |
+ | 하나 이상(one or more) 일치 |
? | 0 또는 1개 일치(zero or one) |
. | NULL을 제외한 모든 문자와 일치 |
| | OR |
[ ] | [ ]에 있는 문자 중 하나를 일치시키려는 목록을 지정 |
[^ ] | [^ ]에 있는 문자를 제외한 모든 문자를 일치시키려는 리스트 지정 |
( ) | 표현식을 하위 표현식으로 그룹화하는데 사용 |
{m} | m번 일치 |
{m, } | 적어도 m번 일치 |
{m, n} | 적어도 m번 일치 ~ n번 이하 일치 |
\n | ()번째 하위 표현식과 일치 |
[..] | 하나 이상의 문자가 될 수 있는 하나의 집합요소와 일치 |
[::] | 문자 클래스와 일치. [:digit:], [:punct:], [:alpha:] 등 |
[==] | equivalence 클래스와 일치 |
메타 캐릭터 | 기능 |
---|---|
\d | 숫자와 일치 |
\D | 숫자가 아닌 것과 일치 |
\w | 영문자 숫자 밑줄문자(_)와 일치 |
\W | 영문자 숫자 밑줄문자(_) 이외의 문자와 일치 |
\s | 공백문자와 일치 |
\S | 공백이 아닌 문자와 일치 |
\A | 문자열의 시작 부분 일치, 새 행의 문자 앞에 있는 문자열의 끝 부분과 일치 |
\Z | 문자열의 끝과 일치 |
*? | 앞선 패턴이 0번 이상 발생 |
+? | 앞선 패턴이 한 번 이상 발생 |
?? | 앞선 패턴이 0번 또는 1번 발생 |
{n}? | 앞선 패턴이 n번 일치 |
{n, }? | 앞선 패턴이 적어도 n번 일치 |
{n,m}? | 앞선 패턴이 적어도 n번 ~ m번 이하 일치 |
오라클 공식 Docs, Ref를 보고 확인, 활용하도록 하자.
정규표현식 함수 : REGEXP_LIKE()
- like 연산자의 정규 표현식 적용 함수
REGEXP_LIKE(해당문자열, 정규식패턴 [, 매칭매개변수옵션])
-- string 문자열
-- 검색될 문자열
-- pattern 정규식 패턴
-- 비교할 정규식 패턴을 나타내는 리터럴 문자열
i
: 대소문자 무관하게 처리c
: 대소문자 구분 (기본값)n
: 마침표(.) 문자가 새 라인과 일차하도록 허용m
: 소스 문자열을 앵커 문자(^$)를 활성화하는 여러 줄로 처리 x
: 공백 문자 무시
예시를 보고 익혀보도록 하자.
사원명에서 “n”으로 끝나는 모든 사원명 출력
SELECT. last_name FROM. employees WHERE. REGEXP_LIKE(last_name, '(*)n$'); -- n으로 끝나는 모든 글자를 반환
LAST_NAME 1 De Haan 2 Hartstein 3 Whalen 업무에서 “S”로 시작하는 모든 업무 출력
SELECT. job_id FROM. employees WHERE. REGEXP_LIKE(job_id, '^S(*)');
JOB_ID 1 SA_MAN 2 SA_REP 3 SA_REP 4 SA_REP 5 ST_CLERK 6 ST_CLERK 7 ST_CLERK 8 ST_CLERK 9 ST_MAN 이름이 “Ste”로 시작하는데 “v”나 “ph”를 포함하며 “en”으로 끝나는 모든 이름과 성을 출력
SELECT. first_name, last_name FROM. hr.employees WHERE. REGEXP_LIKE(first_name, '^Ste(v|ph)en$');
FIRST_NAME LAST_NAME 1 Steven King 2 Steven Markle 3 Stephen Stiles EMPLOYEES
에 대한 이 query에서는first_name
에 Steven 또는 Stephen이 포함된 모든 사원 정보를 표시한다.^
는 표현식의 시작$
는 표현식의 끝|
는 둘 중 하나(OR)
정규표현식 함수 : REGEXP_INSTR()
- 지정된 정규식 패턴을 문자열에서 검색하고 일치 항목이 있는 문자열의 위치(숫자값)를 반환하고, 미일치시 0을 반환
- 처음 문자의 위치는 1
REGEXP_INSTR(해당문자열, 정규식패턴 [, 시작위치
[, 일치횟수 [, 반환할 문자위치 [, 매칭 파라미터]]]])
마찬가지로 예시를 통해 이해해보자.
SELECT. street_address,
REGEXP_INSTR(street_address, '[^[:alpha:]]')
FROM. hr.locations
WHERE. REGEXP_INSTR(street_address, '[^[:alpha:]]') > 1;
STREET_ADDRESS | REGEXP_INSTR(STREET_ADDRESS.’[^[:ALPHA:]]’) | |
---|---|---|
1 | Magdalen Centre, The Oxford Science Park | 9 |
2 | Schwanthalerstr. 7031 | 16 |
3 | Rua Frei Caneca 1360 | 4 |
4 | Murtenstrasse 921 | 14 |
… | … | … |
REGEXP_INSTR
함수로 주소를 검색하여 문자가 아닌(^
) 첫 번째 알파벳 문자의 위치를 찾는다.
[:alpha:]
는 임의의 알파벳 문자와 일치한다.
[
는 표현식을 시작^
는 부정 의미[:alpha:]
는 알파벳 문자 클래스 표시]
는 표현식을 종료
정규표현식 함수 : REGEXP_SUBSTR()
REGEXP_SUBSTR(COLUMN, [REG_EXP], [START_INDEX], [GROUP_INDEX])
COLUMN
: 칼럼명REG_EXP
: 정규표현식 작성START_INDEX
: 해당 정규표현식을 검색할 문자열의 INDEX 지정GROUP_INDEX
: 해당 정규표현식으로 잘라진 그룹이 2개 이상이라면 INDEX 지정해 그룹 선택
SELECT last_name,
REGEXP_SUBSTR(last_name, 'a|e|i|o|u', 1, 1, 'i')
AS "첫 모음"
FROM. employees;
LAST_NAME | 첫 모음 | |
---|---|---|
1 | Abel | A |
2 | Davies | a |
3 | De Haan | e |
4 | Ernst | E |
5 | Fay | a |
6 | Gietz | i |
… | … | … |
last_name
에서 첫 모음을 대소문자 무관하게 출력한다.
i
를 옵션으로 지정했기 때문에 대소문자에 무관하게 값이 출력된다.
SELECT. street_address,
REGEXP_SUBSTR(street_address, ' [^ ]+ ') AS "Road"
FROM. locations;
STREET_ADDRESS | Road | |
---|---|---|
1 | 2014 Jabberwocky Rd | Jabberwocky |
2 | 2011 Interiors Blvd | Interiors |
3 | 2004 Charade Rd | Charade |
… | … | … |
LOCATIONS
테이블에서 road name 추출.
이를 위해 REGEXP_SUBSTR
함수를 사용하여 STREET_ADDRESS
열에서 첫 번째 공백 뒤에 있는 내용을 반환.
[
는 표현식을 시작^
는 NOT을 나타냄- 공백을 나타냄
]
는 표현식을 종료+
는 1 이상을 나타냄- 공백을 나타냄
SELECT. street_address,
REGEXP_INSTR(street_address, '[^ ]+', 1, 3) AS REGEXP_INSTR,
REGEXP_SUBSTR(street_address, '[^ ]+' 1, 3) AS REGEXP_SUBSTR
FROM. locations
WHERE. REGEXP_INSTR(street_address, '[^ ]+', 1, 3) > 1;
- 공백이 아닌 세 번째 단어를 검색하기 위해 1~3까지의 단어 중 세번째에 해당하는 단어를 출력한다. (단어 2개의 행은 제외)
STREET_ADDRESS | REGEXP_INSTR | REGEXP_SUBSTR | |
---|---|---|---|
1 | 2014 Jabberwocky Rd | 18 | Rd |
2 | 2011 interiors Blvd | 16 | Blvd |
3 | 2004 Charade Rd | 14 | Rd |
정규표현식 메타 캐릭터 : REGEXP_REPLACE()
REGEXP_REPLACE(해당문자열, 정규식패턴, [, 대체문자열 [, 시작위치
[, 일치횟수 [, 매칭매개변수]]]])
replacement_string
: 대체문자열, 해당 문자여로가 패턴이 일치할 때 대체될 문자. 생략할 시 패턴에 일치하는 문자가 제거됨Start_position
: 시작위치, 검색 시작 위치를 지정. 기본값 = 1.nth_appearance
: 일치횟수, n번째 일치된 문자열 대체. 기본값 = 0. 모든 값 대체.- “Oracle Database” 에서 a, e, i, o, u 를 첫 번째에서 찾아 ‘Z’로 변경
SELECT. REGEXP_REPLACE('Oracle Database', 'a|e|i|o|u', 'Z', 1, 0, 'i')
AS. REP
FROM. dual;
REP | |
---|---|
1 | ZrZclZ DZtZbZsZ |
- 대상 열에서 문자열에 포함된 숫자를 모두 ‘*’ 로 변경
SELECT. street_address, REGEXP_REPLACE(street_address, '\d', '*')
AS. "Road"
FROM. locations;
STREET_ADDRESS | Road | |
---|---|---|
1 | 2014 Jabberwocky Rd | ** Jabberwocky Rd |
2 | 2011 Interiors Blvd | ** Interiors Blvd |
3 | 2004 Charade Rd | ** Charade Rd |
- 전화번호에서 ‘.’ 을 ‘-’로 변경
SELECT. last_name, REGEXP_REPLACE(phone_number, '\.', '-')
AS. phone
FROM. employees;
| | LAST_NAME | PHONE_NUMBER | | — | — | — | | 1 | OConnell | 650-507-9833 | | 2 | Grant | 650-507-9844 | | 3 | Whalen | 515-123-4444 | | 4 | Hartstein | 515-123-5555 |
- 마침표(
.
) 구분자를 대시(-
) 구분자로 대체하도록 전화번호 형식을 다시 지정할 수 있다. phone_number
는 소스 열\.
는 검색 패턴- 작은 따옴표(
’ ‘
)를 사용하여 리터럴 문자 마침표(.
)를 검색 - 백슬래시(
\
)를 사용하여 일반적으로 메타 문자로 처리되는 문자를 검색 -
는 대체 문자열
정규표현식 메타 캐릭터 : REGEXP_COUNT()
- 문자열 중에 매칭되는 문자열 또는 패턴이 몇 번 나오는지 횟수 출력
REGEXP_COUNT(source_char, pattern [, position [, occurrence [, match_option]]])
- 대상 문자열에서 ‘a’ 글자는 몇 번 반복될까 ?
SELECT. REGEXP_COUNT('Oracle Database', 'a') AS REP
FROM. dual;
- 지정한 전화번호 패턴과 일치하면 1, 아니면 0 출력
SELECT. phone_number,
REGEXP_COUNT(phone_number,
'([[:digit:]]{3})\.([[:digit:]]{3})\.([[:digit:]]{4})')
AS. phone_format
FROM. employees;
PHONE_NUMBER | PHONE_FORMAT | |
---|---|---|
1 | 650.121.8009 | 1 |
2 | 650.121.2994 | 1 |
3 | 650.121.2874 | 1 |
4 | 011.44.1344.429018 | 0 |
정규표현식 활용 1
Email 주소 분리 = ID Domain - REGEXP_SUBSTR(소스 문자열, Pattern [, 위치 [, 발생 횟수 [, Match를 시도할 때의 옵션]]])
- @를 기준으로 @가 아닌 앞 글자와 뒷글자 출력
SELECT. REGEXP_SUBSTR(email, '[^@]+', 1, 1) AS "ID",
REGEXP_SUBSTR(email, '[^@]+', 1, 2) AS "MailAddr"
FROM. (SELECT '&input_email' email FROM dual);
[대체 변수 입력] input_email에 대한 값 입력 ::
대체 변수로 사용자가 이메일을 입력할 수 있다.
첫 번째 정규표현식에 의해 @
를 기준으로 앞글자를 출력하고,
두 번째 정규표현식에 의해 @
를 기준으로 뒷글자를 출력한다.
ID | MailAddr | |
---|---|---|
1 | heoj10272 | gmail.com |
정규표현식 활용 2
- 주민번호 Masking
- REGEXP_REPLACE(소스 문자열, Pattern [, 바꿀 문자열 [, 위치 [, 발생횟수 [Match 파라미터]]]])
SELECT. REGEXP_REPLACE(SSN, '[0-9]', '*', 7) REG_REP
FROM. (SELECT '&jaeyoung' AS SSN FROM dual);
[대체 변수 입력] jaeyoung에 대한 값 입력 ::
내 주민번호를 입력하면, 마스킹되어 저장된다.
들어온 문자열에서 7번째 까지만 숫자로 출력하고, 나머지는 *
로 바꾼다.
정규표현식 활용 3
- 전화번호 패턴 만들어 저장하기
SELECT. REGEXP_REPLACE('010.1234.1234', '([[:digit:]]{3})\.([[:digit:\\{4})\.
([[:digit:]]{4})','(\1) \2 - \3')
AS. "Result1"
FROM. dual;
Result1 | |
---|---|
1 | (010) 1234 - 1234 |
- 생년월일 패턴 만들어 저장하기
SELECT. REGEXP_REPLACE('&생년월일_YYYYMMDD',
'([[:digit:]]{4})([[:digit:]]{2})([[:digit:]]{2})',
'\1 년 \2 월 \3 일')
FROM. dual;
REGEXP_REPLACE(’20050819’, ‘([[:DIGIT:]]{4})… | |
---|---|
1 | 2005 년 08 월 19 일 |