PHP & Others

MySQL에서 서브쿼리(subquery) 1 장

페이지 정보

본문

MySQL에서 서브쿼리(subquery)  1 장
번역자
김영진(cogolda@hanmail.net)

알아두기

이 문서는 제가 http://www.mysql.com/articles/subqueries_part_1.html/ 이곳에 있는 문서를 번역, 추가, 생략한 내용입니다. 이 문서는 1장이고 mysql 사이트에 계속 연재되면 여기다 올리겠습니다. 아직 까지는 1장만 연재되어 있습니다.
질문이나 의견 있으신 분은 메일이나 코멘트를 이용해 주시면 감사하겠습니다.
이 문서는 서브쿼리를 모르거나, 들어는 봤는데 써 본적 없는 php 프로그래머를 위해 작성되었습니다. 그러나 MySQL과 SQL의 기초는 알아야 합니다.

머리말

우리는 서브쿼리를 깊이있게 알아볼 것이다. 서브쿼리는 복잡하고 큰 주제이다. 그래서 만약 우리가 그것은 약간의 로직 부분으로 나눌 수 있다면, 최고이다. 이 기사에서, 우리는 기본적이고 간결한 서브쿼리와 FROM 절에 있는 서브쿼리를 배울 것이다.

우리가 예제를 살펴보면서, 우리는 MySQL 공식 매뉴얼의 서브쿼리 부분을 열고 싶어할 것이다.

서브쿼리란 무엇인가?

서브쿼리는, 핵심적으로, 다른 SELECT 구문의 부분으로서 자주 사용 되는 SELECT 구문이다. 물론 INSERT, UPDATE, DELETE나 다른 구문과 함께 사용 될 수 있다. 서브쿼리는 다양한 최적화를 위하는 것과 마찬가지로, 매우 복잡한 검색이나 복잡한 보고서를 저장하기 위하여 사용된다. 우리는 GROUP BY 절의 모든 개수 핵심적으로 갖고, 여러분의 결과를 좀더 세밀히 구별하기 위해 SELECT의 FROM 절로 사용할 수 있다.

시작하기

mysql에서 서브쿼리를 사용하기 위해서, 여러분은 MySQL 4.1의 버전이 필요할 것이다. 다만 4.1 alpha 버전만 제외하고, 어떤 버전이든 상관없다(4.1 이상으로). 여러분은 최신 버전을 원할 것이다. 만약 없다면, 이 기사를 읽어보고, 가서 mysql 공식 사이트에 가서 다운받자.

이 기사에 있는, 모든 여제는 world database을 이용할 것이다. 그것은 world.sql.gz으로 mysql 웹 사이트에서 사용할 수 있다.
http://www.mysql.com/get/Downloads/Manual/world.sql.gz/from/pick/

초 간단 예제

음. 처음 볼 것은 가능한한 가장 간결한 서브쿼리이다

SELECT (SELECT 1);

  +------------+
  | (SELECT 1)  |
  +------------+
  |          1    |
  +------------+

이것은 정말 간단하다.

SELECT (SELECT (SELECT 1));

  +---------------------+
  | (SELECT (SELECT 1))  |
  +---------------------+
  |                  1          |
  +---------------------+

어떤가? 그러나 별로 쓸모있진 않다.

보다 쓸모있는 예제
지금부터, 보다 효율적인 방법(JOINs같은)으로 될 수 있는 것들은 무시하자, 그리고 약간의 가능한 서브쿼리과 그 결과를 주목하자. 처음으로, 우리는 다른 방법의 개수가 되게 할수 있는 매우 간결한 쿼리를 볼 것이다.

SELECT name, headofstate
  FROM Country
  WHERE code=(SELECT "SWE");

  +--------+-----------------+
  | name  | headofstate          |
  +--------+-----------------+
  | Sweden | Carl XVI Gustaf    |
  +--------+-----------------+

그래, 나는 인정할 것이다. 그것은 별로 쓸모있지 않다. 가지말고 나와 함께 놀아죠.(Stay with me here!). 만약 여러분이 비슷한 쿼리를 시도한다면, 좀더 유용하게 시간을 써보자. 가장 인구가 많은 도시의 정보를 알아보자.
(population은 인구, head of state는 시장/대통령이라는 뜻)
SELECT name, headofstate, population
  FROM Country
  WHERE population=(SELECT MAX(population) FROM Country);

  +-------+-------------+------------+
  | name  | headofstate | population    |
  +-------+-------------+------------+
  | China | Jiang Zemin | 1277558000    |
  +-------+-------------+------------+

