REGEXP

한상우·2024년 10월 6일

SQL

목록 보기
8/8

1527. Patients With a Condition

Write a solution to find the patient_id, patient_name, and conditions ofthe patients
who have Type I Diabetes. Type I Diabetes always starts with DIAB1 prefix.
  • sample input
+------------+--------------+--------------+
| patient_id | patient_name | conditions   |
+------------+--------------+--------------+
| 1          | Daniel       | YFEV COUGH   |
| 2          | Alice        |              |
| 3          | Bob          | DIAB100 MYOP |
| 4          | George       | ACNE DIAB100 |
| 5          | Alain        | DIAB201      |
+------------+--------------+--------------+
  • sample output
+------------+--------------+--------------+
| patient_id | patient_name | conditions   |
+------------+--------------+--------------+
| 3          | Bob          | DIAB100 MYOP |
| 4          | George       | ACNE DIAB100 | 
+------------+--------------+--------------+

정규 표현식 (Regular Expression, regexp) 이란?

  • 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어이다. 정규 표현식은 많은 텍스트 편집기와 프로그래밍 언어에서 문자열의 검색과 치환을 위해 지원하고 있다.

  • 정규 표현식이라는 문구는 일치하는 텍스트가 준수해야 하는 "패턴"을 표현하기 위해 특정한 표준의 텍스트 신택스를 의미하기 위해 사용된다.


MySQL의 주요 정규표현식 패턴

  • Character Classes
패턴설명
[abc]'a', 'b', 'c' 중 하나를 매칭합니다.
[a-z]소문자 알파벳을 매칭합니다.
[^a-z]소문자 알파벳이 아닌 모든 문자를 매칭합니다.
[A-Z]대문자 알파벳을 매칭합니다.
[0-9]숫자를 매칭합니다.

  • Anchors
패턴설명
^문자열의 시작을 매칭합니다.
$문자열의 끝을 매칭합니다.
  • 예시:
    • ^word: "word"로 시작하는 문자열을 매칭합니다.
    • word$: "word"로 끝나는 문자열을 매칭합니다.

  • Operators
패턴설명
*0번 이상 반복을 매칭합니다.
+1번 이상 반복을 매칭합니다.
?0번 또는 1번 반복을 매칭합니다.
|두 표현식 중 하나를 매칭합니다(OR).
()그룹화하여 수량자를 적용합니다.
{n}정확히 n번 반복을 매칭합니다.
{n,}n번 이상 반복을 매칭합니다.
{n,m}n번에서 m번 사이의 반복을 매칭합니다.
  • 예시:
    • a*: 'a'가 0번 이상 반복되는 패턴 (예: "", "a", "aaa").
    • (ab)+: "ab"가 1번 이상 반복되는 패턴 (예: "ab", "abab").
    • colou?r: "colour" 또는 "color"를 매칭.

  • Metacharacters
패턴설명
.모든 단일 문자를 매칭합니다 (줄바꿈 제외).
\\s공백 문자 (space, tab, 줄 바꿈 등)를 나타냅니다.
\\b단어의 시작 또는 끝을 가리키며, 특정 단어가 독립적으로 존재하는지 확인합니다.
\\d숫자 ([0-9])를 나타냅니다.
\\w알파벳, 숫자 또는 밑줄 문자 ([A-Za-z0-9_])를 나타냅니다.
  • 예시:
    • a\\d+: 'a' 뒤에 하나 이상의 숫자를 매칭. (예: "a1", "a123").
    • \\bword\\b: "word"라는 단어가 독립적으로 존재하는지 매칭. (예: "word", "the word")

  • Predefined Character Classes
패턴설명
[:digit:]숫자 ([0-9])를 매칭합니다.
[:alpha:]알파벳 문자 ([A-Za-z])를 매칭합니다.
[:alnum:]알파벳이나 숫자 ([A-Za-z0-9])를 매칭합니다.
[:space:]공백 문자 (공백, 탭, 줄바꿈 등)를 매칭합니다.
  • 예시:
    • [[:digit:]]+: 하나 이상의 숫자를 매칭. (예: "123")
    • [[:alpha:]]{3}: 알파벳 문자 3개를 매칭. (예: "abc")
    • [[:alnum:]]*: 알파벳이나 숫자가 0번 이상 나오는 패턴을 매칭. (예: "", "abc123")
    • [[:space:]]: 공백 문자를 매칭. (예: " ", "\t")

