최근 수정 시각 : 2019-07-17 18:11:23

Structured Query Language

SQL에서 넘어옴
파일:나무위키+유도.png   미국의 공항에 대한 내용은 샌 카를로스 공항 문서를 참조하십시오.
TIOBE에서 선정한 검색어 점유율 상위 20개 프로그래밍 언어
Java C Python C++ C#
VB .NET JavaScript PHP SQL Objective-C
Ruby 어셈블리어 Swift MATLAB Groovy
Go 비주얼 베이직 Delphi/Object
Pascal
Perl R

结构化查询语言(결구화사순어언)[1] / Structured Query Language: SQL
#!syntax sql
begin
    dbms_output.put_line('hello world'); 
end;

파일:18a311c9932a4327ded4436577bda857.png
1. 개요2. 상세3. 구문 설명

1. 개요

에스큐엘, 혹은 시퀄이라고 읽는다. 구조적 데이터 질의 언어. 데이터베이스에서 자료를 처리하는 용도로 쓰인다.

로고는 SQL을 사용하는 회사별로 색상은 다르게 표현된다. 그러나 마크 자체는 원통을 쌓은 모양이 공용으로 쓰인다.

2. 상세

어느 데이터베이스에서나 기본으로 제공하기 때문에 배워두면 여러 곳에서 쓸데가 많다. 다만, NoSQL 계열에서는 SQL 문을 사용하지 않는다.

그러나 DBMS 벤더[2]에 따라 사용하는 SQL에는 다소 차이가 있다. 그래서 ANSI SQL이라고 하는 표준 SQL 구문이 있으나 DBMS 시장을 독식하고 있는 오라클이 잘 지키지 않아서 거기다가 오픈소스 DBMS인 MySQL도 표준 따위는 지키질 않는다. 현실은 시궁창. 최근 인기를 얻고 있는 PostgreSQL은 표준 SQL을 잘 지키고 있다. 일반적으로 사용하는 데이터베이스는 RDB(관계형 데이터베이스)이며, RDB에서 데이터 처리는 데이터 모음인 테이블[3]을 기준으로 이루어지기 때문에 SQL 역시 테이블을 염두에 두고 읽으면 이해하기 쉽다.

아래 구문 설명 및 예제는 공통적으로 대문자는 키워드, 소문자는 이름이나 값 등 변수를 의미하고, 주로 사용되는 WHERE 옵션은 중괄호로 표기한다.[4] SQL은 대소문자를 구분하지 않으므로 SELECT와 select는 같은 것이다. 물론 따옴표로 인용된 '값'은 대소문자를 구분한다. 또한 대부분의 DBMS는 세미콜론(;)을 입력하지 않으면 명령어를 계속 입력하고 있다고 간주한다. GUI를 사용할 경우 GUI프론트엔드에서 알아서 세미콜론을 붙여 주기 때문에 잘 모르고 넘어갈 수 있는데 터미널로 작업할 경우에는 이것 때문에 셸이 먹통이 되는 경우가 있다.

그리고 쿼리를 연습할 때 주의할 점이 한 가지 있는데 SQL 구문은 탐욕스럽다(Greedy). SQL은 가능한 넓은 범위에 걸처 작업하려고 한다. 따라서 WHERE절이나 LIMIT 구문을 생략하면 SELECT의 경우 끝도 없이 출력하는 레코드열을 보게 될 것이고 UPDATE와 DELETE의 경우 사보타주에 가까운 광역 변조(파괴)가 일어난다. 또한 대부분의 DBMS에서는 트랜잭션 BEGIN을 먼저 걸고 작업하지 않는 한 작업을 취소(UNDO)할 수 없다. 다시 말해 BEGIN을 입력하지 않고 DELETE from table; 을 입력한 경우 ROLLBACK 명령을 입력해도 소용이 없다. 가장 치명적인 오타로 WHERE절 입력 직전에 따옴표를 닫는다는게 바로 옆의 세미콜론을 잘못 쳐서 오타를 낸 경우. 이 경우 앞의 명령어를 모든 레코드에 대해 수행해서 테이블을 파괴해 버린 뒤에 뒤쪽 명령어를 '문법 오류'로 출력한다.