LIMIT를 왜 않쓰냐고?, 그러나 LIMIT는 MySQL-한정적이다, 그래서 다른 데이터베이스에 포터블한 해결책이 아니다. 그리고 항상 아주 정확한 결과가 되지 못한다. (ORDER BY와 LIMIT으로 하면, 만약 정확히 같고, 가장 인구가 높은 두 국가 중 하나만 선택(selete)된다. 위의 서브쿼리는 대부분의 데이터베이스에 호환성이 높다. 보다 복잡한 걸 해보자, 공식언어로 국가에 관한 정보이다.

테이블을 속여보자
FROM 절에서, 서브쿼리는 새로운 서브쿼리 지원하는 가장 유용한 특징중 하나이다. 특히, 그것은 쿼리의 FROM 부분에서 입력을 허락하게 되는 간결한 쿼리문이다. MySQL이 쿼리문을 처리할 때, 그것은 서브쿼리의 결과는 실제로는 다른 테이블이다. 처음에는 간결한 예제로 보자, 내 말을 보여주겠다.

SELECT foo
  FROM (SELECT 1 AS foo) AS tbl;

  +-----+
  | foo  |
  +-----+
  |  1  |
  +-----+

여러분들은 끝 부분을 주목할 것이다. 'FROM (SELECT 1 AS foo) AS tbl' 은 테이블의 목록을 찾기위한 입력이다. 여러분은 또한 내가 AS tbl로 서브쿼리(메인 쿼리 부분) 를 대응하는 것을 주목할 것이다. FROM 절에서, 모든 서브쿼리는 어떤 것이 모든 것이 대응할 것이다. 한편 여러분은 에러를 볼것이다. 모든 테이블은 이름이 있어야 하고 서브쿼리도 예외는 아니다.

만약 우리가 보다 어려운 (그러나 유용한) 예제로 옮긴다면, 우리는 서브쿼리의 힘을 보기시작할 것이다. 모든 국가에서 가장 많은 공식언어가 몇개인지 찾아보자.

/* 1 */
/* AS는 MAX()함수로 나온 값의 임시 필드명을 지정하는 것입니다. */
  SELECT MAX(tbl.nr) AS nr
  FROM
    (
      /* 2 */
      SELECT countrycode, COUNT(*) AS nr
      FROM CountryLanguage
      WHERE isofficial='T'
      GROUP BY countrycode
    ) AS tbl;

  +---------+
  | MAX(nr)  |
  +---------+
  |      4  |
  +---------+

다시, MySQL 전문가인 여러분들 중 몇몇은 ORDER BY 와 LIMIT로 찾았을 같은 결과를 주목할 지도 모른다. 다시, 필자는 서브쿼리 부분을 별명(alias)을 불러야 한다. 그리고 필자는 AS tbl을 선택했다. 서브쿼리 없이 쉽게 할 수 없는 것을 해보자: 공식언어의 최대 개수로 그의 국가에 대한 정보를 얻어보자.


/* 1 */
  SELECT name, population, headofstate, top.nr
  FROM
    Country,
    (
      /* 2 공식언어(official languages)의 개수에 기반한 국가 코드(country codes)를 비교한다.*/
      SELECT countrycode, COUNT(*) AS nr
      FROM CountryLanguage
      WHERE isofficial='T'
      GROUP BY countrycode
      HAVING nr=(
        /* 3 SELECT #4 번으로부터 nr_official_language의 최대크기를 알아낸다. */
        SELECT MAX(summary.nr_official_languages)
        FROM
          (
            /* 4 모든 국가와 각각의 공식언어(official languages)의 개수를 찾는다. */
            SELECT countrycode, COUNT(*) AS nr_official_languages
            /* CountryLanguage 테이블에서 */
            FROM CountryLanguage
            /* 공식언어가 있는 경우에만 */
            WHERE isofficial='T'
            /* countrycode 순서로 */
            GROUP BY countrycode
          /* 원래의 테이블 이름 AS 별칭으로 임시로 쓸 이름 */
          ) AS summary
        )
    ) as top
  WHERE Country.code=top.countrycode

  +--------------+------------+-------------+----+
  | name        | population | headofstate | nr        |
  +--------------+------------+-------------+----+
  | Switzerland  |    7160400 | Adolf Ogi  |  4          |
  | South Africa |  40377000 | Thabo Mbeki |  4      |
  +--------------+------------+-------------+----+

이 쿼리문은 간결하지 않다. 그것은 복잡한 결과를 얻기 위해 네개의 SELECT 구문으로 다른 부분으로 감쌌다. 필자는 /* 1 */ 이런 형식의 주석을 추가했다. 그래서 우리는 논리적으로 쿼리문은 분할할수 있다. 만약 우리는 숫자로 분리된 각 쿼리문을 보고, 역순으로, 이해하면 가장 쉽다.
(복잡한 서브쿼리문을 볼떄는 안에서 바깥으로 보면 쉽다는 얘기입니다)

4. 이 SELECT 문은 모든 국가와 각각의 공식언어(official languages)의 개수를 찾는다.
3. 이 SELECT 문은 SELECT #4 번으로부터 nr_official_language의 최대크기를 알아낸다. 동시에, 최대크기를 위해 countrycode 컬럼을 얻는 방법은 SQL-표준이 아니다. 
2. 이 SELECT에서, 우리는 공식언어(official languages)의 개수에 기반한 국가 코드(country codes)를 비교한다.
1. 마지막으로 우리는 매칭한(matching) 국가에 관해 국가정보(country information)를 찾기 위한 국가(country) 테이블에 기대어 JOIN을 사용할 수 있다.

필자는 여러분은 무언가를 배웠기를 희망한다. 그리고 필자는 서브쿼리에 관해 여러분 스스로 읽기를 바란다, 이 시리즈의 다음 기사는 Correlation, ANY, EXISTS를 논의할 것이다.

역자 한마디..
오류가 많으니 읽으시는 분들이 좀 주의해서 읽어 주시면 감사하겠습니다.


관련자료

등록된 댓글이 없습니다.
Today's proverb
사랑이란 자기 희생이다. 이것은 우연에 의존하지 않는 유일한 행복이다. (톨스토이)