Lina's Toolbox

SQL 문자 데이터 가공하기 / replace, substring(substr), concat, if, case 본문

스파르타 내일 배움 캠프 AI 웹개발 과정/Database & SQL

SQL 문자 데이터 가공하기 / replace, substring(substr), concat, if, case

Woolina 2024. 8. 7. 02:15

 

 

데이터를 조회하다보면, Query 결과를 그대로 이용하지 못하는 경우가 있어요.

아마 실습을 하면서 아래의 경우를 한 번쯤은 생각해봤을 텐데요, 한 번 각각의 케이스와 해결 방법을 알아봅시다.

  • 데이터를 보니 잘못된 값이 있어요. 이전에 사용하던 값이어서 다른 문자로 수정을 해줘야하는데, 하나하나 수동으로 하기는 너무 많아서 SQL 로 바꿀 수 있을까요?
  • 저는 주소 전체가 아닌 ‘시도’ 정보만 필요해요. 서울의 통계만 구하고 싶은데, 전체 주소가 아닌 ‘서울’ 로 문자를 변경할 수는 없을까요?
  • 저는 보고서를 작성할 때 사업장 명과 함께 지역이 같이 나와야해요. ‘사업장 [지역]’ 과 같은 형태로 문자 포맷을 변경할 수 있을까요?

특정 문자를 다른 문자로 바꾸기 (REPLACE)

사용방법

replace(바꿀 컬럼, 현재 값, 바꿀 값)

 

  • 실습: 식당 명의 ‘Blue Ribbon’ 을 ‘Pink Ribbon’ 으로 바꾸기
select restaurant_name "원래 상점명",
       replace(restaurant_name, 'Blue', 'Pink') "바뀐 상점명"
from food_orders
where restaurant_name like '%Blue Ribbon%'

 

  • 주소의 ‘문곡리’ 를 ‘문가리’ 로 바꾸기
select addr "원래 주소",
       replace(addr, '문곡리', '문가리') "바뀐 주소"
from food_orders
where addr like '%문곡리%'


원하는 문자만 남기기 substring(substr)

전체 데이터가 아닌 특정 문자만 필요할 때, SQL 로 필요한 부분만 조회할 수 있습니다.

  • SQL 에서는 특정 문자만 골라서 조회할 수 있는 기능을 제공합니다
  • 예시) 전체 주소에서 앞부분인 ‘시도’ 부분만 필요해요
  • 함수명 : substring (substr)
  • 사용 방법
substr(조회 할 컬럼, 시작 위치, 글자 수)

 

  • 실습: 서울 음식점들의 주소를 전체가 아닌 ‘시도’ 만 나오도록 수정
select addr "원래 주소",
       substr(addr, 1, 2) "시도"
from food_orders
where addr like '%서울특별시%'

addr의 1번째 글자부터 2번째 글자까지 뽑는 것!

(6부터였다면 공백, 7번째부터는 종로구의 "종" 부터 출력 되었을 것.)


여러 컬럼의 문자를 합치기 (concat)

원하는 문자가 여러 컬럼에 있을 때, 하나로 합쳐서 업무에 필요한 형태로 만들 수 있습니다.

 

  • 사용방법
concat(붙이고 싶은 값1, 붙이고 싶은 값2, 붙이고 싶은 값3, .....)

 

  • 일 수 있는 문자의 종류
    • 컬럼
    • 한글
    • 영어
    • 숫자
    • 기타 특수문자
  • 실습: 서울시에 있는 음식점은 ‘[서울] 음식점명’ 이라고 수정
select restaurant_name "원래 이름",   
       addr "원래 주소",
       concat('[', substring(addr, 1, 2), '] ', restaurant_name) "바뀐 이름"
from food_orders
where addr like '%서울%'


예제

1. 서울 지역의 음식 타입별 평균 음식 주문금액 구하기 (출력 : ‘서울’, ‘타입’, ‘평균 금액’) 

