Post

SELECT부터 정렬, 중복 제거까지, SQL 조회에 대해 알아보기

SQL 조회(SELECT) - 데이터 꺼내보기

데이터베이스는 결국 “필요한 데이터를 꺼내서” 쓰는 게 핵심이다.
테이블에 저장된 데이터를 가져올 때 사용하는 가장 기본적인 명령어가 SELECT이다.

1
SELECT * FROM member;
  • SELECT : 무엇을 가져올지 (열/컬럼)
  • FROM : 어느 테이블에서 가져올지

* 는 “모든 열”을 의미한다.
즉 위 구문은 member 테이블에 있는 모든 열의 값을 다 보여달라는 의미이다.

[실행 결과]

user_idemailuser_namebirth_dategenderpasswordpassword_expiredphone_numbannedwithdrawproviderrole
1sihyun@gmail.com시현이1990-04-23Fpassword-1230010-1234-567800googleUSER
2leesuman@gmail.com이수만1992-02-14Mpassword-4560010-9876-543200googleUSER

🤔 왜 SELECT * 를 남발하면 안되는걸까?

  • 성능 저하 : 불필요한 열까지 다 읽다보면 느려진다
  • 가독성 저하 : 우리가 보고 싶은 건 몇 개 열 뿐인데 너무 많은 정보가 출력된다
  • 보안/트래픽 낭비 : password 같은 민감한 값까지 다 나와 버릴 수 있다

➔ 실무에서는 꼭 필요한 열만 명시적으로 지정해서 조회한다

특정 열만 골라 조회

1
2
SELECT user_name, email, phone_num
FROM member;

member 테이블에서 이름, 이메일, 전화번호 3가지만 조회한다

[실행 결과]

user_nameemailphone_num
시현이sihyun@gmail.com010-1234-5678
이수만leesuman@gmail.com010-9876-5432

결과에 별칭 붙이기 (AS)

1
2
SELECT user_name AS 이름, email AS 이메일, phone_num AS 전화번호
FROM member;
  • 별칭을 붙이므로써, 어떤 데이터를 의미하는지 한눈에 파악이 가능하다
1
2
SELECT user_name 이름, email 이메일, phone_num 전화번호
FROM member;
  • AS는 생략해도 되며, 열 이름이 겹치지 않게 구분할 때 많이 사용한다

[실행 결과]

이름이메일전화번호
시현이sihyun@gmail.com010-1234-5678
이수만leesuman@gmail.com010-9876-5432

WHERE 절 - 원하는 조건만 필터링하기

SELECT는 데이터를 가져오는 명령어이지만,
거기에 WHERE절이 붙으면 필요한 데이터만 콕 집어서 가져올 수 있다.

예를 들어, “성별이 F인 회원만 보고 싶다” 이런식으로 조건을 주어 필터링 할 수 있다.

WHERE 기본형

1
2
3
SELECT 열목록
FROM 테이블명
WHERE 조건;

WHERE 절에는 보통 비교 연산자를 사용해서 조건을 만든다

연산자의미예시
=같다gender = ‘F’
!= 또는 <>같지 않다banned != 1
>초과birth_date > ‘2000-01-01’
<미만birth_date < ‘2000-01-01’
>=이상wishlist_count >= 500

이메일이 특정 값과 정확히 일치하는 회원 찾기

1
2
3
SELECT *
FROM member
WHERE email = 'sihyun@gmail.com';

이메일 값과 정확히 일치하는 회원 정보만 보여준다.

[실행 결과]

user_idemailuser_namebirth_dategenderpasswordpassword_expiredphone_numbannedwithdrawproviderrole
1sihyun@gmail.com시현이1990-04-23Fpassword-1230010-1234-567800googleUSER

남성 회원만 조회 (gender = 'M')

1
2
3
SELECT user_id, user_name, gender
FROM member
WHERE gender = 'M';

[실행 결과]

user_iduser_namegender
2이수만M

남성 회원이 아닌 사람 찾기 (!=)

1
2
3
SELECT user_name, gender
FROM member
WHERE gender != 'M';

[실행 결과]

user_namegender
시현이F


여러 조건 결합 - 논리 연산자 (AND/OR/NOT)

AND 예시

“남자이면서, banned = false 인 정상 회원만 조회” (banned : 정지 여부)

1
2
3
SELECT user_name, gender, banned
FROM member
WHERE gender = 'M' AND banned = false;
  • AND는 둘 다 만족해야한다

[실행 결과]

user_namegenderbanned
이수만M0

OR 예시

“관리자이거나, 비밀번호 만료된 사람”

1
2
3
SELECT user_name, role, password_expired
FROM member
WHERE role = 'ADMIN' OR password_expired = true;
  • OR은 둘 중 하나만 참이어도 조회된다

NOT 예시

“정지되지 않은(banned가 아닌) 회원만 조회”

