Post

SQL 데이터 가공 방법

데이터 가공하기 - 산술 연산

서비스를 운영하다 보면 단순히 데이터베이스에 저장된 값만 보는 것이 아니라,
그 값을 조합하고 계산해서 더 유용한 정보로 가공해야 하는 경우가 많다.

예를 들어,

  • 아바타가 지금 착용한 아이템의 총 가격은 얼마일까?
  • 상품 가격에서 할인율을 적용하려면 최종 판매가는 얼마일까?

SQL에서는 산술 연산을 SELECT문 안에서 쉽게 처리할 수 있다.

아바타가 입고 있는 아이템 총액 구하기

아바타(closet_avatar)가 착용한 아이템들이 closet_avatar_item 테이블에 저장되어있고, 각 아이템의 가격은 product 테이블에 있다.

그럼 아바타별로 총합을 계산해보자.

1
2
3
4
5
6
7
8
9
SELECT
    ca.closet_avatar_id,
    SUM(p.price) AS total_value
FROM closet_avatar ca
JOIN closet_avatar_item cai
    ON ca.closet_avatar_id = cai.closet_avatar_id
JOIN product p
    ON cai.product_id = p.product_id
GROUP BY ca.closet_avatar_id;

[실행 결과]

closet_avatar_idtotal_value
1138900
2119000
370000

각 아바타가 입고 있는 아이템들의 가격 합계(total_value)를 알 수 있다.


할인된 가격 구하기

상품 테이블(product)에는 할인율(sale) 컬럼이 있다.
할인율이 있다면 “실제 판매가”는 할인율을 적용한 값이어야한다.

1
2
3
4
5
6
SELECT
    product_name,
    price,
    sale,
    price - (price * sale / 100) AS final_price
FROM product;

따라서 이 최종 판매가(final_price)를 기준으로 상품 판매가 되도록 한다.


문자열 가공하기

문자열을 어떻게 보여줄 것인지도 중요하다.

예를 들어,

  • 고객 정보 페이지에서 닉네임 (이메일) 형태로 보여주고 싶을 때
  • 상품 상세 페이지에 브랜드 - 상품명 형식으로 노출하고 싶을 때
  • 상품명 검색을 위해 대소문자를 통일해야할 때

CONCAT() - 문자열 합치기

회원의 닉네임과 이메일을 보기 좋게 합쳐서 보여주고 싶다면?

1
2
3
SELECT
  CONCAT(user_name, ' (', email, ')') AS user_info
FROM member;
user_info
시현이 (sihyun@gmail.com)
이수만 (lee@gmail.com)

CONCAT_WS() - 구분자와 함께 합치기

CONCAT_WS(separator, string1, string2, ...)
첫 번째 인자로 구분자를 주면, 각 문자열 사이에 자동으로 넣어준다.

상품을 [브랜드 - 상품명] 형식으로 보여주고 싶으면?

1
2
3
SELECT
  CONCAT_WS(' - ', brand, product_name) AS product_summary
FROM product;
product_summary
TIO - 베이직 반팔 티셔츠
TIO - 데님 바지
TTIO - 경량 패딩

대소문자 변환 : UPPER(), LOWER()

product테이블에 아래와 같은 상품명이 있다고 가정해보자.

  • Nike Air Max
  • adidas SuperStar
  • GUCCI Bag

사용자가 검색할 때 전부 소문자로 nike라고 입력할 수도 있고, 대문자로 NIKE라고 입력할 수도 있다.
SQL에서 기본적으로 문자열 비교는 대소문자를 구분하기 때문에, WHERE product_name LIKE '%nike%'라고 하면 원하는 결과가 안 나올 수 있다.

이럴 땐, 검색 조건과 데이터 모두를 동일한 대소문자로 변환해서 비교하면 된다!

1
2
3
SELECT product_name
FROM product
WHERE LOWER(product_name) LIKE LOWER('%nike%');

문자열 길이 확인 : LENGTH(), CHAR_LENGTH()

회원 닉네임이 너무 짧거나 길면 제약을 줄 수 있다.
이때, 문자열 길이를 확인하면 된다.

1
2
3
4
5
SELECT
  user_name,
  CHAR_LENGTH(user_name) AS name_length,
  LENGTH(user_name) AS byte_length
FROM member;
user_namename_lengthbyte_length
시현이39
이수만39
  • CHAR_LENGTH() : 글자 수
  • LENGTH() : 바이트 수 (UTF-8 ➔ 한글 3 byte)

NULL 값 처리하기

상품(product) 테이블의 content 컬럼(상품 설명)에 값이 없을 수 있다.

예를 들어, 테이블에 데이터가 다음과 같다고 하자.

1
2
3
4
5
product_id | product_name     | content
----------------------------------------------
1          | 베이직 반팔 티셔츠   | 여름철 필수 반팔 티셔츠
2          | 데님 바지          | NULL
3          | 경량 패딩          | 가볍고 따뜻한 패딩

상품 설명에 값이 없다면 “추가 정보를 준비하고 있습니다” 등 안내 문구를 대신 보여주는 것이 낫다.
이때, NULL 처리 함수를 사용한다.

IFNULL() - NULL 값을 다른 값으로 대체