select substring(addr, 1, 2) "시도",
       cuisine_type "음식 종류",
       avg(price) "평균 금액"
from food_orders
where addr like '%서울%'
group by 1, 2

group by, order by 기준이될 컬럼명을 다 적어줄 필요 없이, 첫번째 컬럼 ➡️ 1, 두번째 컬럼 ➡️ 2 이런식으로 적어줘도 된다!

실행결과

 

2. 이메일 도메인별 고객 수와 평균 연령 구하기

* 이 문제에서는 @ 앞에 오는 글자수가 8개로 정해져있다. ➡️ 도메인은 @를 제외하면 10번째 글자부터 출력하는 것!

select substring(email, 10) "도메인",
       count(customer_id) "고객 수",
       avg(age) "평균 연령"
from customers
group by 1

substring을 10번째 글자부터 끝까지 출력하고 싶은 경우엔, 3번째 필드를 이렇게 적지 않으면 된다!

실행결과

 

3. ‘[지역(시도)] 음식점이름 (음식종류)’ 컬럼을 만들고, 총 주문건수 구하기

select concat('[', substring(addr, 1, 2), '] ', restaurant_name, ' (', cuisine_type, ')') "바뀐이름",
       count(1) "주문건수"
from food_orders
group by 1


조건에 따라 포맷을 다르게 변경해야한다면 (IF, CASE)

조건에 따라 다른 연산을 하는 방법을 알아봅시다.

범주별로 값을 구할 때는 group by 를 썼죠.
범주별로 다른 연산 (계산, 문자 바꾸기) 을 적용할 수도 있을까요?

SQL 은 조건에 따라 연산을 적용할 수 있는 기능을 제공합니다.
’내가 원하는 범주’ 를 조건으로 주고, 해당 범주에 적용하고 싶은 것을 지정해 주는 방식입니다.

 

💡 개념을 이해하기 어렵다면 아래의 예시를 참고해봅시다

  • 음식 타입을 ‘Korean’ 일 때는 ‘한식’, ‘Korean’ 이 아닌 경우에는 ‘기타’ 라고 지정하고 싶어요
  • 주소의 시도를 ‘경기도’ 일때는 ‘경기도’, 아닐 때는 앞의 두 글자만 사용하고 싶어요
  • 음식 단가를 주문 수량이 1일 때는 음식 가격, 주문 수량이 2개 이상일 때는 음식가격/주문수량 으로 지정하고 싶어요 

 

조건에 따라 다른 방법을 적용하고 싶을 때 - If 문 기초 

조건을 지정해주는 가장 기초 문법은 ‘If’ 문입니다 (엑셀의 기능과 유사합니다)

  • IF 문은 원하는 조건에 충족할 때 적용할 방법과 아닌 방법을 지정해 줄 수 있습니다
  • 예시) 음식 타입을 ‘Korean’ 일 때는 ‘한식’, ‘Korean’ 이 아닌 경우에는 ‘기타’ 라고 지정하고 싶어요
  • 함수명 : if
  • 사용 방법
if(조건, 조건을 충족할 때, 조건을 충족하지 못할 때)

 

 

실습1: 음식 타입을 ‘Korean’ 일 때는 ‘한식’, ‘Korean’ 이 아닌 경우에는 ‘기타’ 라고 지정

select restaurant_name,
       cuisine_type "원래 음식 타입",
       if(cuisine_type='Korean', '한식', '기타') "음식 타입"
from food_orders

 

 

 

실습2: 02. 번 실습에서 ‘문곡리’ 가 평택에만 해당될 때, 평택 ‘문곡리’ 만 ‘문가리’ 로 수정

select addr "원래 주소",
       if(addr like '%평택군%', replace(addr, '문곡리', '문가리'), addr) "바뀐 주소"
from food_orders
where addr like '%문곡리%'

 

 

실습3: 03. 번 실습에서 잘못된 이메일 주소 (gmail) 만 수정을 해서 사용

