서브쿼리는 SELECT
명령에 의한 데이터 질의로, 상부가 아닌 하부의 부수적인 질의를 의미한다.
서브쿼리
(SELECT 명령)
SELECT
명령으로 괄호로 묶어 지정한다.SELECT 명령
이라고 적었지만 SELECT 구
, FROM 구
, WHERE 구
등 SELECT
명령의 각 구를 기술할 수 있다.WHERE
구는 SELECT
, DELETE
, UPDATE
구에서 사용할 수 있는데 이들 중 어떤 명령에서든 서브쿼리를 사용할 수 있다.a
열의 값이 가장 작은 행을 제거하고 싶다면 DELETE
명령과 SELECT
명령을 결합시켜서 제거할 수 있다.최솟값을 가지는 행 삭제하기
DELETE FROM sample54 WHERE a = (SELECT MIN(a) FROM sample54);
서브쿼리를 사용하면 이렇게 DELETE
와 SELECT
를 결합시킬 수 있다.
괄호로 둘러싼 서브쿼리 부분을 먼저 실행한 후 DELETE
명령을 실행한다고 생각하면 이해하기 쉬울 것이다.
단, MySQL
에서는 위 예제 쿼리를 실행할 수 없음에 주의해야 한다.
대신 DELETE
명령을 SELECT
명령으로 바꾸면 실행할 수 있다.
MySQL
에서 예제를 실행하면 You can't specify target table 'sample54' for update in FROM clause
라는 에러가 발생한다. 데이터를 추가하거나 갱신할 경우 동일한 테이블을 서브쿼리에서 사용할 수 없도록 되어있기 때문이다.DELETE FROM sample54 WHERE a = (SELECT a FROM (SELECT MIN(a) AS a FROM sample54) AS x);
한편 SQL에는 순차형 언어에서처럼 변수가 존재하지 않는다. 만약 변수를 사용할 수 있다고 한다면, 다음과 같이 정리해 표현할 수 있을 것이다.
변수 = (SELECT MIN(a) FROM sample54);
DELETE FROM sample54 WHERE a = 변수;
클라이언트 변수
앞서 언급한 변수에 관한 것으로, mysql 클라이언트에 한해 다음과 같이 구현할 수 있다.
이때 @a가 변수가 되고 set이 변수에 대입하는 명령이 된다.
mysql> set @a = (SELECT MIN(a) FROM sample54);
mysql> DELETE FROM sample54 WHERE a = @a;
SELECT
명령이 어떤 값을 반환하는지 주의할 필요가 있다.서브쿼리의 패턴
패턴 1. 하나의 행과 하나의 값을 반환하는 패턴
SELECT MIN(a) FROM sample54;
패턴 2. 복수의 행이 반환되지만 열은 하나인 패턴
SELECT no FROM sample54;
패턴 3. 하나의 행이 반환되지만 열이 복수인 패턴
SELECT MIN(a), MAX(no) FROM sample54;
패턴 4. 복수의 행, 복수의 열이 반환되는 패턴
SELECT no, a FROM sample54;
단일 값
으로 통용되지만 데이터베이스 업계에서는 스칼라 값
이라 불리는 경우가 많으므로 기억해 두자.SELECT
명령을 특별 취급하는 이유는 서브쿼리로서 사용하기 쉽기 때문이다.SELECT
명령을 작성하고자 한다면 SELECT
구에서 단일 열을 지정한다.SELECT
구에서 하나의 열을 지정하고, GROUP BY
를 지정하지 않은 채 집계함수를 사용하면 결과는 단일한 값이 된다.GROUP BY
로 그룹화를 하면 몇 가지의 그룹으로 나뉘어져 버릴 가능성이 있기 때문에 결과적으로 단일한 값이 반환되지 않을 수 있다.WHERE
조건으로 하나의 행만 검색할 수 있다면 단일 값이 되므로 스칼라 값을 반환하는 SELECT
명령이 된다.WHERE
구에서 스칼라 값을 반환하는 서브쿼리는 =
연산자로 비교할 수 있다는 뜻이다.DELETE FROM sample54 WHERE a = (SELECT MIN(a) FROM sample54);
SELECT
명령으로 되어있으므로 =
연산자를 사용해 열 a
의 값과 비교할 수 있다.SELECT
구에서 다른 열을 지정하거나 GROUP BY
를 지정하면 바로 에러가 발생한다.스칼라 서브쿼리
라 부르기도 한다.HAVING
구를 설명할 때 '집계함수는 WHERE
구에서는 사용할 수 없다'라고 설명했었다.스칼라 서브쿼리
라면 WHERE
구에 사용할 수 있으므로 집계함수를 사용해 집계한 결과를 조건식으로 사용할 수 있다.GROUP BY
에서 지정한 열 이외의 열을 SELECT
구에 지정하면 에러가 난다'라는 것도 있었다.SELECT
구, UPDATE
의 SET
구 등 다양한 구 안에서 지정할 수 있다.하나의 항목
으로 취급한다.SELECT
구에서 서브쿼리를 지정할 때는 스칼라 서브쿼리가 필요하다.SELECT 구에서 서브쿼리 사용하기
SELECT
(SELECT COUNT(*) sample51) AS sq1,
(SELECT COUNT(*) sample54) AS sq2;
SELECT
명령에는 FROM
구가 없다는 것이다. MySQL
등에서는 실제로 FROM
구를 생략할 수 있다.Oracle
등 전통적인 데이터베이스 제품에서는 FROM
를 생략할 수 없다.Oracle
에서는 다음과 같이 FROM DUAL
로 지정하면 실행할 수 있다.DUAL
은 시스템 쪽에서 데이터베이스에 기본으로 작성되는 테이블이다.SELECT 구에서 서브쿼리 사용하기(Oracle의 경우)
SELECT
(SELECT COUNT(*) sample51) AS sq1,
(SELECT COUNT(*) sample54) AS sq2 FROM DUAL;
UPDATE
의 SET
구에서도 서브쿼리를 사용할 수 있다.SET 구에서 서브쿼리 사용하기
UPDATE sample54 SET a = (SELECT MAX(a) FROM sample54);
SET
구에서 서브쿼리를 사용할 경우에도 스칼라 값을 반환하도록 스칼라 서브쿼리를 지정할 필요가 있다.UPDATE
명령을 실행하면 a 열 값이 모두 a 열의 최댓값으로 갱신된다.UPDATE
명령과 관련이 있는 조건식으로 지정되지 않으면 별 의미가 없다(차차 설명함).FROM
구에서도 서브쿼리를 사용할 수 있다.FROM
구에 서브쿼리를 지정하는 경우에도 서브쿼리의 기술방법은 같다.SELECT
명령을 묶으면 된다. 다만 FROM
구에는 기본적으로 테이블을 지정하는 만큼 다른 구와는 조금 상황이 다르다.SELECT
구나 SET
구에서는 스칼라 서브쿼리를 지정해야 하지만 FROM
구에 기술할 경우에는 스칼라 값을 반환하지 않아도 된다. 물론 스칼라 값이라도 상관없다.FROM 구에서 서브쿼리 사용하기
SELECT * FROM (SELECT * FROM sample54) sq;
SELECT
명령 안에 SELECT
명령이 들어있는 듯 보이는데, 이를 네스티드(nested) 구조
, 또는 중첩구조
나 내포구조
라 부른다.Sub Query
의 이니셜에서 따온 것이다.SELECT
구에서는 열이나 식에 별명을 붙일 수 있다.FROM
구에서는 테이블이나 서브쿼리에 별명을 붙일 수 있다.SELECT
구에서 별명을 붙일 때처럼 AS
키워드를 사용하여 지정한다(단, Oracle
에서는 AS
를 붙이면 에러가 발생한다. Oracle
에서는 AS
를 붙이지 않는다).FROM 구에서 서브쿼리 사용하기(3단계)
SELECT * FROM (SELECT * FROM (SELECT * FROM sample54) sq1) sq2;
LIMIT
구에 관해서 설명할 때 Oracle
에는 LIMIT
구가 없다고 했습니다.ROWNUM
으로 행 개수를 제한할 수 있지만, 정렬 후 상위 몇 건을 추출하는 조건은 붙일 수 없었다.ROWNUM
의 경우 WHERE
구로 인해 번호가 할당되기 때문이다.FROM
구에서 서브쿼리를 사용하는 것으로 Oracle
에서도 정렬 후 상위 몇 건을 추출한다는 행 제한을 할 수 있다.Oracle에서 LIMIT 구의 대체 명령
SELECT * FROM (
SELECT * FROM sample54 ORDER BY a DESC
) sq
WHERE ROWNUM <= 2;
INSERT
명령과 서브쿼리를 조합해 사용할 수도 있다.INSERT
명령에는 VALUES
구의 일부로 서브쿼리를 사용하는 경우와, VALUES
구 대신 SELECT
명령을 사용하는 두 가지 방법이 있다.VALUES
구의 값으로 서브쿼리를 사용하는 경우 서브쿼리는 스칼라 서브쿼리로 지정할 필요가 있다. 물론 자료형도 일치해야 한다.VALUES 구에서 서브쿼리 사용하기
INSERT INTO sample541 VALUES(
(SELECT COUNT(*) FROM sample51),
(SELECT COUNT(*) FROM sample54)
);
결과
|a|b|
|-|-|
|5|3|
VALUES
구 대신에 SELECT
명령을 사용하는 예를 살펴보자.SELECT 결과를 INSERT하기
INSERT INTO sample541 SELECT 1, 2;
결과
|a|b|
|-|-|
|5|3|
|1|2|
INSERT SELECT
라 불리는 명령으로 INSERT
와 SELECT
를 합친 것과 같은 명령이 되었다.SELECT
가 결괏값으로 1과 2라는 상수를 반환하므로, INSERT INTO sample 541 VALUES (1, 2)
의 경우와 같다.SELECT
명령이 반환하는 값이 꼭 스칼라 값일 필요는 없다.SELECT
가 반환하는 열 수와 자료형이 INSERT
할 테이블과 일치하기만 하면 된다.INSERT SELECT
명령은 SELECT
명령의 결과를 INSERT INTO
로 지정한 테이블에 전부 추가한다.SELECT
명령의 실행 결과를 클라이언트로 반환하지 않고 지정된 테이블에 추가하는 것이다.INSERT SELECT
명령으로 행을 복사할 수도 있다.테이블의 행 복사하기
INSERT INTO sample542 SELECT * FROM sample543;