따라서 UPDATE, DELETE를 실행해보기 전에는 반드시 BEGIN을 입력하여 트랜쟥션을 시작한 뒤에 해당 쿼리를 실행하고, 실행 결과가 의도한 대로인지를 SELECT로 검증한 뒤에 의도한 결과대로 쿼리가 수행되었음을 확인하고 나서, COMMIT을 입력하여 트랜잭션을 '적용'하는 습관을 들이는 게 좋다. 만약 뭔가가 잘못되었을 경우 즉시 ROLLBACK 명령을 입력하면 BEGIN을 입력한 뒤에 행한 모든 작업이 취소된다. GUI의 경우에는 여러 쿼리를 입력했을 경우 마지막 SELECT의 결과를 보여주는 기능이 탑재되어 있다는 전제 아래(그렇지 않은 GUI 프론트엔드도 있다. 이 경우 그냥 OK 한 줄 보여주고 끝이다) 처음부터 BEGIN; ~ ROLLBACK; 블럭을 미리 입력해 두고 작업을 하는 게 안전하다.

일단 SQL을 입력할 때에는 절대 함부로 엔터키를 눌러선 안 된다. 지적확인을 하는 게 도움이 될 수도 있다. 특히 WHERE절 앞뒤를 매우 꼼꼼하게 살펴야 하고 WHERE절이 없는 쿼리는 무조건 틀렸다고 간주하자. SQL은 기본값이 rm -rf /라고 생각하는 게 편하다. 아예 날쿼리를 함부로 넣지 말라고 프로시저(Procedure)라는 쿼리 템플릿 기능을 제공한다.

참고로 SQL과 PL/SQL은 다른 것이다. PL/SQL이 SQL을 포함하고 있기는 하지만 SQL을 자유자재로 쓸 정도의 실력이 되지 않았는데 PL/SQL을 공부하면 안 된다. 금융계에서는 PL/SQL을 널리 쓰니까 은행에 개발자로 취업하려면 꼭 배워야 하지만 배울 때 배우더라도 SQL을 자신있게 다룰 수 있게 된 다음의 일이다. 진도를 빨리 빼고 싶은 욕심에 SQL도 모르는 채로 PL/SQL에 도전했다간 기존에 공부한 데이터베이스의 이론체계에 혼동을 일으켜 오히려 진도가 뒤로 후퇴한다. 레알 주화입마

3. 구문 설명

  • INSERT : 데이터를 입력하는 구문. 형식은 다음과 같다.
INSERT INTO table(field1, field2, ...) VALUES (value1, value2, ...);[5]
table에 field1=value1, field2=value2, ...와 같은 속성값을 가지는 항목을 새로 생성하여 삽입한다. 만약 테이블에 입력될 값이 문자라면 반드시 '' 으로 문자열 (String)임을 표시해 주어야 탈이 없다. 이는 UPDATE 구문에서도 마찬가지. table 이름 바로 뒤에 필드 목록을 생략해도 되지만 생략하려면 VALUES 뒤의 것의 갯수와 내용이 테이블 스키마와 정확히 일치해야 한다.
  • UPDATE : 데이터를 수정하는 구문.
UPDATE table SET field1=value1, field2=value2, {WHERE 조건};
table의 field1에 value1, field2에 value2, ... 로 변경한다. WHERE 절이 없을 경우 테이블의 모든 항목이 바뀌므로, 대개 WHERE절과 함께 사용하여 범위를 한정해 준다.
ex) UPDATE document SET title='무제' WHERE author='무명씨';
작성자가 '무명씨'인 모든 문서의 제목을 '무제'로 바꾸는 명령.
  • DELETE : 데이터를 삭제하는 구문.