정규 표현식을 사용한 문제 해결

  • 문제에서 찾고 싶은 패턴은 DIAB1 이라는 prefix를 찾아내는 것이다.

    • pattern 1 : condition의 시작에 DIAB1 이 있는 경우이다.

      • ex) DIAB100 MYOP
    • pattern 2 : condition의 중간에 (즉, 공백 뒤에) DIAB1 이 있는 경우이다.

      • ex) ACNE DIAB100

  • 다양한 패턴을 적용해 해당 문제를 해결해 보자

  • 1) 문자열의 시작(^)과 공백을 OR로 그룹화하여 패턴을 만든다

SELECT *
FROM Patients
WHERE conditions REGEXP '(^| )DIAB1';
  • 2) pattern 1과 pattern 2를 OR로 묶어준다
SELECT *
FROM Patients
WHERE conditions REGEXP '^DIAB1|\\sDIAB1'
  • 3) 단어 경계(\b)를 사용하여 pattern 1과 pattern 2을 모두 포함하는 패턴을 만든다
SELECT * 
FROM Patients 
WHERE conditions REGEXP '\\bDIAB1'

어려운데 LIKE 쓰면 안되나요?

  • 위 문제는 실제로 LIKE를 사용해도 잘 풀리고, LeetCode에서의 풀이도 대부분 아래와 같이 작성했다.
SELECT * 
FROM Patients
WHERE conditions LIKE 'DIAB1%' OR conditions LIKE '% DIAB1%'
  • 더 복잡한 문제를 같이 확인해 보며 정규 표현식의 장점에 대해 알아보자

1517. Find Users With Valid E-Mails

Write a solution to find the users who have valid emails.

A valid e-mail has a prefix name and a domain where:

1. The prefix name is a string that may contain letters (upper or lower case), digits, 
underscore '_', period '.', and/or dash '-'. The prefix name must start with a letter.

2. The domain is '@leetcode.com'.
  • sample input
+---------+-----------+-------------------------+
| user_id | name      | mail                    |
+---------+-----------+-------------------------+
| 1       | Winston   | winston@leetcode.com    |
| 2       | Jonathan  | jonathanisgreat         |
| 3       | Annabelle | bella-@leetcode.com     |
| 4       | Sally     | sally.come@leetcode.com |
| 5       | Marwan    | quarz#2020@leetcode.com |
| 6       | David     | david69@gmail.com       |
| 7       | Shapiro   | .shapo@leetcode.com     |
+---------+-----------+-------------------------+
  • sample output
+---------+-----------+-------------------------+
| user_id | name      | mail                    |
+---------+-----------+-------------------------+
| 1       | Winston   | winston@leetcode.com    |
| 3       | Annabelle | bella-@leetcode.com     |
| 4       | Sally     | sally.come@leetcode.com |
+---------+-----------+-------------------------+

  • 1517번 문제는 찾아야 하는 패턴이 다양하고 복잡하기 때문에 LIKE를 사용하여 풀기에는 어려워 보인다.

  • 정규 표현식을 사용한 풀이는 다음과 같다.

SELECT *
FROM Users
WHERE mail REGEXP '^[a-zA-Z][a-zA-Z0-9_.-]*@leetcode\\.com$'
  • 정규 표현식을 나눠서 살펴보자.

    • ^[a-zA-Z] : The prefix name must start with a letter.

    • [a-zA-Z0-9_.-]* : prefix name is a string that may contain letters (upper or lower case), digits, underscore '_', period '.', and/or dash '-'

      • prefix의 첫번째 문자 이후 올 수 있는 문자들의 패턴이다. '*' operator를 통해 해당 패턴이 0번 이상 반복하는 경우를 매칭한다.
    • @leetcode\\.com$ : The domain is '@leetcode.com'.

      • 마지막에 도메인 주소가 일치하는지 확인하는데, '.'는 정규 표현식에서 특수 문자에 해당하기 때문에 백슬래쉬(escape character)를 추가해준다.

REGEXP와 다른 문자열 매칭 함수의 비교

  • REGEXPLIKEINSTR과 같은 다른 문자열 매칭 함수들보다 복잡하고 유연한 매칭을 지원하는 것이 큰 장점이다.

  • REGEXP는 기능적으로는 모든 매칭을 커버할 수 있지만, 간단한 패턴(single condition)의 경우는 LIKEREGEXP보다 빠르다.

  • 반대로, 복잡한 패턴(multiple condition)의 경우는 REGEXPLIKE보다 빠르다.

  • 따라서, 쿼리에서 찾고자 하는 문자열 패턴의 복잡도를 고려하여 적절한 함수를 사용하는 것이 좋다.

profile
개인 공부용 블로그입니다

0개의 댓글