본문 바로가기
개발 및 기타 등등/Database

DataBase Query문(쿼리문) 이해하기, SELECT와 WHERE

by 클수저 2026. 9. 5.
728x90
반응형

1. 핵심 모델 : 두 방향의 자르기

SQL의 기본 조회는 2차원 표를 두 방향으로 자르는 것입니다.

하는일 방향 관계대수 용어
SELECT 어떤 컬럼을 볼까 세로 ↕ projection (투영)
WHERE 어떤 을 남길까 가로 ↔ selection (선택)
SELECT  stn, wrn_cd, tm_ef      -- ← 세로: 컬럼 선택
FROM    tb_xxx
WHERE   stn = 108;              -- ← 가로: 행 필터링

🔬 실험 1 : 두 방향을 분리해서 체감하기

-- ① 전부
SELECT * FROM tb_xxx LIMIT 5;

-- ② 세로만 자르기: 컬럼 3개, 행은 전부 대상
SELECT stn, wrn_cd, tm_ef FROM tb_xxx LIMIT 5;

-- ③ 가로만 자르기: 컬럼 전부, 행은 조건에 맞는 것만
SELECT * FROM tb_xxx WHERE stn = 108 LIMIT 5;

-- ④ 둘 다
SELECT stn, wrn_cd, tm_ef FROM tb_xxx WHERE stn = 108 LIMIT 5;
  • ②는 화면 너비가 줄어듭니다
  • ③은 결과 행 수가 줄어듭니다

이 차이를 눈으로 확인하는 게 실험의 목적입니다.

⚠️ LIMIT은 습관으로 붙이세요.
실습 대상 테이블이 12MB만 되어도 SELECT *는 결과를 받아오는 데 한참 걸립니다. 실무 테이블은 수 GB인 경우가 흔합니다.

 

2. 함정 ① :  =를 SELECT 자리에 쓰면?

SELECT stn = 108 FROM tb_xxx LIMIT 5;

에러가 날 것 같지만 정상 실행됩니다. 대신 결과가 이렇습니다.

 ?column?
----------
 f
 f
 t
 f
 t

행이 전혀 걸러지지 않았습니다.

❓왜 이렇게 되는가

=는 boolean을 반환하는 연산자입니다.

  • SELECT 절은 "각 행마다 이 식을 계산해서 보여줘"라는 뜻
  • 그래서 stn = 108의 계산 결과(참/거짓) 가 하나의 컬럼으로 출력됨
  • WHERE가 특별한 이유는, 그 boolean이 참인 행만 통과시키는 자리이기 때문

컬럼명이 ?column?으로 나오는 건 이름 붙일 근거가 없어서입니다. AS로 지정할 수 있습니다.

  • 교훈: "어떤 컬럼을 보여줄까"와 "어떤 행을 남길까"는 완전히 다른 일입니다.

3. 함정 ② : 큰따옴표와 작은따옴표

SELECT * FROM tb_xxx WHERE wrn_cd = "W";   -- ✗ 에러
SELECT * FROM tb_xxx WHERE wrn_cd = 'W';   -- ✓ 정상

큰따옴표 버전의 에러

ERROR:  column "W" does not exist

"값이 없다"가 아니라 "컬럼이 없다" 고 나오는 게 힌트입니다.

따옴표 역할 예시

따옴표 역할 예시
'작은따옴표' 문자열ㅇ 리터럴 (값) WHERE name = '홍길동'
"큰따옴표" 식별자 (테이블명, 컬럼명) SELECT "MyColumn" FROM "MyTable"

SQL 표준이 정한 규칙이고, PostgreSQL은 이를 엄격히 따릅니다.
(MySQL은 큰따옴표도 문자열로 봐주지만 이건 MySQL의 확장입니다)

💡 진단법: 에러 메시지가 "column"을 언급하는데 나는 값을 쓴 것 같다면 → 따옴표를 의심하세요.

문자열 안에 작은따옴표가 필요할 때

SELECT 'It''s SQL' AS 방법1;      -- 작은따옴표 2번 (표준)
SELECT $$It's SQL$$ AS 방법2;     -- 달러 인용 (PostgreSQL 확장)

 

4. 함정 ③ : 쉼표 하나가 컬럼을 삼킨다

이게 가장 잡기 어려운 버그입니다. 에러가 안 나고 결과만 조용히 틀립니다.

-- 쉼표 누락
SELECT grp_cd_id, cd_id, cd_nm ordr
FROM   public.tb_cd
WHERE  del_yn = 'N';
  • 결과: 컬럼이 4개가 아니라 3개만 나오고, 3번째 헤더가 ordr로 찍힙니다.

왜 에러가 안 나는가

SQL에서 AS는 생략 가능합니다.

SELECT cd_nm AS ordr    -- 명시적
SELECT cd_nm    ordr    -- AS 생략 (동일한 의미)

PostgreSQL은 cd_nm ordr를 cd_nm AS ordr로 해석했습니다. 즉 cd_nm 컬럼에 ordr이라는 별명을 붙인 것이지, ordr 컬럼을 조회한 게 아닙니다.

🔬 실험 2 : 두 결과를 나란히 비교

-- 잘못된 버전
SELECT grp_cd_id, cd_id, cd_nm ordr FROM public.tb_cd LIMIT 5;

-- 올바른 버전
SELECT grp_cd_id, cd_id, cd_nm, ordr FROM public.tb_cd LIMIT 5;

버전 컬럼 수 ordr 열의 내용

버전 컬럼수 ordr열의 내용
쉼표 누락 3개 한글 텍스트 (실은 cd_nm 값)
정상 4개 숫자 (진짜 ordr 값)