DELETE FROM table {WHERE 조건};
말 그대로 데이터를 삭제하는 구문이다. 조건절이 없을 경우 테이블의 모든 데이터를 비우게 되므로[6], 대개 조건절과 함께 사용된다.
  • SELECT : 데이터를 읽어오는 구문. 현업에서 사용하다 보면 가장 복잡하고 고려할 사항이 많은, 중요하고 어려운 구문이다.
SELECT field1, field2, ... FROM table {WHERE 조건};
서비스시 가장 빈번하게 사용되는 구문이다. 테이블에서 지정된 값을 가져오는 구문이며, 모든 값을 가져오기 위해 으레 field 부분을 생략하고 *로 표기하기도 한다.[7][8]
ex) SELECT * FROM document WHERE author='무명씨';
작성자가 '무명씨'인 모든 문서의 모든 항목를 가져오는 구문.
SELECT title FROM document WHERE date > to_date('2011/09/01', 'yyyy/mm/dd/');
2011년 9월 1일 이후로 작성된 모든 문서의 제목을 가져오는 구문(to_date 함수는 문자열을 시간 데이터로 바꾸는 함수로 오라클 한정).
SELECT * FROM document WHERE no between 1 and 10;
1부터 10의 번호를 가지는 document 테이블 내의 모든 항목을 가져오는 구문.
SELECT * FROM document WHERE author in ('무명씨','홍길동');
작성자가 무명씨, 홍길동인 문서의 모든 항목을 가져오는 구문.
SELECT * FROM document WHERE author like '김*';[9]
작성자가 김으로 시작되는 문서의 모든 항목을 가져오는 구문.

실제 업무에서는 여러 쿼리를 조합하거나 테이블을 UNION, INTERSECTION, JOIN 등으로 가공하여 사용하는 경우가 많으므로 쿼리가 매우 복잡해진다.
{{{
SELECT document FROM (
SELECT document, COUNT(document) cnt
FROM rel_document_keyword
WHERE keyword IN (
SELECT id FROM keyword WHERE keyword IN ('key1', 'key2', 'key3', 'key4')
) GROUP BY document
) WHERE cnt=4;
}}}

역 인덱스 문서 검색 쿼리인데 특정 키워드가 포함된 문서를 고속으로 찾는 쿼리이다. key1, key2, key3, key4라는 키워드가 모두 포함된 문서를 찾는다.
  • DROP : 개체 삭제
DROP TABLE table_name;

데이터베이스 내의 개체를 삭제해 버린다. 즉, 테이블 내의 데이터 뿐만 아니라 구조까지 모조리 날려버리는 궁극의 명령어이다. 실수로 사용중인 DB 내에서는 쓰지 않기를 바란다. 만약 이 명령어로 회사의 데이터를 날려먹었을 경우... 상사에게 깨지거나 회사에서 해고당하는 정도로 끝나면 다행으로, 고소장이 날아오는 경우도 많다고 한다. 참고로 DB의 DROP 명령은 트랜젝션의 보호를 받지 못해 Rollback(UNDO)이 안 된다.

DROP DATABASE database_name;

데이터베이스 내의 모든 테이블, 스키마, 관계(Relation)를 전부 삭제한다. 워낙에 치명적인 명령어라서 슈퍼유저 권한이 아니면 명령이 먹히지 않는다. 어쨌거나 이 명령이 서비스중인 DB에서 실행되면 해당 서버를 물리적으로 파괴한 것과 동등한 위력을 발휘한다.
  • UNION, JOIN : 둘 이상의 테이블을 결합
(SELECT * FROM `table_a`) UNION (SELECT * FROM `table_b`);
SELECT a.*, b.* FROM `table_a` a LEFT OUTER JOIN `table_b` b;

둘 이상의 테이블을 묶어서 가져온다. UNION의 경우는 아래쪽에 붙이고, JOIN은 옆에 붙인다는 차이점이 있다. UNION의 경우 ALL, JOIN의 경우 FULL, INNER, OUTER, LEFT, RIGHT 등 보조적인 명령어를 사용하여 원하는 대로 조합할 수 있다.