1
2
3
SELECT user_name, banned
FROM member
WHERE NOT banned = true; -- 또는 WHERE banned = false;

[실행 결과]

user_namebanned
시현이0
이수만0


WHERE 조건에 자주 쓰이는 컬럼은 기본키인덱스 컬럼일수록 조회가 훨씬 빠르다.
인덱스는 일종의 ‘색인’ 역할을 하기 때문에, 테이블을 처음부터 끝까지 다 훑지 않고도 원하는 데이터를 바로 찾아갈 수 있게 해준다.


WHERE - 더 편리한 조건 검색

기본 WHERE= 또는 > 같은 단순 조건으로 필터링했지만,
SQL에는 이런 조건들을 훨씬 간결하고 가독성 좋게 만들어주는 편리한 연산자들이 있다.

대표적으로 자주 쓰는 조건 연산자

기능연산자
특정 범위BETWEEN A AND B
목록/집합IN (…)
패턴 검색LIKE
값이 없는지 확인IS NULL

BETWEEN : 특정 범위에 있는 값 조회

1
2
SELECT * FROM product
WHERE price >= 50000 AND price <= 100000;

위와 같은 방식을 BETWEEN으로 더 깔끔하게 작성할 수 있다.

1
2
SELECT * FROM product
WHERE price BETWEEN 50000 AND 100000;

➔ 최소값과 최대값을 포함하며, 눈으로 봐도 범위 조건이라는 게 바로 보인다.

[실행 결과]

product_idcategory_idproduct_nameimg1img2img3img4img5contentpricesalebrandcreated_atdeletedwishlist_countgenderpopularity_score
32경량 패딩/img/padding.jpgNULLNULLNULLNULLNULL990000TTIO2025-08-1400U0

NOT BETWEEN : 특정 범위 제외

1
2
SELECT * FROM product
WHERE price NOT BETWEEN 50000 AND 100000;

[실행 결과]

product_idcategory_idproduct_nameimg1img2img3img4img5contentpricesalebrandcreated_atdeletedwishlist_countgenderpopularity_score
11베이직 반팔 티셔츠/img/tshirt.jpgNULLNULLNULLNULLNULL199000TIO2025-08-1400U0
21데님 바지/img/jeans.jpgNULLNULLNULLNULLNULL490000TIO2025-08-1400U0


IN : 여러 값 중 하나와 일치하는 것 찾기

“브랜드가 ‘GUCCI’, ‘PRADA’, ‘TIO’ 중 하나인 상품만 보고 싶다”

1
2
SELECT * FROM product
WHERE brand = 'GUCCI' OR brand = 'PRADA' OR brand = 'TIO';

OR로 쓰면 길어진다. 따라서 IN을 사용하면 훨씬 깔끔하다.

1
2
SELECT * FROM product
WHERE brand IN ('GUCCI', 'PRADA', 'TIO');

NOT IN : 목록에 없는 값 찾기

특정 브랜드를 제외하고 보고 싶을 때

1
2
SELECT * FROM product
WHERE brand NOT IN ('GUCCI', 'PRADA');


LIKE : 패턴(문자열) 검색

LIKE는 모양이 비슷한 문자열을 찾을 때 사용한다.

“member 테이블에서 이메일이 @naver.com으로 끝나는 회원만 보고 싶다.

1
2
3
SELECT user_id, email
FROM member
WHERE email LIKE '%@naver.com';
  • %는 어떤 문자든 0개 이상을 의미하는 와일드카드이다
  • 계절% ➔ 계절로 시작하는 단어 (예: 계절옷, 계절상품, …)
  • %티셔츠% ➔ 이름에 티셔츠가 들어가는 모든 상품


IS NULL : 값이 없는(비어 있는) 데이터를 찾고 싶을 때

아직 이미지가 등록되지 않은 상품만 찾고 싶다 ➔ img2NULL인 상품

1
2
3
SELECT product_name, img2
FROM product
WHERE img2 IS NULL;
  • NULL 비교에는 = NULL 쓰면 안 된다 ➔ IS NULL / IS NOT NULL


🤔 왜 NULL 비교에는 = NULL을 쓰면 안될까?

SQL에서 NULL은 단순한 값(value)이 아니고, 값이 존재하지 않는다를 의미하는 특수한 상태이다
즉, NULL은 아무 값도 아닌 상태이기 때문에, 다른 값과 “비교”하는 (=, !=) 방식으로는 판단이 불가능하다

(예시) SELECT * FROM product WHERE img2 = NULL;
위 쿼리는 실제론 항상 false 취급돼서 아무것도 안나온다.
왜냐면 NULL은 값이 없는데 그걸 비교하는 것 자체가 성립되지 않는다라고 SQL이 생각하기 때문이다.


ORDER BY - 결과를 정렬해주는 절

