SELECT 는 SQL 문에서 가장 많이 사용되는 문법으로, 데이터베이스에서 데이터를 구축한 후에 그 내용들을 활용한다. 데이터를 아무리 완벽하게 준비해 놓았더라도 활용을 잘 못하면 의미가 없다. 그렇듯 SELECT를 잘 사용해야 데이터베이스를 100% 활용할 수 있다.
SELECT의 가장 기본 형식은 SELECT ~ FROM ~ WHERE이다. SELECT 바로 다음에는 열 이름이, FROM 다음에는 테이블 이름이 나온다. WHERE 다음에는 조건식이 나오는데, 조건식을 다양하게 표현함으로써 데이터베이스에서 원하는 데이터를 뽑아낼 수 있다.
실습용 데이터베이스 구축
실습용 데이터베이스 개요
이제는 본격적으로 SELECT 문을 연습할 차례이다. 그런데 SELECT를 실행하기 위해서는 데이터베이스가 있어야 한다. 이미 몇 개의 데이터베이스가 있지만 sakila, world는 구조가 복잡하고, shop_db는 데이터가 다양하지 않아 앞으로 연습할 SELECT에 적합하지 않다.
그래서 이번에는 간단하고, 보기 쉬운 테이블을 만들어서 사용하려고 한다. 이 전에는 MySQL 워크벤치에서 마우스 클릭으로 작업했지만 이번에는 모두 SQL로 작업해보자.
'인터넷 마켓 DB 구성도'는 인터넷 마켓에서 운영하는 데이터베이스를 단순화한 것이라고 생각하면 된다. 데이터 내용은 가상으로 넣은 것이며, 이 인터넷 마켓은 특별히 그룹으로 이루어진 가수만 가입되도록 했다고 가정하자.
가수 그룹의 리더는 물건을 사기 위해서 회원가입을 한다. 입력한 회원 정보는 회원 테이블(member)에 입력된다. 물론, 더 많은 정보를 입력해야 하지만 간단히 아이디/이름/주소/국번/전화번호/평균 키/데뷔 일자 등만 입력하는 것으로 하자.
회원가입을 한 후에 인터넷 마켓에서 물건을 구입하면 회원이 구매한 정보는 구매 테이블(buy)에 기록된다. 그러면 인터넷 마켓의 배송 담당자는 구매 테이블(buy)을 통해서 회원이 주문한 물건을 준비하고, 회원 테이블(member)에서 구매 테이블(buy)의 아이디와 일치하는 회원의 아이디를 찾아서 그 행의 주소로 물품을 배송한다.
예를 들어, 배송 담당자는 구매 테이블(buy)에서 BLK라는 아이디를 가진 회원이 구매한 지갑 2개, 맥북 프로 1개, 청바지 3벌을 포장한 후에 회원 테이블(member)에서 BLK라는 아이디를 찾는다. 그리고 포장박스에 이름은 '블랙핑크', 주소는 '경남', 연락처는 '055-222-2222'라고 적어서 배송하면 된다.
실습용 데이터베이스 만들기
실습용 데이터베이스를 만드는 SQL은 다음과 같다.
DROP DATABASE IF EXISTS market_db; -- 만약 market_db가 존재하면 우선 삭제한다.
CREATE DATABASE market_db;
USE market_db;
CREATE TABLE member -- 회원 테이블
( mem_id CHAR(8) NOT NULL PRIMARY KEY, -- 사용자 아이디(PK)
mem_name VARCHAR(10) NOT NULL, -- 이름
mem_number INT NOT NULL, -- 인원수
addr CHAR(2) NOT NULL, -- 지역(경기,서울,경남 식으로 2글자만입력)
phone1 CHAR(3), -- 연락처의 국번(02, 031, 055 등)
phone2 CHAR(8), -- 연락처의 나머지 전화번호(하이픈제외)
height SMALLINT, -- 평균 키
debut_date DATE -- 데뷔 일자
);
CREATE TABLE buy -- 구매 테이블
( num INT AUTO_INCREMENT NOT NULL PRIMARY KEY, -- 순번(PK)
mem_id CHAR(8) NOT NULL, -- 아이디(FK)
prod_name CHAR(6) NOT NULL, -- 제품이름
group_name CHAR(4) , -- 분류
price INT NOT NULL, -- 가격
amount SMALLINT NOT NULL, -- 수량
FOREIGN KEY (mem_id) REFERENCES member(mem_id)
);
INSERT INTO member VALUES('TWC', '트와이스', 9, '서울', '02', '11111111', 167, '2015.10.19');
INSERT INTO member VALUES('BLK', '블랙핑크', 4, '경남', '055', '22222222', 163, '2016.08.08');
INSERT INTO member VALUES('WMN', '여자친구', 6, '경기', '031', '33333333', 166, '2015.01.15');
INSERT INTO member VALUES('OMY', '오마이걸', 7, '서울', NULL, NULL, 160, '2015.04.21');
INSERT INTO member VALUES('GRL', '소녀시대', 8, '서울', '02', '44444444', 168, '2007.08.02');
INSERT INTO member VALUES('ITZ', '잇지', 5, '경남', NULL, NULL, 167, '2019.02.12');
INSERT INTO member VALUES('RED', '레드벨벳', 4, '경북', '054', '55555555', 161, '2014.08.01');
INSERT INTO member VALUES('APN', '에이핑크', 6, '경기', '031', '77777777', 164, '2011.02.10');
INSERT INTO member VALUES('SPC', '우주소녀', 13, '서울', '02', '88888888', 162, '2016.02.25');
INSERT INTO member VALUES('MMU', '마마무', 4, '전남', '061', '99999999', 165, '2014.06.19');
INSERT INTO buy VALUES(NULL, 'BLK', '지갑', NULL, 30, 2);
INSERT INTO buy VALUES(NULL, 'BLK', '맥북프로', '디지털', 1000, 1);
INSERT INTO buy VALUES(NULL, 'APN', '아이폰', '디지털', 200, 1);
INSERT INTO buy VALUES(NULL, 'MMU', '아이폰', '디지털', 200, 5);
INSERT INTO buy VALUES(NULL, 'BLK', '청바지', '패션', 50, 3);
INSERT INTO buy VALUES(NULL, 'MMU', '에어팟', '디지털', 80, 10);
INSERT INTO buy VALUES(NULL, 'GRL', '혼공SQL', '서적', 15, 5);
INSERT INTO buy VALUES(NULL, 'APN', '혼공SQL', '서적', 15, 2);
INSERT INTO buy VALUES(NULL, 'APN', '청바지', '패션', 50, 1);
INSERT INTO buy VALUES(NULL, 'MMU', '지갑', NULL, 30, 1);
INSERT INTO buy VALUES(NULL, 'APN', '혼공SQL', '서적', 15, 1);
INSERT INTO buy VALUES(NULL, 'MMU', '지갑', NULL, 30, 4);
SELECT * FROM member;
SELECT * FROM buy;
코드의 제일 위쪽을 보면 데이터베이스가 이미 존재하는 경우 해당 데이터베이스를 삭제하고 다시 생성함을 알 수 있다. 이는 market_db가 초기화되는 효과를 갖는다. 앞으로 market_db를 초기화해야 하는 상황이 많이 발생할 수 있는데 그때마다 지금과 같이 초기화시키면 된다.
market_db.sql 파일 내용 살펴보기
위 코드블럭의 구성을 파악해보자
데이터 베이스 만들기
DROP DATABASE IF EXISTS market_db; -- 만약 market_db가 존재하면 우선 삭제한다.
CREATE DATABASE market_db;
1. DROP DATABASE는 market_db를 삭제하는 문장이다. 이는 market_db.sql을 처음 실행할 때는 필요없다. 하지만 책을 학습하다 보면 다시 market_db.sql을 실행할 일이 있기 때문에 기존의 market_db를 삭제하는 것이다.
2. 데이터베이스를 새로 만든다. schemas 패널에서 Create Schema를 선택하는 것과 같은 효과이다.
회원 테이블(member) 만들기
USE market_db;
CREATE TABLE member -- 회원 테이블
( mem_id CHAR(8) NOT NULL PRIMARY KEY, -- 사용자 아이디(PK)
mem_name VARCHAR(10) NOT NULL, -- 이름
mem_number INT NOT NULL, -- 인원수
addr CHAR(2) NOT NULL, -- 지역(경기,서울,경남 식으로 2글자만입력)
phone1 CHAR(3), -- 연락처의 국번(02, 031, 055 등)
phone2 CHAR(8), -- 연락처의 나머지 전화번호(하이픈제외)
height SMALLINT, -- 평균 키
debut_date DATE -- 데뷔 일자
);
1. USE 문은 market_db 데이터베이스를 선택하는 문장이다. MySQL Workbench의 schemas 패널에서 shop_db를 더블클릭 하는 것과 같은 효과이다.
2. member 테이블을 만드는 과정이다. '인터넷 마켓 DB 구성도'에는 열 이름이 한글로 표현되었는데, 여기서는 영문으로 표현했다. 주석을 참고하자(SQL에서 주석은 -- (하이픈 2개)). 그 외에 VARCHAR가 있는데 CHAR와 유사하지만 차이점이 있다. 추후에 알아보자
구매 테이블(buy) 만들기
CREATE TABLE buy -- 구매 테이블
( num INT AUTO_INCREMENT NOT NULL PRIMARY KEY, -- 순번(PK)
mem_id CHAR(8) NOT NULL, -- 아이디(FK)
prod_name CHAR(6) NOT NULL, -- 제품이름
group_name CHAR(4) , -- 분류
price INT NOT NULL, -- 가격
amount SMALLINT NOT NULL, -- 수량
FOREIGN KEY (mem_id) REFERENCES member(mem_id)
);
1. 구매 테이블을 생성하는 코드이다.
2. AUTO_INCREMENT가 처음 나왔다. 이것은 자동으로 숫자를 입력해준다는 의미이다. 즉, 순번은 직접 입력할 필요 없이 1, 2, 3, ...과 같은 방식으로 자동으로 증가한다. 잠시 후에 다시 확인해보자.
3. FOREGIN KEY도 처음 나왔는데 나중에 다시 다뤄보자.
데이터 입력하기
이제는 데이터를 입력하는 INSERT문을 살펴보자. 회원 테이블(member)과 구매 테이블(buy) 1개씩만 살펴보자.
INSERT INTO member VALUES('TWC', '트와이스', 9, '서울', '02', '11111111', 167, '2015.10.19');
INSERT INTO buy VALUES(NULL, 'BLK', '지갑', NULL, 30, 2);
1. 회원 테이블(member)에 값을 입력한다. CHAR, VARCHAR, DATE형은 작은따옴표로 값을 묶어줬다. INT형은 작은따옴표 없이 그냥 넣어주면 된다.
2. 구매 테이블(buy)의 첫 번째 열인 순번(num)은 자동으로 입력되므로 그 자리에는 NULL이라고 써주면 된다. 그러면 알아서 1, 2, 3, ...으로 증가하면서 입력된다. 여기서는 처음이므로 1이 입력된다.
기본 조회하기: SELECT ~ FROM
이제는 본격적으로 '인터넷 마켓 DB 구성도'를 활용해서 SELECT 문을 배워보자.
USE 문
SELECT 문을 실행하려면 먼저 사용할 데이터베이스를 지정해야 한다. 현재 사용하는 데이터베이스를 지정 또는 변경하는 형식은 다음과 같다.
USE 데이터베이스_이름;
이렇게 지정해 놓은 후에 다시 USE 문을 사용하거나 다른 DB를 사용하겠다고 명시하지 않으면 앞으로 모든 SQL 문은 market_db에서 수행된다. 쿼리 창을 닫았다가 새로 열면 다시 USE를 실행해야 한다.
SELECT 문의 기본 형식
SELECT 문은 처음에는 사용하기 간단하지만, 사실 상당히 복잡한 구조를 갖는다. 간단한 형태로 먼저 살펴보자.
SELECT select_expr
[FROM table_references]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
좀 더 풀어서 하나씩 차근차근 살펴보자.
SELECT 열_이름
FROM 테이블_이름
WHERE 조건식
GROUP BY 열_이름
HAVING 조건식
ORDER BY 열_이름
LIMIT 숫자
기본적이고 핵심적인 형식을 먼저 살펴보자.
SELECT 열_이름
FROM 테이블_이름
WHERE 조건식
SELECT와 FROM
USE market_db;
SELECT * FROM member;
SELECT: 테이블에서 데이터를 가져올 때 사용하는 예약어, 가장 많이 사용함
원래 테이블의 전체 이름은 데이터베이스_이름.테이블_이름 형식으로 표현한다. '인터넷 마켓 DB 구성도'를 예로 든다면 이 테이블의 전체 이름은 market_db.member이다. 그렇기 때문에 원칙적으로는 다음과 같이 사용해야 한다.
SELECT * FROM market_db.member;
하지만 데이터베이스 이름을 생략하면 USE 문으로 지정해 놓은 데이터베이스가 자동으로 선택된다. 현재 선택된 데이터베이스가 market_db이므로 다음 두 쿼리는 동일한 것이다.
SELECT * FROM market_db.member;
SELECT * FROM member;
이번에는 해당 테이블에서 전체 열이 아닌 필요한 열만 가져와보자.
여러개의 열을 가져오고 싶으면 콤마(,)로 구분하면 된다. 열 이름의 순서는 원래 테이블을 만들 때 순서에 맞출 필요 없다. 보고 싶은 순서대로 열을 나열하면 된다.
SELECT addr, debut_date, mem_name FROM member;
(☆)참고로 열 이름에 별칭(alias)을 지정할 수 있다. 열 이름 다음에 지정하고 싶은 별칭을 입력하면 된다. 별칭에 공백이 있으면 큰따옴표(")로 묶어준다.
SELECT addr 주소, debut_date "데뷔 일자", mem_name FROM member;
특정한 조건만 조회하기: SELECT ~ FROM ~ WHERE
SELECT ~ FROM 은 대부분 WHERE와 함께 사용된다. WHERE는 필요한 것들만 골라서 결과를 보는 효과를 갖는다.
기본적인 WHERE 절
WHERE 절은 조회하는 결과에 특정한 조건을 추가해서 원하는 데이터만 보고 싶을 때 사용한다. 형식은 다음과 같다.
SELECT 열_이름
FROM 테이블_이름
WHERE 조건식;
지금 찾는 이름(mem_name)이 '블랙핑크'라면 다음과 같은 조건식을 사용하면 된다. 열_이름=값은 열의 값에 해당하는 결과만 출력해준다. 지금은 이름(mem_name)이 블랙핑크인 결과만 출력했다. 이름(mem_name) 열은 문자형(CHAR)이므로 작은따옴표로 묶어줘야한다.
SELECT * FROM member WHERE mem_name='블랙핑크';
다음과 같이 인원(mem_number)처럼 숫자형 열을 조회할 때는 작은따옴표가 필요 없다. 결과를 보면 인원수가 4명인 회원은 3건 나왔다.
SELECT * FROM member WHERE mem_number=4;
관계 연산자, 논리 연산자의 사용
숫자로 표현된 데이터는 범위를 지정할 수 있다. 예를 들어 평균 키(height)가 162 이하인 회원을 검색하려면 다음과 같이 관계 연산자 <= 를 사용해서 조회할 수 있다.
SELECT mem_id, mem_name
FROM member
WHERE height <= 162;
2가지 이상의 조건을 만족하도록 할 수도 있다. 평균 키(height)가 165 이상이면서 인원(mem_number)도 6명 초과인 회원은 다음과 같이 논리 연산자 AND를 이용해서 조회할 수 있다. OR도 마찬가지로 사용 가능하다.
SELECT mem_name, height, mem_number
FROM member
WHERE height >= 165 AND mem_number > 6;
BETWEEN ~ AND
이번에는 AND를 사용해서 평균 키(height)가 163~165인 회원을 조회해보자.
SELECT mem_name, height
FROM member
WHERE height >= 163 AND height <= 165;
그런데 범위에 있는 값을 구하는 경우에는 BETWEEN ~ AND를 사용해도 된다. 다음 SQL은 바로 앞에서 살펴본 AND를 사용한 SQL과 동일하다.
SELECT mem_name, height
FROM member
WHERE height BETWEEN 163 AND 165;
IN()
평균 키(height)와 같이 숫자로 구성된 데이터는 크다/작다의 범위를 지정할 수 있으므로 BETWEEN ~ AND를 사용할 수 있지만, 주소(addr)와 같은 데이터는 문자로 표현되기 때문에 어느 범위에 들어 있다고 표현할 수 없다. 만약 경기/전남/경남 중 한 곳에 사는 회원을 검색하려면 다음과 같이 OR로 일일이 써줘야 한다.
SELECT mem_name, addr
FROM member
WHERE addr='경기' OR addr = '전남' OR addr = '경남';
IN()을 사용하면 코드를 훨씬 간결하게 작성할 수 있다. 다음은 바로 앞의 SQL과 동일한 결과를 낸다.
SELECT mem_name, addr
FROM member
WHERE addr IN('경기', '전남', '경남');
LIKE
문자열의 일부 글자를 검색하려면 LIKE를 사용한다. 예를 들어 이름(mem_name)의 첫 글자가 '우'로 시작하는 회원은 다음과 같이 검색할 수 있다.
SELECT *
FROM member
WHERE mem_name LIKE '우%';
결과는 우주소녀가 나왔다. 이 조건은 제일 앞 글자가 '우'이고 그 뒤는 무엇이든(%) 허용한다는 의미이다.
한 글자와 매치하기 위해서는 언더바(_)를 사용한다. 다음 SQL은 이름(mem_name)의 앞 두 글자는 상관없고 뒤는 '핑크'인 회원을 검색한다.
문자열에서 한 문자에 대응하는 기호가 언더바이고, 반면 문자열에서 여러 문자에 대응하는 기호가 %이다.
SELECT *
FROM member
WHERE mem_name LIKE '__핑크';
결과는 에이핑크과 블랙핑크가 나왔다.
서브 쿼리
SELECT 안에는 또 다른 SELECT가 들어갈 수 있다. 이것을 서브 쿼리(subquery) 또는 하위 쿼리라고 부른다. 여기서는 이름(mem_name)이 '에이핑크'인 회원의 평균 키(height) 보다 큰 회원을 검색하고 싶다고 가정해보자.
SELECT height FROM member WHERE mem_name = '에이핑크';
SELECT mem_name, height FROM member WHERE height > 164;
SQL 문 2개를 사용해서 결과를 얻었다. 그런데 이 두 SQL을 하나로 만들 수는 없을까? 가능하다.
두 번째 SQL의 164 위치에 에이핑크의 평균 키(height)를 조회하는 SQL을 대신 써주면 된다.
SELECT mem_name, height FROM member
WHERE height > (SELECT height FROM member WHERE mem_name='에이핑크');
세미콜론이 하나이므로 이 SQL은 하나의 문장이다. 서브 쿼리의 장점은 2개의 SQL을 하나로 만듦으로써 하나의 SQL만 관리하면 되므로 더 간단해 진다는 것이다.
오늘 하루도 쌓였다!
'혼공학습단 12기' 카테고리의 다른 글
[혼공S] 03 - 3 '데이터 변경을 위한 SQL 문' 정리 (0) | 2024.07.11 |
---|---|
[혼공S] 03 - 2 '좀 더 깊게 알아보는 SELECT 문' 정리 (0) | 2024.07.09 |
[혼공S] 1주차 미션 (0) | 2024.07.07 |
[혼공S] 02 - 3 '데이터베이스 개체' 정리 (0) | 2024.07.06 |
[혼공S] 02 - 2 '데이터베이스 시작부터 끝까지' 정리 (0) | 2024.07.05 |