한편 UNION은 테이블의 열 개수가 다르면 에러를 뱉어내고(...), JOIN은 열 이름이 같은 게 있을 경우 애로사항이 꽃핀다는 단점이 있다. UNION은 SQL 주입(SQL injection)이라는 해킹 기법으로 악용될 수 있다. 테이블 정보를 알고 있다면 이를 이용해서 DB상에 있는 회원 정보를 빼오거나, 그냥 DB 테이블 자체를 날려버릴 수 있으니 로그인 폼을 포함한 모든 곳에서 이를 제대로 필터링해야만 한다. 꼭 해라 두 번 해라. 클라이언트에서 한번 에서 한번.

이를 방지하기 위해 Prepared statement라는 것도 나와 있는데, 변수가 들어갈 부분은 ?[10]로 먼저 넣어두고, 그 다음에 쿼리문의 JIT 컴파일 과정을 거친 뒤 진짜 변수를 넣어 쿼리 명령이 아님을 명시하는 기법이다. Prepared statement의 경우 컴파일 과정에서 실행계획(어떻게 데이터베이스를 건드려야 빨리 결과가 나올지)이 세워진다. 루프를 돌면서 동일한 SQL을 계속 실행해야 한다면 루프 밖에서 Prepared Statement를 미리 정의한 후 사용하자. 루프가 돌아가는 속도가 현격히 빨라진다. 보안을 생각한다면 100% Prepared statement를 사용하는게 좋다. 사실 기업 환경에서는 보통 이렇게 한다.

이외에도 '프로시저(Procedure)'라는 것도 있는데, 다수의 SQL을 묶어서 함수화시켜놓은 것이다. Prepared statement와 조합해서 SQL 주입을 원천적으로 차단할 수 있다.


[1] SQL 쪽에서 설정한 공식 중국어 표기. 간체자가 표준이다.[2] 대표적으로 Oracle DB, MySQL, MariaDB, MSS QL 등이 있다.[3] 단일 주제에 대해 행과 열로 이루어진 데이터 집합이다. 주로 행은 데이터 원소(entity)이며 열은 데이터 원소가 가지는 속성값으로 표현된다. 표나 엑셀 시트 하나를 생각하면 이해하기 쉬운데 예를 들어 '고객'이라는 테이블이 있다고 가정하면 고객 한명은 ID, 이름, 주소, 전화번호 등의 값을 가진다. 각각의 고객은 하나의 행을 차지하여 첫번째 열에는 고객의 ID가, 두번째 열에는 고객의 이름이, 세번째 열에는 고객의 주소가...와 같이 지정되어 저장된다. 테이블에서 열의 순서는 중요하지 않으며 입출력시 열의 이름을 정확하게 지정하여 준다.[4] 물론 옵션은 WHERE 말고도 GROUP BY, COUNT 등 목적에 따라 여러가지 있을 수 있으나 구문 설명을 위해 WHERE절을 사용하지 않을 수 없으므로 WHERE절만 표기한다. 다른 옵션에 대해서는 추가바람.[5] MySQL/MariaDB에서는 SET을 사용해서 후술할 UPDATE 구문과 비슷한 꼴로 만들 수 있다. 다만 쿼리 한 번에 하나씩만 넣을 수 있어서 일장일단이 있다.[6] 데이터를 통째로 비우는 명령으로 TRUNCATE가 있는데, 조건절 없이 사용되는 DELETE 구문과 TRUNCATE 구문은 실행방식이 약간 다르다. 이를테면, TRUNCATE 구문은 테이블을 마치 갓 생성한 것처럼 최적화를 시켜 주지만(자동증가값 초기화, 테이블 오버헤드 초기화 등), DELETE 구문은 그렇지 못하다.[7] 이 경우 프로그램 레벨에서 필드값의 순서를 알고 있어야 하므로 그다지 권장되는 방법은 아니다.[8] 그나마 유용한 구문이라면 COUNT(*) 같은 식으로 해당되는 값의 개수를 알아내는 용도로 쓰는 것이다.[9] '김%' 꼴로 표현하기도 한다.[10] PDO의 경우 변수명을 지정할 수 있다.