위에서 설명한 내용은 원하는 데이터를 뽑는 데 집중했다면,
이제는 “그 결과를 어떤 순서로, 예쁘게 보여줄 것인가?”가 중요해진다.

대표적인 예:

  • 가장 최근 가입한 회원 순으로 보기
  • 가격이 낮은 상품부터, 또는 비싼 상품부터 보기
  • 재고 많은 순 ➔ 그 안에서는 가격 낮은 순

기본 구조

1
2
3
4
SELECT 컬럼
FROM 테이블
WHERE 조건
ORDER BY 정렬기준 컬럼 [정렬방식];
  • 정렬기준 컬럼: 어떤 값을 기준으로 위/아래를 정할지
  • 정렬방식(옵션):
    • ASC : 오름차순 (작은값 ➔ 큰값)
    • DESC : 내림차순 (큰값 ➔ 작은값)

최근 가입자 순으로 보기 (member 테이블)

1
2
3
SELECT user_name, email, created_at
FROM member
ORDER BY created_at DESC;

➔ 가장 나중에 가입한 사람이 위에 올라온다 (“created_at” 컬럼 기준 내림차순)

가격 낮은순 상품 정렬 (product 테이블)

1
2
3
SELECT product_name, price
FROM product
ORDER BY price ASC;   -- ASC는 생략도 가능

다중 정렬

“재고가 많은 순 ➔ 그 안에서는 가격이 낮은 순”

1
2
3
SELECT product_name, stock_quantity, price
FROM product
ORDER BY stock_quantity DESC, price ASC;

➔ 1순위: 재고 가장 많은 상품부터
➔ 재고가 같으면 그들끼리 가격 낮은 순으로 정렬됨


LIMIT - 결과 개수 제한하기

현실에서는 전체 데이터를 다 보여주는 경우보다, 상위 몇 개만 보거나 페이지당 몇 개씩 잘라서 보여주는 경우가 훨씬 많다.

LIMIT는 그걸 위해 존재한다.

1
2
3
4
SELECT 컬럼
FROM 테이블
ORDER BY 기준
LIMIT 개수;

가장 찜 수가 많은 상품 2개만 보여줄 경우

1
2
3
4
SELECT product_name, price
FROM product
ORDER BY wishlist_count DESC
LIMIT 2;


왜 정렬과 LIMIT이 필요할까?

  • 사용자는 “순위”나 “정렬된 데이터”를 원함 (최근순, 인기순, 가장 저렴한 순서…)
  • 전체 데이터를 한번에 가져오면 너무 느리고 무거움
    • LIMIT으로 필요한 만큼만 가져오고 페이지 나눔

따라서 데이터를 화면에 보여주기 위해 꼭 필요한 기본 기술이라고 할 수 있다.


DISTINCT - 중복 제거하고 유일한 값만 보고 싶은 경우

데이터를 조회하다 보면 같은 값이 여러 번 반복되는 경우가 많다.
예를 들어, 하나의 아바타가 모자, 신발, 가방 등 여러 상품을 착용하고 있고
다른 아바타들도 같은 상품을 여러 번 착용했다면 product_id가 계속 중복되어 조회된다.

그럴 때 필요한 기능이 바로 DISTINCT - 중복을 제거하고 유일한 값만 남기는 것이다.

기본 사용법

1
2
SELECT DISTINCT 컬럼명
FROM 테이블;

closet_avatar_item 테이블에서 UNIQUE한 상품 ID만 뽑기

1
2
SELECT DISTINCT product_id
FROM closet_avatar_item;

➔ 이 쿼리는 “현재 아바타들이 착용한 상품 중복 없이 보여줘”라는 의미이다.
같은 상품(product_id)이 여러 번 착용되었더라도 결과엔 한 번만 나타난다.

어떤 아바타가 어떤 상품을 입었는지 조합으로 중복없이 보기

1
2
SELECT DISTINCT closet_avatar_id, product_id
FROM closet_avatar_item;
  • DISTINCT closet_avatar_id, product_id는 이 두 컬럼을 하나의 세트(묶음)으로 보고
  • 둘의 조합이 똑같이 반복되는 경우에만 중복으로 보고 제거한다

예를 들어, 아래와 같은 데이터가 있다면:

closet_avatar_idproduct_id
1101
1101
1102
2101
2101

[실행 결과]

closet_avatar_idproduct_id
1101
1102
2101


🤔 DISTINCT를 언제 쓸까?

  • 회원 테이블에서 role 값이 어떤 것들로 구성되어있는지 알고 싶을 때
  • 구매된 상품 카테고리는 총 몇 종류 있는지 파악할 때
  • 아바타 테이블에서 지금까지 착용된 상품 종류가 몇 개나 되는지 확인할 때

즉, 특정 컬럼의 ‘고유한 값 목록’이나 ‘유형 개수’가 궁금할 때 DISTINCT를 쓰면 된다.



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

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

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

© sihyun. Some rights reserved.