SELECT 문은 구축이 완료된 테이블에서 데이터를 추출하는 기능을 한다.
SELECT의 가장 기본 형식은 SELECT ~ FROM ~ WHERE이다. SELECT 바로 다음에는 열 이름이, FROM 다음에는 테이블 이름이 나온다. WHERE 다음에는 조건식이 나오는데, 조건식을 다양하게 표현함으로써 데이터베이스에서 원하는 데이터 뽑아낼 수 있다.
가수 그룹의 리더는 물건을 사기 위해서 회원가입을 한다. 입력한 회원 정보는 회원 테이블(member)에 입력된다. 물론, 더 많은 정보를 입력해야 하지만 간단히 아이디/이름/인원/주소/국번/평균 키/데뷔 일자 등만 입력하는 것으로 하자.
회원가입 후, 인터넷 마켓에서 물건을 구입하면 회원이 구매한 정보는 구매 테이블(buy)에 기록된다. 그러면 인터넷 마켓의 배송 담당자는 구매 테이블(buy)을 통해서 회원이 주문한 물건을 준비하고, 회원 테이블(member)에서 구매 테이블(buy)의 아이디와 일치하는 회원의 아이디를 찾아서 그 행의 주소로 물품을 배송한다.
market_db에서 구매 테이블(buy) 아이디는 FK(Foreign Key, 외래 키)로 지정되어 있다.
먼저 인터넷 마켓 데이터베이스(market_db)를 만드는 SQL이 저장된 파일을 다운로드하자. 한빛미디어 사이트에서 다운로드 가능!
MySQL Workbench를 실행해서 열려 있는 쿼리 창 모두 닫는다. [File] - [Open SQL Scrip] 메뉴 선택 후 Open SQL Script 창에서 앞에서 다운로드한 'market_db.sql'을 선택한 후 [열기] 버튼을 클릭한다.
Execute the selected portion of the script or everything 아이콘을 클릭해서 SQL을 선택한다. [Result Grid] 창의 하단에서 [member 1] 탭을 클릭해서 회원 테이블(member)의 완성된 상태를 확인해보자.
이번에는 [Result Grid] 창의 하단에서 [buy 2] 탭을 클릭해서 구매 테이블(buy)을 확인해보자. 이로써 '인터넷 마켓 DB 구성도'가 완성되었다.
DROP DATABASE IF EXISTS market_db;-----①
CREATE DATABASE market_db;-------------②
① DROP DATABASE는 market_db를 삭제하는 문장. 이를 market_db.sql을 처음 실행할 때는 필요 X. 하지만 책 학습하다 보면 다시 market_db.sql을 실행할 일이 있기 때문에 기존의 market_db를 삭제한 것.
② 데이터베이스를 새로 만든다. 이 과정은 2장에서 실습했던 것과 동일한 역할을 한다!
① USE 문은 market_db 데이터베이스를 선택하는 문장이다. 2장에서는 MySQL Workbench의 [SCHEMAS] 패널에서 shop_db 데이터베이스를 더블 클릭해서 선택했다. 그 동작과 동일한 효과!
② member 테이블 만드는 과정. '인터넷 마켓 DB 구성도'에는 열 이름이 한글로 표현되었는데, 여기서는 영문으로 표현. 주석으로 해당하는 한글 이름 표기해 두었다.
① 구매 테이블(buy) 생성
② AUTO_INCREMENT가 처음 나왔다. 자동으로 숫자 입력해준다는 의미.
즉, 순번은 직접 입력할 필요 없이 1, 2, 3, .... 과 같은 방식으로 자동으로 증가.
③ FOREIGN KEY도 처음 나옴!
이제는 데이터 입력하는 INSERT 문 살펴보자. 회원 테이블(member)과 구매 테이블(buy) 1개씩만 살펴보기.
INSERT INTO member VALUES('TWC', '트와이스', '9', '서울', '02', '11111111', 167, '2015.10.29);------- ①
INSERT INTO buy VALUES(NULL, 'BLK', '지갑', NULL, 30, 2);------------②
① 회원 테이블(member)에 값 입력. CHAR, VARCHAR, DATE 형은 작은따옴표로 값 묶어줌. INT 형은 작은따옴표 없이 그냥 넣어주면 된다.
② 구매 테이블(buy)의 첫 번쨰 열인 순번(num)은 자동으로 입력되므로 그 자리에는 NULL이라고 써주면 된다. 그러면 알아서 1, 2, 3,...으로 증가하면서 입력됨. 여기서는 처으미므로 1이 입력.
market_db.sql 파일의 마지막 2개 행에서는 입력된 내용 확인하기 위해서 SELECT로 조회.
SELECT * FROM member;
SELECT * FROM buy;
다음 두 SQL을 입력하고 한꺼번에 실행. 예상대로 별도의 탭으로 동시에 결과가 나온다. 그런데 이 두 SQL은 앞으로 상당히 자주 사용된다고 가정해보자. 매번 두 줄의 SQL을 입력해야 한다면 상당히 불편할 것이고, SQL의 문법을 잊어버리거나 오타를 입력할 수도 있다.
SELECT * FROM member WHERE member_name = '나훈아';
SELECT * FROM product WHERE product_name = '삼각김밥';
두 SQL을 하나의 스토어드 프로시저로 만들어보자. 다음 SQL을 입력하고 실행해보자. 첫 행과 마지막 행에 구분 문자라는 의미의 DELIMITER // ~ DELIMITER; 문이 나왔다. 일단 이것은 스토어드 프로시저를 묶어주는 약속으로 생각하자. 그리고 BEGIN과 END 사이에 SQL 문을 넣으면 된다.
DELIMITER //
CREATE PROCEDURE myProc() → 스토어드 프로시저 이름 지정
BEGIN
SELECT * FROM member WHERE member_name = '나훈아';
SELECT * FROM product WHERE member_name = '삼각김밥';
END //
DELIMITER ;
이제부터는 두 줄의 SQL 문을 실행할 필요 없이 앞에서 만든 스토어드 프로시저를 호출하기 위해서 CALL 문을 실행하면 된다. 다음 SQL 실행하면 동일한 것을 확인할 수 있다.
CALL myProc()
+ CREATE 문과 DROP 문
테이블, 인덱스, 뷰, 스토어드 프로시저 등의 데이터베이스 개체를 만들기 위해서는 CREATE 개체종류 개체이름 ~~ 형식을 사용한다. 반대로 데이터베이스 개체를 삭제하기 위해서는 DROP 개체종류 개체이름 형식을 사용한다. 예로, 실습에서 생성한 스토어드 프로시저를 삭제하면 DROP PROCEDURE myProc를 사용한다.
MySQL 워크벤치에서 생성, 삭제하려면 먼저 [SCHEMAS] 패널의 빈 곳에서 마우스 오른쪽 버튼을 클릭하고 [Refresh All]을 선택해서 새로 고침한다. 그리고 생성, 삭제할 데이터 베이스 개체에서 마우스 오른쪽 버튼을 클릭하고 생성하려면 [Create 데이터베이스개체]를, 삭제하려면 [Drop 데이터베이스_개체]를 선택한다._
데이터 조회하기
market_db.sql 파일의 마지막 2개 행에서는 입력된 내용을 확인하기 위해서 SELECT로 조회했다.
SELECT FROM member;
SELECT FROM buy;
이렇게 해서 MySQL 워크벤치에서 데이터베이스를 구축하는 것과 동일한 작업을 SQL로도 진행할 수 있다는 것을 확인했다. 다시 이야기하지만, SQL 내용까지 완전히 이해하지는 못해도 상관없다. 앞으로 차근차근 학습하면서 자연스럽게 이해될테니!
이제는 본격적으로 '인터넷 마켓 DB 구성도'를 활용해서 SELECT 문을 배워보자.
SELECT 문을 실행하려면 먼저 사용할 데이터베이스를 지정해야한다. 현재 사용하는 데이터베이 스를 지정 또는 변경하는 형식은 다음과같다.
[ USE 데이터베이스_이름; ]
market_db를 사용하려면 쿼리 창에 다음과 같이 입력한다.
USE market_db;
이렇게 지정해 놓은 후에 다시 USE 문을 사용하거나 다른 DB를 사용하겠다고 명시하지 않으면 앞으로 모든 SQL 문은 marketdb 에서 수행된다.
_MySQL 워크벤치를 재시작하거나 쿼리 창을 새로 열면 다시 USE를 실행 해야한다.
_+ 여기서 잠깐 USE를 잘못 사용한 예시
다음 SQL을 실행하면 오류가 발생한다. 현재 선택된 sys 데이터베이스에는 member라는 테이블이 없기 때문에 발생 한 에러이다. 여기서는 sys가 아닌 marketdb를 선택 해야한다.
USE sys;
SELECT FROM member;
오류 메시지
Error Code: 1146. Table 'sys.member' doesn't exist
SELECT 문은 처음에는 사용하기 간단하지만, 사실 상당히 복잡한 구조를 갖는다. MySQL 매뉴얼에 나온 것은 너무 복잡해서 여기서는 간단한 형태로 먼저 살펴보자.
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)]
대괄호([])로 묶인 부분은 생략이 가능합니다.
간단하다고 이야기했지만, 별로 간단해 보이지는 않지만, 좀 더 알아보기 쉽게 핵심만 표현해서 다시 살펴보자. 3장과 4장에서는 이 문장에 대한 이해와 실습이 대부분. 하나씩 차근차근 살펴보자.
SELECT 열_이름
FROM 테이블 이름
WHERE 조건식
GROUP BY 일 이름
HAVING 조건식
ORDER BY 열 이름
LIMIT 숫자
여기서는 다음과 같은 기본적인 핵심적인 형식 먼저 살펴보자.
SELECT 열_이름
FROM 테이블_이름
WHERE 조건식
다시 USE 문으로 market_db를 선택하고 SELECT 문으로 회원 테이블(member)을 조회해보자.
USE market_db;
SELECT FROM member;
SELECT 문을 하나하나 살펴보기.
① 테이블에서 데이터를 가져올 때 사용하는 예약어. 가장 많이 사용하게 될 것.
② 일반적으로 '모든 것'을 의미. 그런데 *
가 사용된 위치가 열 이름이 나올 곳이므로 모든 열을 말함. 여기서는 member 테이블의 8개 열 모두를 의미.
③ FROM 다음에 테이블 이름이 나온다. 테이블에서 내용을 가져온다는 의미.
결국 풀어서 쓰면 'member 테이블에서 모든 열의 내용을 가져와라'라는 뜻. 결과는 10건의 회원이 출력됨.
원래 테이블의 전체 이름은 데이터베이스_이름, 테이블 이름 형식으로 표현한다. '인터넷 마켓 DB 구성도'를 예로 든다면 이 테이블의 전체 이름은 market_db.member 이다. 그렇기 때문에 원칙 적으로는 다음과 같이 사용해야 한다.
SELECT FROM market_db.member;
하지만 데이터베이스 이름을 생략하면 USE 문으로 지정해 놓은 데이터베이스가 자동으로 선택된다. 현재 선택된 데이터베이스가 market_db이므로 다음 두 쿼리는 동일한 것이 됨.
SELECT FROM market_db.member;
SELECT FROM member;
_+ 여기서 잠깐 [Output] 패널의 의미
[Output] 패널에서 제일 앞의 아이콘과 [Message] 정도는 확인하는 것이 좋습니다. [Output] 패널의 의미는 다음과 같 습니다.
이번에는 해당 테이블에서 전체 열이 아닌 필요한 열만 가져오자. 다음과 같이 회원 테이블 (member)의 이름(mem_name) 열만 가져와보자.
SELECT mem_name FROM member;
SELECT addr, debut_date, mem_name FROM member;
+여기서 잠깐 열 이름의 별칭
참고로 열 이름에 별칭(alias)을 지정할 수 있다. 열 이름 다음에 지정하고 싶은 별칭을 입력하면 된다. 별칭에 공백 이 있으면 큰따옴표(')로 묶어줌.
SELECT addr 주소, debut_date "데뷔 일자", mem_name FROM member;
SELECT~FROM은 대부분 WHERE와 함께 사용함. WHERE는 필요한 것들만 골라서 결과 를 보는 효과를 갖는다.
WHERE가 없이 SELECT ~ FROM만으로 테이블을 조회하면 테이블의 모든 행이 출력된다. market_db처럼 데이터의 건수가 적은 경우에는 별 문제가 없지만, 실제 쿠팡/마켓컬리/지마켓/옥 션 등과 같이 회원이 수백만 명 이상 되는 인터넷 쇼핑몰에서 회원 테이블(member)을 다음과 같 이 검색하면 어떨까?
SELECT * FROM 쿠팡_회원_테이블(member);
결과가 엄청나게 많이 나올 듯? 출력된 수백만 건 이상의 결과에서 필요한 데이터를 눈으로 찾아내는 것은 상당히 어려울 뿐 아니라, 이렇게 많은 데이터 를 출력하면 아무리 고성능의 컴퓨터라도 부담이 될 수밖에 없다. 그래 서 학습 등을 할 때와 같이 작은 데이터를 조회할 때를 제외한 SELECT 문은 WHERE 절과 함께 사용한다.
WHERE 절은 조회하는 결과에 특정한 조건을 추가해서 원하는 데이터만 보고 싶을 때 사용한다. 형식은 다음과 같다.
SELECT 열_이름 FROM 테이블 이름 WHERE 조건식;
또는
SELECT 열 이름
FROM 테이블 이름
WHERE 조건식;
이 형식에서 세미콜론(:)이 나오기 전까지는 한 줄로 쓰든, 여러 줄로 쓰든 동일. SQL이 길거나 복잡한 경우에는 여러 줄로 나눠 쓰는 것이 좀 더 읽기 편하다.
지금 찾는 이름(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를 이용해서 조회할 수 있다.
SELECT mem_name, height, mem_number FROM member
평균 키(height)가 165 이상이거나 인원(mem number)이 6명 초과인 회원은 다음과 같이 논리 연산자 OR를 이용해서 조회할 수 있다. AND가 두 조건이 모두 만족해야 하는 것이라면, OR는 두 조건 중 하나만 만족해도 됨!
SELECT men name, height, men number
FRON menber WHERE helght > 165 OR mem_munber > 6;
이번에는 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;
평균 키(height)와 같이 숫자로 구성된 데이터는 크다/작다의 범위를 지정할 수 있으므로 BETWEEN ~ AND를 사용할 수 있지만, 주소(addr)와 같은 데이터는 문자로 표현되기 때문에 어느 범위에 들어 있다고 표현 X. 만약, 경기/전남/경남 중 한 곳에 사는 회원을 검색하려면 다음과 같이 OR로 일일이 써줘야 함.
SELECT mem_name, addr
FROM member
WHERE addr = '경기' OR addr = '전남' OR addr='경남';
한 글자와 매치하기 위해서는 언더바를 사용함. 다음 SQL은 아름(mem name)의 앞 두 글자는 상관없고 뒤는 '핑크'인 회원을 검색한다. 결과는 에이핑크와 블랙핑크가 나왔다.
SELECT *
FROM menber
WHERE mwm_name LIKE '__핑크'; → 언더바 2개
좀 더 알아보기 서브 쿼리
SELECT 안에는 또 다른 SELECT가 들어갈 수 있음. 이것을 서브 쿼리(subquery) 또는 하위 쿼리라고 부른다. 여기서는 이름(mem_name)이 '에이핑크'인 회원의 평균 키(height)보다 큰 회원을 검색하고 싶다고 가정.
우선 에이핑크의 평균 키(height)를 알아내야 한다. 에이핑크의 키가 164임을 알아냄.
SELECT height FROM menber WHERE men_name = '에이핑크';
이제는 164보다 평균기(height)가 큰 회원을 조회하면 됩니다.
SELECT men_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 mes name 에이밍크");
세미콜론(:)이 하나이므로 이 SQL은 하나의 문장이다. SQL 안에 또 SQL이 들어간 모양. 즉, 괄호 안의 SELECT 결과가 161이므로 이 자리에 164를 직접 써준 것과 동일한 효과를 얻었다. 서브 쿼리의 장점은 2개의 SQL을 하나로 만들으로써 하나의 SQL만 관리하면 되므로 미 간단해 진다는 것. 실무에서도 종종 사용되므로 기억해두기!