쉼표 하나가 결과를 바꿉니다. 데이터 타입이 예상과 다르면 이걸 의심하세요.

💡 예방책: 별칭에는 항상 AS를 명시하세요. 그러면 AS가 없는 곳은 전부 쉼표 누락입니다.

5. DISTINCT : 값의 종류 파악하기

조건을 쓰려면 어떤 값이 들어있는지 알아야 합니다.

SELECT DISTINCT wrn_cd FROM tb_xxx;

중복을 제거하고 존재하는 값의 종류만 보여줍니다.

대용량 테이블에서는 느립니다. 전체를 훑어야 하기 때문이죠. 맛보기용 대안:

SELECT DISTINCT wrn_cd FROM (SELECT wrn_cd FROM tb_xxx LIMIT 1000) t;

여러 컬럼을 함께 쓰면 조합 기준으로 중복을 제거합니다.

SELECT DISTINCT grp_cd_id, grp_cd_nm FROM public.tb_cd ORDER BY 1;

 

6. LIKE와 와일드카드

이름을 정확히 모를 때 씁니다.

패턴 의미 매칭 예

패턴 의미 매칭 예시
'warning%' warning으로 시작 warning_log ✓ / tb_warning ✗
'%warning' warning으로 tb_warning ✓ / warning_log ✗
'%warning%' warning 포함 둘 다 ✓
'_arning' _는 정확히 1글자 warning ✓ / xxarning ✗

🔬 실험 3 : % 위치에 따른 결과 차이

SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'warning%';
SELECT table_name FROM information_schema.tables WHERE table_name LIKE '%warning';
SELECT table_name FROM information_schema.tables WHERE table_name LIKE '%warning%';

제 환경의 테이블은 전부 tb_로 시작하므로 첫 번째는 0건이 나왔습니다. %를 어디 붙이느냐가 결과를 통째로 바꿉니다.

대소문자 무시 검색

SELECT * FROM t WHERE name ILIKE '%kim%';    -- ILIKE (PostgreSQL 확장)
SELECT * FROM t WHERE lower(name) LIKE '%kim%';  -- 표준 방식

ILIKE는 PostgreSQL 확장이라 이식성이 없습니다.

7. ORDER BY : 정렬의 함정

컬럼 순번으로 지정하기

SELECT table_schema, table_name FROM information_schema.tables
ORDER BY 1, 2;

숫자 1, 2는 SELECT에 적은 컬럼의 순번입니다. 다음과 완전히 동일합니다.

ORDER BY table_schema, table_name;

🔬 실험 4 : 정렬 기준 순서 바꾸기

ORDER BY 1, 2;   -- 스키마로 먼저 묶고, 그 안에서 이름순
ORDER BY 2, 1;   -- 이름순으로 먼저, 스키마는 섞임

정렬 기준의 순서가 결과 모양을 결정합니다.

🔬 실험 5 : 왜 대문자가 전부 앞에 오는가

ORDER BY 2, 1을 실행하니 LSM... 같은 대문자 테이블명이 전부 앞으로 몰렸습니다.

SELECT 'Z' < 'a' AS 대문자Z가_소문자a보다_앞인가;
  • 결과: true

알파벳 순으로는 a가 Z보다 앞일 것 같지만, DB는 알파벳이 아니라 문자 코드값으로 비교합니다.

문자 

문자 ASCII 코드
A ~ Z 65 ~ 90
a ~ z 97 ~ 122

대문자가 전부 앞선다는 뜻입니다.

해결 : 대소문자 무시 정렬

SELECT table_schema, table_name
FROM   information_schema.tables
WHERE  table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER  BY lower(table_name), table_schema;

lower()로 소문자로 통일해 비교하면 LSM...이 l 위치로 이동합니다.

📌 부수적 발견: 이 정렬 결과에서 "이 DB에 대문자 테이블명이 존재한다"는 사실을 알게 됐습니다. 이 테이블들은 조회 시 반드시 큰따옴표가 필요합니다.

NULL의 정렬 위치

SELECT unnest(ARRAY[3, 1, NULL, 2]) AS v ORDER BY v;

PostgreSQL은 오름차순에서 NULL을 마지막에 둡니다. 명시적으로 바꿀 수 있습니다.

ORDER BY v NULLS FIRST;
ORDER BY v DESC NULLS LAST;

 

8. 보너스 : DBeaver 실행 단축키

여러 쿼리를 써놓고 실행했는데 결과가 하나만 나온 적이 있다면 이것 때문입니다.

단축키 동작
Ctrl + Enter 커서가 있는 한 문장만 실행
Alt + X 스크립트 전체 실행 (결과 탭 여러 개 생성)
select count(*) from public.tb_cd;                     -- 커서가 여기 없으면 실행 안 됨
select count(*) from public.tb_cd where del_yn = 'N';  -- 이것만 실행됨

9. 정리

개념 요점

개념 요점
SELECT 세로 자르기 (projection). 컬럼 선택
WHERE 가로 자르기 (selection). 행 필터링
= boolean 반환 연산자. SELECT에 쓰면 값이 출력될 뿐
'...' 문자열 리터럴 (값)
"..." 식별자 (테이블/컬럼명)
쉼표 누락 AS 생략으로 해석 → 에러 없이 컬럼이 사라짐
DISTINCT 중복 제거. 여러 컬럼이면 조합 기준
LIKE / % / _ 부분 일치 검색
ORDER BY 1,2 컬럼 순번 정렬
문자 정렬 알파벳순이 아닌 코드값순. 대문자 우선
lower() 대소문자 무시 정렬

 

 

728x90
반응형