select substring(if(email like '%gmail%', replace(email, 'gmail', '@gmail'), email), 10) "이메일 도메인",
       count(customer_id) "고객 수",
       avg(age) "평균 연령"
from customers
group by 1


조건을 여러가지 지정하고 싶을 때 - Case 문 기초 

조건을 지정하다보면, 두 개 이상 지정을 해야 할 경우가 생깁니다.

이 때는 case 문을 이용하여 여러번의 if 문을 적용 한 효과를 낼 수 있습니다.

  • Case 문은 각 조건별로 적용 할 값을 지정해 줄 수 있습니다.
  • 조건별로 지정을 해주기 때문에 아래와 같이 if 문을 여러번 쓴 효과를 낼 수 있습니다. case(조건1, 값1, case(조건2, 값2, 값3))
  • 예시) 음식 타입을 ‘Korean’ 일 때는 ‘한식’, ‘Japanese’ 혹은 ‘Chienese’ 일 때는 ‘아시아’, 그 외에는 ‘기타’ 라고 지정
  • 함수명 : case
  • 사용 방법
case when 조건1 then 값(수식)1
     when 조건2 then 값(수식)2
     else 값(수식)3
end

 

 

[실습1] 음식 타입을 ‘Korean’ 일 때는 ‘한식’, ‘Japanese’ 혹은 ‘Chienese’ 일 때는 ‘아시아’, 그 외에는 ‘기타’ 라고 지정

select restaurant_name,
       cuisine_type AS "원래 음식 타입",
       case when (cuisine_type='Korean') then '한식'
       else '기타'
       end as " 음식 타입"
from food_orders

 

 

[실습2] 음식 단가를 주문 수량이 1일 때는 음식 가격, 주문 수량이 2개 이상일 때는 음식가격/주문수량 으로 지정

select order_id,
       price,
       quantity,
       case when quantity=1 then price
            when quantity>=2 then price/quantity end "음식 단가"
from food_orders

 

[실습3] 주소의 시도를 ‘경기도’ 일때는 ‘경기도’, ‘특별시’ 혹은 ‘광역시’ 일 때는 붙여서, 아닐 때는 앞의 두 글자만 사용

select restaurant_name,
       addr,
       case when addr like '%경기도%' then '경기도'
            when addr like '%특별%' or addr like '%광역%' then substring(addr, 1, 5)
            else substring(addr, 1, 2) end "변경된 주소"
from food_orders


조건을 사용할 수 있는 경우

  • 새로운 카테고리 만들기
    • 음식 타입과 같이 새로운 카테고리를 만들 수 있습니다.
      • 한국 음식, 아시아 음식, 미국 음식, 유럽 음식 이런 식의 새로운 cuisine_category 를 생성할 수 있죠
    • 고객들의 분류도 만들 수 있습니다.
      • 10대 여성, 10대 남성, 20대 여성, 20대 남성 등, 이런 식의 성별과 나이별로 새로운 고객 군 카테고리를 생성할 수 있죠
  • 연산식을 적용할 조건 지정하기
    • 수수료를 계산할 때 흔히들 현금 사용, 카드사용을 나누고는 하죠
    • 현금일 때의 수수료율과 카드일 때의 수수료율이 다르다면, 연산식을 만들 때 if 문 혹은 case 문으로 각각 다른 수수료율 혹은 수수료 계산 방식을 적용할 수 있습니다
  • 다른 문법 안에서 적용하기
    • if, case 문 안에 다른 문법이나 연산을 넣을 수도 있지만, 다른 문법 안에 조건문을 넣을 수도 있습니다
    • 예를 들어 concat 문으로 여러 컬럼을 합칠 때, rating 이 있을 때는 rating 을 넣어주고 없을 때는 아무것도 넣지 않도록, concat 안에 if 문을 넣어줄 수 있죠

[예제] SQL로 간단한 User Segmentation 해보기

[실습] 10세 이상, 30세 미만의 고객의 나이와 성별로 그룹 나누기 (이름도 같이 출력)

