SELECT 기본 조회
SELECT 목록으로 필요한 열만 고르고 WHERE로 필요한 행만 남기는 방법을, 고정된 실습 테이블에서 결과를 확인하며 익힙니다. 별칭, AND와 OR의 우선순위, 조건마다 남는 행의 수를 세어 필터를 점검하는 방법을 다룹니다.
게시글 목록 화면에 필요한 값은 제목과 조회수, 작성 시각뿐인데 SELECT *로 긴 본문까지 가져온다면, 결과는 나오더라도 좋은 조회라고 하기 어렵습니다. 조회는 테이블을 그대로 복사하는 일이 아니라, 결과를 받는 쪽에 필요한 열과 행을 분명히 적는 일입니다.
이 글에서는 열을 고르는 투영(projection)과 행을 고르는 조건식, 결과의 이름을 정하는 별칭(alias)을 한 쿼리 안에서 따로따로 읽어 봅니다. 결과를 교재와 똑같이 재현할 수 있도록, 3장은 고정된 실습 데이터(fixture)를 담은 별도의 테이블을 만들어 읽기만 합니다.
3장의 실습 테이블
앞 장의 연습 문제를 실행했는지에 따라 누적된 posts의 행 수와 자동으로 만들어진 id가 달라집니다. 그래서 3장은 독립된 테이블 query_post_demo에 14행을 고정해 두고, 누적 모델의 posts는 건드리지 않습니다. 앞 장의 board_lab 데이터베이스를 선택한 상태에서 실행합니다.
DROP TABLE IF EXISTS query_post_demo;
CREATE TABLE query_post_demo (
id BIGINT UNSIGNED NOT NULL,
author_id BIGINT UNSIGNED NOT NULL,
title VARCHAR(80) NOT NULL,
content VARCHAR(5000) NOT NULL,
view_count BIGINT UNSIGNED NOT NULL,
created_at DATETIME(6) NOT NULL,
published_at DATETIME(6) NULL,
status VARCHAR(20) NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB;
INSERT INTO query_post_demo (
id, author_id, title, content, view_count,
created_at, published_at, status
) VALUES
(1, 1, 'SELECT 기초', 'SELECT의 열 선택을 설명합니다.', 25, '2026-07-10 08:00:00', '2026-07-10 08:25:00', 'PUBLISHED'),
(2, 1, 'SQL WHERE', 'WHERE 경계값을 복습합니다.', 50, '2026-07-11 09:00:00', '2026-07-11 09:50:00', 'PUBLISHED'),
(3, 2, 'NULL 복습', 'NULL 비교 규칙을 정리합니다.', 40, '2026-07-11 10:00:00', '2026-07-11 10:40:00', 'PUBLISHED'),
(4, 3, '설치 메모', 'MySQL 설치 절차를 정리합니다.', 10, '2026-07-11 11:00:00', '2026-07-11 11:10:00', 'PUBLISHED'),
(5, 1, '정렬 연습', '완전한 정렬 키를 설명합니다.', 35, '2026-07-12 13:30:00', '2026-07-12 14:05:00', 'PUBLISHED'),
(6, 2, 'MySQL 인덱스', '인덱스 기초를 소개합니다.', 25, '2026-07-12 15:00:00', '2026-07-12 15:25:00', 'PUBLISHED'),
(7, 3, '설치 메모', 'Docker 설치 절차를 보충합니다.', 10, '2026-07-12 16:00:00', '2026-07-12 16:10:00', 'PUBLISHED'),
(8, 2, '고급 SQL 복습', '숨김 처리된 고급 SQL 글입니다.', 45, '2026-07-13 12:00:00', '2026-07-13 12:40:00', 'HIDDEN'),
(9, 1, 'SQL 함수', '숨김 처리된 함수 예제입니다.', 30, '2026-07-13 13:00:00', '2026-07-13 13:25:00', 'HIDDEN'),
(10, 2, 'LIKE 복습', 'LIKE 패턴을 설명합니다.', 20, '2026-07-13 14:00:00', '2026-07-13 14:20:00', 'PUBLISHED'),
(11, 1, '정렬 동률', '동률 정렬을 설명합니다.', 40, '2026-07-13 15:00:00', '2026-07-13 15:40:00', 'PUBLISHED'),
(12, 1, 'LIMIT 실습', 'LIMIT 경계를 설명합니다.', 45, '2026-07-13 15:00:00', '2026-07-13 15:45:00', 'PUBLISHED'),
(13, 3, 'JOIN 계획', '아직 공개하지 않은 JOIN 초안입니다.', 0, '2026-07-14 09:00:00', NULL, 'DRAFT'),
(14, 2, 'GROUP BY 검토', '작성 중인 GROUP BY 글입니다.', 15, '2026-07-14 10:00:00', NULL, 'DRAFT');
SELECT COUNT(*) AS fixture_rows FROM query_post_demo;fixture_rows
14fixture_rows가 14인지 확인한 뒤 뒤의 쿼리를 실행합니다. 3장 2편부터 3장 4편까지도 이 테이블을 계속 쓰므로, 그 글로 바로 들어왔다면 이 블록부터 실행합니다. 이 테이블의 id는 직접 정한 값이어서 누구의 환경에서나 같습니다.
모든 열과 행을 가져오는 조회
처음 만든 구현이 SQL을 짧게 쓰려고 모든 게시글을 읽은 뒤, 애플리케이션에서 조회수가 30 이상인 행만 남긴다고 해 봅시다. 데이터가 작을 때는 문제가 드러나지 않지만, 전송량과 메모리 사용이 테이블의 크기에 따라 커지고 어떤 조건으로 읽었는지도 SQL에 남지 않습니다.
USE board_lab;
SELECT *
FROM query_post_demo;이 SELECT는 14행을 모두 돌려주며, 조회수가 10회인 글과 아직 공개하지 않은 초안, 본문까지 함께 옵니다. 아래는 그 가운데 처음 세 행만 옮긴 것입니다. ORDER BY가 없으므로 지금은 기본 키의 순서로 나오더라도, 그 순서가 보장되는 것은 아닙니다.
id | author_id | title | content | view_count | created_at | published_at | status
1 | 1 | SELECT 기초 | SELECT의 열 선택을 설명합니다. | 25 | 2026-07-10 08:00:00.000000 | 2026-07-10 08:25:00.000000 | PUBLISHED
2 | 1 | SQL WHERE | WHERE 경계값을 복습합니다. | 50 | 2026-07-11 09:00:00.000000 | 2026-07-11 09:50:00.000000 | PUBLISHED
3 | 2 | NULL 복습 | NULL 비교 규칙을 정리합니다. | 40 | 2026-07-11 10:00:00.000000 | 2026-07-11 10:40:00.000000 | PUBLISHEDSELECT *는 현재 스키마의 모든 열을 결과에 암묵적으로 넣습니다. 나중에 private_note 같은 열이나 큰 JSON 열이 더해지면, 호출하는 코드를 바꾸지 않아도 결과의 모양이 달라지고 개인정보가 함께 나갈 수도 있습니다. 애플리케이션에서 거르는 방식은 데이터베이스가 줄일 수 있었던 행을 네트워크 밖까지 보낸 뒤 버리므로, 거르는 일을 맡는 위치부터 잘못된 것입니다.
SELECT와 WHERE가 하는 일
SELECT 목록에는 결과를 받는 쪽이 실제로 쓸 표현식만 둡니다. WHERE는 행마다 조건식을 평가해 참(TRUE), 거짓(FALSE), 알 수 없음(UNKNOWN) 가운데 하나를 얻고, 참인 행만 다음 단계로 보냅니다. 별칭은 저장된 열의 이름을 바꾸지 않고, 결과의 이름을 받는 쪽의 말로 옮겨 줍니다.
비교식은 왼쪽과 오른쪽 값의 관계를 나타내고, AND는 모든 조건이, OR는 하나 이상의 조건이 참일 때 참입니다. 괄호 없이 섞으면 AND가 OR보다 먼저 평가되므로, 업무의 문장이 어떻게 묶이는지 괄호로 드러내는 편이 안전합니다. 열의 선택과 비교 연산자, AND와 OR, NOT, AS는 모두 표준 SQL의 핵심입니다.
MySQL에서 문자열의 비교는 열의 정렬 규칙(collation)이 정한 대소문자 규칙을 따르므로, 이메일 같은 값을 비교하는 방식은 열의 정렬 규칙과 함께 확인합니다. WHERE view_count >= 30처럼 열을 그대로 두고 비교하는 형태는 읽기 쉬울 뿐 아니라, 뒤에서 인덱스를 설계할 때도 유리합니다. 나중에 같은 열 이름을 가진 테이블을 조인하면 author_id가 어느 테이블의 것인지 모호해지므로, 그때는 테이블 별칭을 함께 씁니다.
업무의 질문을 SQL로 옮긴다
요구 사항을 "작성자 1의 공개 게시글 가운데 조회수가 30 이상인 글"처럼 먼저 문장으로 적은 뒤, 열과 행을 나누어 씁니다. 이 단일 테이블 조회에서 author_id는 작성자의 조건이고, status와 view_count는 공개 상태와 최소 조회수의 조건입니다.
SELECT
id,
title AS post_title,
view_count AS views,
created_at
FROM query_post_demo
WHERE author_id = 1
AND status = 'PUBLISHED'
AND view_count >= 30
ORDER BY created_at, id;id | post_title | views | created_at
2 | SQL WHERE | 50 | 2026-07-11 09:00:00.000000
5 | 정렬 연습 | 35 | 2026-07-12 13:30:00.000000
11 | 정렬 동률 | 40 | 2026-07-13 15:00:00.000000
12 | LIMIT 실습 | 45 | 2026-07-13 15:00:00.000000작성자 1의 글 가운데 1번은 조회수가 25여서, 9번은 숨김 상태여서 빠졌습니다. 11번과 12번은 작성 시각이 같으므로 ORDER BY의 두 번째 기준인 id가 순서를 정합니다. 결과의 열 이름에는 별칭인 post_title과 views가 쓰입니다.
결과의 별칭은 같은 쿼리의 WHERE에서 참조할 수 없습니다. 논리적으로 WHERE가 SELECT 목록보다 먼저 평가되어, 그 시점에는 별칭이 아직 없기 때문입니다. 같은 조건이 여러 곳에서 되풀이되더라도 곧바로 뷰나 계산 열을 만들기보다, 그 조건이 업무의 규칙인지 특정 화면의 조회 조건인지부터 구별합니다.
조건마다 남는 행을 센다
복합 조건은 완성된 결과만 보지 말고, 조건식 하나하나가 행을 몇 개로 줄이는지 확인하면 오타와 우선순위의 오류를 빨리 찾을 수 있습니다. 다음 쿼리는 조건을 하나씩 더해 가며 통과하는 행의 수를 한 번에 셉니다.
SELECT
COUNT(*) AS all_rows,
SUM(author_id = 1) AS author_rows,
SUM(author_id = 1 AND status = 'PUBLISHED') AS published_rows,
SUM(
author_id = 1
AND status = 'PUBLISHED'
AND view_count >= 30
) AS final_rows
FROM query_post_demo;all_rows | author_rows | published_rows | final_rows
14 | 6 | 5 | 4행의 수가 14에서 6, 5, 4로 줄어드는 것을 보면 어느 조건에서 예상과 달라졌는지 찾기 쉽습니다. 이 쿼리는 MySQL에서 참인 비교식이 숫자로 1, 거짓이 0이 되는 성질을 이용한 것입니다. 표준 SQL로 옮기려면 SUM(CASE WHEN ... THEN 1 ELSE 0 END)의 형태로 씁니다.
WHERE는 14행을 4행으로, SELECT는 8열을 4열로
고정 실습 데이터를 읽는다. 네 열만 반환한다. 각 단계의 값과 전환 이유를 비교합니다.
SELECT · ROWS / COLUMNS
WHERE는 14행을 4행으로, SELECT는 8열을 4열로
고정 query_post_demo fixture를 읽는 두 책임입니다. 행을 줄이는 조건과 결과 열의 선택은 서로 독립적으로 검토합니다.
통과 ID는2·5·11·12입니다. WHERE는 행을 고르고 SELECT는 id, title(post_title), view_count(views), created_at의 네 열만 결과에 둡니다. 원본 행이나 열을 삭제하는 작업이 아닙니다.
숫자는 본문의 고정 fixture에 해당합니다. 이 단계는 결과 의미를 설명하며 실제 인덱스 경로나 물리 평가 순서를 지정하지 않습니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 원본부터 결과 투영까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 설명·조건 | 고정 실습 데이터(query_post_demo)를 읽는 두 책임입니다. 행을 줄이는 조건과 결과 열의 선택은 서로 독립적으로 검토합니다. |
| 관계 설명 | 원본 14행 8열에서 작성자 6행, 공개 상태 5행, 조회 수 4행이 남습니다. 열 투영 뒤 4행 4열이 됩니다. 의미상의 단계이며 물리 실행 순서를 지정하지 않습니다. |
| 설명·조건 | 통과 ID는 2·5·11·12입니다. WHERE는 행을 고르고 SELECT는 id, title(post_title), view_count(views), created_at의 네 열만 결과에 둡니다. 원본 행이나 열을 삭제하는 작업이 아닙니다. |
| 설명·조건 | 숫자는 본문의 고정 실습 데이터에 해당합니다. 이 단계는 결과 의미를 설명하며 실제 인덱스 경로나 물리 평가 순서를 지정하지 않습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 원본 고정 실습 데이터를 읽는다. |
|
|
| 작성자 작성자 1을 고른다. |
|
|
| 공개 상태 작성자 조건 안에서 상태를 고른다. |
|
|
| 조회 수 30 이상만 남긴다. |
|
|
| 결과 투영 네 열만 반환한다. |
|
|
WHERE는 14행을 4행으로, SELECT는 8열을 4열로
고정 실습 데이터를 읽는다. 네 열만 반환한다. 각 단계의 값과 전환 이유를 비교합니다.
SELECT · ROWS / COLUMNS
WHERE는 14행을 4행으로, SELECT는 8열을 4열로
고정 query_post_demo fixture를 읽는 두 책임입니다. 행을 줄이는 조건과 결과 열의 선택은 서로 독립적으로 검토합니다.
통과 ID는2·5·11·12입니다. WHERE는 행을 고르고 SELECT는 id, title(post_title), view_count(views), created_at의 네 열만 결과에 둡니다. 원본 행이나 열을 삭제하는 작업이 아닙니다.
숫자는 본문의 고정 fixture에 해당합니다. 이 단계는 결과 의미를 설명하며 실제 인덱스 경로나 물리 평가 순서를 지정하지 않습니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 원본부터 결과 투영까지 진행합니다.
관계도의 보충 설명
| 구분 | 전체 도표의 내용 |
|---|---|
| 설명·조건 | 고정 실습 데이터(query_post_demo)를 읽는 두 책임입니다. 행을 줄이는 조건과 결과 열의 선택은 서로 독립적으로 검토합니다. |
| 관계 설명 | 원본 14행 8열에서 작성자 6행, 공개 상태 5행, 조회 수 4행이 남습니다. 열 투영 뒤 4행 4열이 됩니다. 의미상의 단계이며 물리 실행 순서를 지정하지 않습니다. |
| 설명·조건 | 통과 ID는 2·5·11·12입니다. WHERE는 행을 고르고 SELECT는 id, title(post_title), view_count(views), created_at의 네 열만 결과에 둡니다. 원본 행이나 열을 삭제하는 작업이 아닙니다. |
| 설명·조건 | 숫자는 본문의 고정 실습 데이터에 해당합니다. 이 단계는 결과 의미를 설명하며 실제 인덱스 경로나 물리 평가 순서를 지정하지 않습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 원본 고정 실습 데이터를 읽는다. |
|
|
| 작성자 작성자 1을 고른다. |
|
|
| 공개 상태 작성자 조건 안에서 상태를 고른다. |
|
|
| 조회 수 30 이상만 남긴다. |
|
|
| 결과 투영 네 열만 반환한다. |
|
|
열을 줄이는 이유
투영을 좁히는 일은 결과를 보기 좋게 만드는 데서 끝나지 않습니다. 필요한 열이 모두 보조 인덱스에 들어 있으면, InnoDB는 테이블의 행을 다시 읽지 않는 커버링 인덱스(covering index)를 쓸 수 있습니다. 다만 열을 적게 적었다고 언제나 빨라지는 것은 아닙니다. 조건이 전체 행 가운데 얼마나 적은 행만 골라내는지를 나타내는 선택도(selectivity)는 7장 1편에서, 커버링 인덱스의 효과는 7장 3편에서 실행 계획으로 확인합니다.
실습에서는 같은 질문을 세 번에 나누어 실행해 보세요. 처음에는 전체 행을 보고, 다음에는 WHERE만 적용해 남는 행을 확인하고, 마지막에 SELECT 목록을 줄입니다. 이 순서를 따르면 결과가 비었을 때 원인이 열의 표현식인지 조건식인지 바로 나눌 수 있습니다.
리뷰할 때는 SELECT 목록과 WHERE 조건을 따로 봅니다. 결과에 식별자가 빠지면 화면에서 행을 안정적으로 갱신할 수 없고, 단위가 다른 숫자를 value 같은 이름으로 돌려주면 받는 쪽이 오해합니다. 반대로 내부의 외래 키나 개인정보를 습관처럼 돌려주면 다른 코드와 얽히는 범위와 노출되는 범위가 커집니다.
조건 쪽에서는 삭제 여부와 상태, 소유자의 범위가 빠지지 않았는지 먼저 봅니다. 여러 고객의 데이터를 한 테이블에 두는 멀티테넌트(multi-tenant) 서비스에서 소유자 조건은 검색 옵션이 아니라 권한의 일부입니다. 같은 쿼리를 관리자와 일반 사용자가 함께 쓴다면, 권한 조건을 호출하는 쪽의 선택에 맡기지 말고 접근 계층에서 강제합니다.
입력과 형 변환, 빈 결과
운영 코드에서는 값을 SQL 문자열에 이어 붙이지 말고, 준비된 문장(prepared statement)의 바인딩 변수를 씁니다. author_id와 최소 조회수, 상태는 모두 외부에서 들어올 수 있는 값이며, 바인딩은 SQL 주입을 막고 값의 타입도 함께 전달합니다.
타입이 다른 값끼리 비교하는 것도 피합니다. MySQL 8.4 문서에 따르면 문자열과 숫자를 비교할 때는 둘을 부동소수점 수로 바꾸어 비교하므로, WHERE title = 0이라고 쓰면 숫자로 시작하지 않는 제목이 모두 0으로 바뀌어 조건에 맞습니다. 이때 경고가 함께 나오므로, 파라미터의 타입을 열의 타입과 맞추고 경고를 확인하는 습관을 들입니다.
조회하는 동안 데이터가 바뀔 수 있다는 점도 기억해 둡니다. 두 SELECT 사이에 행이 추가되면 단계별 건수가 달라질 수 있으므로, 같은 스냅샷에서 비교해야 할 때는 트랜잭션의 범위를 일부러 정합니다(8장 3편). 이 글의 건수 진단은 실습 데이터가 바뀌지 않는다는 전제에서 한 것입니다.
결과가 0행인 것은 오류가 아닙니다. 조건에 맞는 데이터가 없다는 정상적인 결과일 수 있으므로, 애플리케이션은 빈 목록과 데이터베이스의 장애를 같은 예외로 처리하지 않습니다.
| 따져 볼 것 | 질문 |
|---|---|
| 열 | 받는 쪽이 실제로 쓰는 열과 표현식만 골랐는가 |
| 행 | 거르는 일을 데이터베이스가 하고, 조건이 업무의 문장과 맞는가 |
| 논리 | AND와 OR가 섞인 조건을 괄호로 분명히 했는가 |
| 이름 | 별칭이 값의 뜻과 단위를 드러내는가 |
운영에서 쓰는 조회는 결과의 예시를 API의 규칙처럼 함께 남겨 두면 좋습니다. 열의 순서에 기대는 코드보다 이름으로 읽는 코드를 쓰고, SELECT *는 구조를 살펴보는 일회성 콘솔 조회 정도로만 씁니다.
더 깊이: 문자열과 숫자를 비교할 때 잃는 것
비교하는 두 값의 타입이 다르면 MySQL은 정해진 규칙을 차례로 적용합니다. 둘 다 문자열이면 문자열로, 둘 다 정수면 정수로 비교하고, 한쪽이 DATETIME이나 TIMESTAMP 열이고 다른 쪽이 상수라면 상수를 시각으로 바꿉니다. 한쪽이 DECIMAL이고 다른 쪽이 정수나 DECIMAL이면 DECIMAL로 비교합니다. 이 가운데 어느 경우에도 해당하지 않으면 양쪽을 배정밀도 부동소수점 수로 바꾸어 비교하는데, 문자열과 숫자의 비교가 바로 이 경우입니다.
이 마지막 규칙은 인덱스에도 영향을 줍니다. 문자열 열에 인덱스가 있어도 WHERE str_col = 1처럼 숫자와 비교하면 MySQL은 그 인덱스로 값을 빠르게 찾지 못합니다. '1', ' 1', '1a'처럼 숫자 1로 바뀌는 문자열이 여럿이고, 이 값들은 문자열의 정렬 순서에서 한곳에 모여 있지 않기 때문입니다. 그래서 전화번호나 우편번호처럼 숫자 모양의 값을 문자열 열에 저장했다면, 파라미터도 문자열로 바인딩해야 인덱스를 쓸 수 있습니다.
부동소수점 비교는 정확하지도 않습니다. 배정밀도 수는 64비트 정수를 모두 정확히 나타내지 못하므로, MySQL 8.4 문서의 예에서는 '9223372036854775807' = 9223372036854775806도 참(1)이 됩니다. 문자열로 저장한 외부 주문 번호처럼 자릿수가 긴 값을 숫자 파라미터와 비교하면, 다른 값의 행이 조건에 맞을 수 있다는 뜻입니다.
정리
- SELECT 목록은 결과에 담을 열을, WHERE는 통과시킬 행을 정합니다.
- WHERE는 조건식이 참인 행만 남기며, 결과의 별칭은 같은 쿼리의 WHERE에서 쓸 수 없습니다.
- 필요 없는 열과 행을 데이터베이스 밖으로 보낸 뒤 버리지 않습니다.
- AND는 OR보다 먼저 평가되므로, 복합 조건은 업무의 문장대로 괄호로 묶습니다.
- 조건을 하나씩 더하며 남는 행의 수를 세면 잘못된 필터를 빨리 찾을 수 있습니다.
연습 문제
1. 조회수의 범위
author_id가 2인 공개 게시글 가운데 조회수가 20회 이상 90회 이하인 행을 찾아 보세요. 결과에는 title과 view_count, created_at만 넣고, 조회수 열은 views라는 이름으로 돌려줍니다. OR를 쓰지 말고 두 비교식으로 범위를 적습니다.
해설 보기
하한과 상한을 각각 비교식으로 적어 AND로 이으면, 범위의 양 끝이 포함되는지가 분명해집니다. 다음 글에서는 같은 조건을 BETWEEN으로 줄여 씁니다.
SELECT
title,
view_count AS views,
created_at
FROM query_post_demo
WHERE author_id = 2
AND status = 'PUBLISHED'
AND view_count >= 20
AND view_count <= 90
ORDER BY created_at;title | views | created_at
NULL 복습 | 40 | 2026-07-11 10:00:00.000000
MySQL 인덱스 | 25 | 2026-07-12 15:00:00.000000
LIKE 복습 | 20 | 2026-07-13 14:00:00.000000조회수가 정확히 20회인 'LIKE 복습'도 포함되어야 합니다. 양 끝을 빼려면 >와 <로 바꿔야 하므로, 요구 사항의 "이상"과 "이하"를 연산자에 그대로 옮겼는지 확인합니다. 8번 '고급 SQL 복습'도 작성자와 조회수의 범위는 맞지만 HIDDEN이어서 빠지고, 14번은 조회수가 15이고 초안이어서 빠집니다.
2. 별칭을 WHERE에서 쓰면
다음 쿼리는 왜 실패하나요? 원하는 결과를 얻도록 고쳐 보세요.
SELECT id, view_count AS views
FROM query_post_demo
WHERE views >= 30;해설 보기
WHERE가 평가될 때는 SELECT 목록의 별칭이 아직 없으므로, MySQL은 views를 테이블의 열로 찾다가 실패합니다.
ERROR 1054 (42S22): Unknown column 'views' in 'where clause'조건에는 저장된 열의 이름을 써서 WHERE view_count >= 30으로 고칩니다. 별칭은 SELECT 목록이 계산된 뒤에 평가되는 ORDER BY에서는 쓸 수 있으므로, ORDER BY views DESC는 문제가 없습니다.
3. AND와 OR의 우선순위
다음 두 쿼리의 결과는 각각 몇이고, 차이가 나는 행은 무엇인가요? 두 쿼리는 이 글의 query_post_demo를 대상으로 하며, WHERE의 괄호 하나만 다릅니다.
SELECT COUNT(*) AS without_parens
FROM query_post_demo
WHERE author_id = 1 OR author_id = 2 AND status = 'PUBLISHED';
SELECT COUNT(*) AS with_parens
FROM query_post_demo
WHERE (author_id = 1 OR author_id = 2) AND status = 'PUBLISHED';해설 보기
첫 쿼리는 author_id = 1 OR (author_id = 2 AND status = 'PUBLISHED')로 읽히므로, 작성자 1의 글 6개가 상태와 관계없이 모두 들어가고 작성자 2의 공개 글 3개가 더해져 9입니다. 둘째 쿼리는 두 작성자의 공개 글만 세므로 작성자 1의 5개와 작성자 2의 3개를 더한 8입니다.
without_parens
9
with_parens
8차이는 작성자 1의 숨김 글인 9번 'SQL 함수'입니다. 줄바꿈이나 들여쓰기는 논리를 바꾸지 않으므로, "작성자 1 또는 2의 공개 글"이라는 문장은 괄호로 묶어야 합니다. 같은 열의 여러 값은 다음 글의 IN으로 author_id IN (1, 2)처럼 쓰면 괄호를 빠뜨릴 일도 줄어듭니다.
다음 글 조건식과 필터링에서는 연속된 범위와 값의 목록, 문자열의 패턴을 각각 BETWEEN과 IN, LIKE로 나타내고, 양 끝 값과 와일드카드에서 생기는 오류를 확인합니다.