데이터베이스와 SQL 처음부터 배우기
1관계형 데이터베이스란 무엇인가
관계형 데이터베이스는 데이터를 테이블에 저장합니다. 테이블은 스프레드시트처럼 생겼습니다. 각 행은 하나의 레코드이고 각 열은 하나의 속성입니다.
가게에는 id, name, city 열이 있는 테이블 customers와 id, customer_id, total 열이 있는 테이블 orders가 있을 수 있습니다.
기본 키는 각 행을 유일하게 식별하는 열 또는 열의 집합입니다. 값이 반복되거나 비어 있을 수 없습니다. 여기서는 각 테이블의 id입니다.
외래 키는 다른 테이블의 기본 키를 가리키는 열입니다. orders에서 customer_id는 어느 고객이 주문했는지를 알려 줍니다.
이렇게 테이블이 연결됩니다. 한 고객은 여러 주문을 가질 수 있지만, 각 주문은 한 고객에게 속합니다. 이것이 일대다 관계입니다.
SQL(Structured Query Language)은 테이블을 만들고, 데이터를 바꾸고, 데이터에 대해 질의하는 데 쓰는 언어입니다.
자가 점검 기본 키란 무엇인가?
- 각 행을 유일하게 식별하는 열 또는 열의 집합
- 검색을 더 빠르게 하는 인덱스
- 다른 테이블의 행을 가리키는 열
- 모든 테이블의 첫 번째 열
답: 각 행을 유일하게 식별하는 열 또는 열의 집합. 기본 키는 유일하고 NULL이 될 수 없으므로 정확히 한 행을 골라냅니다. 다른 테이블을 가리키는 열은 외래 키입니다.
2SELECT로 데이터 읽기
대부분의 문제는 SELECT 문에 관한 것입니다. 항상 다음 순서로 쓰는 절들로 이루어집니다.
SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY …
- SELECT는 원하는 열을 나열합니다. DISTINCT는 중복된 행을 없앱니다.
- FROM은 테이블, 또는 조인한 테이블들을 지정합니다.
- WHERE는 조건을 만족하는 행만 남깁니다. 예: WHERE total > 20.
- GROUP BY는 값이 같은 행을 한 그룹으로 묶어 그룹별로 세거나 더할 수 있게 합니다.
- HAVING은 조건을 만족하는 그룹만 남깁니다. WHERE에는 쓸 수 없는 집계 함수를 쓸 수 있습니다.
- ORDER BY는 결과를 정렬합니다. 기본은 오름차순이며, DESC를 붙이면 큰 값이 먼저 옵니다.
집계 함수는 여러 행을 하나의 값으로 요약합니다: COUNT, SUM, AVG, MIN, MAX.
데이터베이스는 절을 쓴 순서대로 실행하지 않습니다. 먼저 FROM, 그다음 WHERE, GROUP BY, HAVING, SELECT를 실행하고 마지막에 ORDER BY를 실행합니다.
다른 문장은 데이터를 바꿉니다. INSERT는 행을 추가하고, UPDATE는 행을 바꾸고, DELETE는 행을 지웁니다. CREATE, ALTER, DROP은 테이블 자체를 바꿉니다.
자가 점검 주문이 5건보다 많은 고객만 보고 싶다. COUNT(*) > 5로 거르는 절은 무엇인가?
- DISTINCT
- HAVING
- ORDER BY
- WHERE
답: HAVING. 개수는 행을 그룹으로 묶은 뒤에야 존재하며, HAVING이 그룹을 거릅니다. WHERE는 묶기 전에 실행되므로 COUNT 같은 집계를 쓸 수 없습니다.
3조인, 정규화, 트랜잭션
조인은 조건을 써서 두 테이블의 행을 결합합니다. 보통 외래 키 = 기본 키입니다.
FROM customers c JOIN orders o ON o.customer_id = c.id
- INNER JOIN(또는 JOIN)은 두 테이블 모두에 짝이 있는 행만 남깁니다.
- LEFT JOIN은 왼쪽 테이블의 모든 행을 남깁니다. 짝이 없는 곳은 오른쪽 테이블의 열이 NULL입니다.
- NULL은 값이 없거나 알 수 없다는 뜻입니다. IS NULL로 검사하고, = NULL로는 절대 검사하지 마세요.
정규화는 모든 사실이 한 번만 저장되도록 테이블을 나눕니다. 같은 사실을 여러 곳에서 갱신하는 일을 피할 수 있습니다.
- 1NF: 모든 셀이 하나의 원자 값을 가지며 반복 그룹이 없습니다.
- 2NF: 1NF이고, 모든 비키 열이 복합 키의 일부가 아니라 키 전체에 종속됩니다.
- 3NF: 2NF이고, 어떤 비키 열도 다른 비키 열에 종속되지 않습니다.
트랜잭션은 여러 문장을 하나의 단위로 묶습니다. COMMIT은 이를 영구하게 만들고 ROLLBACK은 되돌립니다. 트랜잭션은 ACID를 따릅니다: 원자성(전부 아니면 전무), 일관성, 격리성, 지속성.
자가 점검 한 고객이 주문을 하나도 하지 않았다. customers LEFT JOIN orders에서 그 고객은 어떻게 되는가?
- 고객은 결과에서 빠진다
- 고객은 테이블의 주문 하나하나마다 한 번씩 나타난다
- 고객은 한 번 나타나며 주문 열에는 NULL이 들어간다
- 질의가 오류로 실패한다
답: 고객은 한 번 나타나며 주문 열에는 NULL이 들어간다. LEFT JOIN은 왼쪽 테이블의 모든 행을 남기고 빠진 오른쪽을 NULL로 채웁니다. 고객을 결과에서 빼는 것은 INNER JOIN이 하는 일입니다.
4단계별 방법
질의를 작성하거나, 주어진 질의가 무엇을 반환하는지 알아내려면:
- 데이터가 들어 있는 테이블과 키로 어떻게 연결되는지 찾으세요.
- 조인을 포함해 FROM을 쓰세요. 짝이 없는 행도 남겨야 하면 LEFT JOIN을 고르세요.
- 개별 행에 대한 조건은 WHERE로 추가하세요.
- 문제에 “별로” 또는 “각각”이 있으면 GROUP BY와 집계를 추가하세요.
- 집계에 대한 조건은 HAVING으로 추가하고, 순서가 중요하면 ORDER BY를 추가하세요.
- 작은 예로 결과를 확인하세요. 데이터베이스가 실행하는 순서대로 각 절 뒤의 행을 나열하세요.
이론 문제에서는 먼저 개념(키, 조인 종류, 정규형, ACID 성질)을 짚은 다음 그 정의에 맞는 보기를 고르세요.
5풀이 예제
모든 단계를 따라가 보세요. 다음 단계를 열기 전에 미리 예측해 보세요.
풀이 예제테이블 customers에는 행 (1, Ana), (2, Ben), (3, Cas)가 있다. 테이블 orders에는 행 (id, customer_id) = (10, 1), (11, 1), (12, 2), (13, 1)이 있다. 이 질의는 무엇을 반환하는가? SELECT c.name, COUNT(o.id) FROM customers c LEFT JOIN orders o ON o.customer_id = c.id GROUP BY c.name
1각 고객은 자기 id를 가진 주문과 짝지어집니다. Ana는 주문 10, 11, 13과 맞습니다. Ben은 주문 12와 맞습니다.
Cas는 주문이 없습니다. LEFT JOIN이므로 Cas는 결과에 남고 o.id는 NULL입니다.
2- Ana, 10
- Ana, 11
- Ana, 13
- Ben, 12
- Cas, NULL
모두 5행입니다.
3GROUP BY c.name은 세 그룹을 만듭니다: 3행인 Ana, 1행인 Ben, 1행인 Cas.
4COUNT(o.id)는 NULL이 아닌 값만 셉니다. Ana는 3, Ben은 1입니다.
Cas는 한 행이 있지만 o.id가 NULL이므로 Cas는 0입니다.
5Ana, Ben, Cas를 합치면 주문은 3 + 1 + 0 = 4건으로 orders의 행 수와 같습니다. 대신 COUNT(*)를 쓰면 COUNT(*)는 행 자체를 세므로 Cas가 잘못되게 1이 됩니다.
6흔한 실수
- WHERE에 집계를 쓰는 것. COUNT, SUM, AVG에 대한 조건은 HAVING에 넣습니다.
- = NULL이라고 쓰는 것. NULL과의 모든 비교는 알 수 없음이므로 IS NULL이나 IS NOT NULL을 쓰세요.
- 짝이 없는 행도 남겨야 하는데 INNER JOIN을 쓰는 것. LEFT JOIN을 쓰세요.
- 조인 조건을 잊는 것. ON이 없으면 모든 행이 모든 행과 짝지어집니다(교차 조인).
- GROUP BY에도 없고 집계 안에도 없는 열을 선택하는 것.
- DELETE(행을 지움)와 DROP TABLE(테이블 전체를 지움)을 혼동하는 것.
- WHERE 없이 UPDATE나 DELETE를 실행하는 것. 테이블의 모든 행이 바뀝니다.
자가 점검 테이블에 10행이 있고 열 col이 모든 행에서 NULL이다. SELECT COUNT(col)은 무엇을 반환하는가?
- 0
- 10
- 1
- NULL
답: 0. COUNT(col)은 NULL이 아닌 값만 세므로 0을 반환합니다. COUNT(*)는 행을 세므로 10을 반환합니다.
7시험 문제의 유형
- 쉬움: 핵심 용어와 사실. SELECT, WHERE, ORDER BY, DELETE가 하는 일, 기본 키와 외래 키, NULL의 의미.
- 보통: 개념 적용. 작업에 맞는 질의, WHERE와 HAVING의 차이, 내부 조인과 외부 조인, 정규형, ACID.
- 어려움: 사례와 분석. 서브쿼리, 격리 수준과 그 이상 현상, BCNF, 윈도 함수, 인덱스와 질의 성능, SQL 인젝션.
질의가 주어지면 FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY 순서로 몇 개의 행에 대해 추적하세요. 함정은 대부분 세 행 안에 드러납니다.
앱에서 단계별로 배우기앱에서 단계별로 배우기