1
IFNULL(표현식1, 표현식2)
  • 표현식1 : NULL 여부 검사할 컬럼
  • 표현식2 : NULL일 경우 대신 반환할 값

값이 있으면 그대로 보여주고, 없다면 지정한 값으로 바꿔준다.

1
2
3
4
SELECT
  product_name,
  IFNULL(content, '추가 정보를 준비하고 있습니다') AS description
FROM product;
product_namedescription
베이직 반팔 티셔츠여름철 필수 반팔 티셔츠
데님 바지추가 정보를 준비하고 있습니다
경량 패딩가볍고 따뜻한 패딩

COALESCE() - 여러 후보 중 첫번째 값 선택

여러 개 후보 중에서 가장 먼저 NULL이 아닌 값을 골라주는 함수이다.

예를 들어, product테이블을 보면
상품마다 대표 이미지(img1)이 있지만, 경우에 따라 img2, img3, … 보조 이미지가 없을 수도 있다.

그런데 만약 대표 이미지가 비어있다면? img2, img3 중에서 보여줄 수 있으면 좋다.
([주의] img1컬럼은 NULL이 아니어야한다는 조건 때문에 NULL일수는 없다.)

이럴 때, COALESCE()함수를 사용하면 된다.

1
2
3
4
SELECT
  product_name,
  COALESCE(img1, img2, img3, '/img/default.jpg') AS display_img
FROM product;
  • img1이 있으면 그걸 사용
  • img1이 NULL이고 img2가 있으면 img2 사용
  • img1, img2가 NULL이고 img3가 있으면 img3 사용
  • 세 개 다 없으면 /img/default.jpg(기본 이미지) 사용

이 기능을 통해 상품 페이지의 이미지가 빈 이미지로 뜨는 일 없이, 항상 뭔가 하나는 노출되게 할 수 있다.


조건부 가공 (CASE WHEN)

조건에 따라 값을 가공할 수 있다.

예를 들어, 할인율이 있으면 ➔ 할인 중, 할인율이 없으면 ➔ 정상가 같은 안내 텍스트를 노출할 수 있다.

1
2
3
4
5
6
7
8
SELECT
  product_name,
  price,
  CASE
    WHEN sale > 0 THEN CONCAT(sale, '% 할인 중')
    ELSE '정상가'
  END AS sale_info
FROM product;

날짜, 시간 가공

날짜 함수(DATE_FORMAT, YEAR, MONTH 등)를 활용해서 나이 계산을 할 수 있다.

예를 들어, 회원 테이블(member)에 birth_date 컬럼이 있는데,
회원의 생일에서 현재 연도를 빼서 나이를 계산할 수 있다.
(정확히 하려면 월, 일까지 비교해야하지만, 이 예제는 간단하게 연도로만 계산)

1
2
3
4
SELECT
  user_name,
  YEAR(CURDATE()) - YEAR(birth_date) AS age
FROM member;

주의할 점 : 가공한 컬럼은 “조회용”일 뿐

가공된 값은 SELECT에서 보기 위한 용도로 쓰이는 거지, 실제 DB에 저장되는 것은 아니다.

SQL에서 SELECT 문으로 산술 연산이나 문자열 가공을 해서 만든 컬럼들은 DB에 실제로 저장되는 컬럼이 아니다
쿼리를 실행할 때 계산되어 결과로 반환되는 값일 뿐이다.

“왜 테이블에 컬럼이 안 생기지?”하는 의문이 생길 수 있기 때문이다.

Tip : 조회용으로 가공된 컬럼을 웹사이트 화면에 반영할 수 있다.

SELECT에서 계산된 가공 컬럼은 DB에 저장되지 않으며, 데이터가 많을 경우 조회 성능에 영향을 줄 수 있다.

[서버에서 쿼리 실행 후 바로 사용]

예를 들어 java, python 등 DB에 쿼리를 날리면:

1
2
3
4
5
6
SELECT
    product_name,
    price,
    sale,
    price - (price * sale / 100) AS final_price
FROM product;

DB가 반환하는 각 행에는 product_name, price, sale, final_price 값이 포함된다.
서버 코드는 이 final_price를 API 응답, JSON 데이터 등으로 내려줄 수 있다.

1
2
3
4
[
  {"product_name": "베이직 티셔츠", "price": 19900, "sale": 10, "final_price": 17910},
  {"product_name": "데님 바지", "price": 49000, "sale": 0, "final_price": 49000}
]

프론트엔드에서 별도의 계산 없이 이 데이터를 기반으로 바로 화면에 표시 가능하다.


이 글은 [실전 데이터베이스 입문] 강의 내용을 바탕으로 작성되었으며,
예제는 제가 진행한 TIO 프로젝트의 데이터베이스 설계를 기반으로 설명하고 있습니다.

프로젝트 환경과 요구사항에 맞춰 구성된 예시이므로,
실제 서비스 환경이나 다른 설계 방식과는 차이가 있을 수 있습니다.

[출처] : (인프런 강의) 김영한의 실전 데이터베이스 입문 - 모든 IT인을 위한 SQL 첫걸음(SQL부터 차근차근)

© sihyun. Some rights reserved.