select name,
       age,
       gender,
       case when (age between 10 and 19) and gender='male' then "10대 남자"
            when (age between 10 and 19) and gender='female' then "10대 여자"
            when (age between 20 and 29) and gender='male' then "20대 남자"
            when (age between 20 and 29) and gender='female' then "20대 여자" end "그룹" 
from customers
where age between 10 and 29

 

[실습] 음식 단가, 음식 종류 별로 음식점 그룹 나누기

select restaurant_name,
       price/quantity "단가",
       cuisine_type,
       order_id,
       case when (price/quantity <5000) and cuisine_type='Korean' then '한식1'
            when (price/quantity between 5000 and 15000) and cuisine_type='Korean' then '한식2'
            when (price/quantity > 15000) and cuisine_type='Korean' then '한식3'
            when (price/quantity <5000) and cuisine_type in ('Japanese', 'Chinese', 'Thai', 'Vietnamese', 'Indian') then '아시아식1'
            when (price/quantity between 5000 and 15000) and cuisine_type in ('Japanese', 'Chinese', 'Thai', 'Vietnamese', 'Indian') then '아시아식2'
            when (price/quantity > 15000) and cuisine_type in ('Japanese', 'Chinese', 'Thai', 'Vietnamese', 'Indian') then '아시아식3'
            when (price/quantity <5000) and cuisine_type not in ('Korean', 'Japanese', 'Chinese', 'Thai', 'Vietnamese', 'Indian') then '기타1'
            when (price/quantity between 5000 and 15000) and cuisine_type not in ('Korean', 'Japanese', 'Chinese', 'Thai', 'Vietnamese', 'Indian') then '기타2'
            when (price/quantity > 15000) and cuisine_type not in ('Korean', 'Japanese', 'Chinese', 'Thai', 'Vietnamese', 'Indian') then '기타3' end "식당 그룹"
from food_orders


[예제] 조건문으로 서로 다른 식을 적용한 수수료 구해보기

[실습] 지역과 배달시간을 기반으로 배달수수료 구하기 (식당 이름, 주문 번호 함께 출력)

select restaurant_name,
       order_id,
       delivery_time,
       price,
       addr,
       case when delivery_time>25 and delivery_time<=30 then price*0.05*(if(addr like '%서울%', 1.1, 1))
            when delivery_time>30 then price*1.1*(if(addr like '%서울%', 1.1, 1))
            else 0 end "수수료"
from food_orders

 

[실습] 주문 시기와 음식 수를 기반으로 배달할증료 구하기

select order_id,
       price,
       quantity,
       day_of_the_week,
       if(day_of_the_week='Weekday', 3000, 3500)*(if(quantity<=3, 1, 1.2)) "할증료"
from food_orders

 

다음의 조건으로 배달시간이 늦었는지 판단하는 값을 만들어주세요.
- 주중 : 25분 이상
- 주말 : 30분 이상
select order_id,  
       restaurant_name,
       day_of_the_week,
       delivery_time,
       case when day_of_the_week='Weekday' and delivery_time>=25 then 'Late'
            when day_of_the_week='Weekend' and delivery_time>=30 then 'Late'
            else 'On-time' end "지연여부"
from food_orders

Data Type 오류 해결하기

rating 은 숫자가 포함되어 있지만 문자 형으로 저장이 되어있습니다

(출력 결과 컬럼명 옆의 ‘ABC’ 혹은 ‘123’ 을 확인해주세요. ‘ABC’ 는 문자로 저장이 되어있다는 의미입니다.)

 

따라서 문자, 숫자를 혼합하여 함수에 사용 할 때에는 데이터 타입을 변경해주어야 합니다.

--숫자로 변경
cast(if(rating='Not given', '1', rating) as decimal) 

--문자로 변경
concat(restaurant_name, '-', cast(order_id as char))

💡 DBeaver에서 여러개의 쿼리를 한번에 작성했을 경우.

한개의 쿼리만 드래그를 해주면 해당 부분만 실행 가능하다!