SQL

ANSI 표준 SQL 조인

Jonathan Gennick

Oracle9i에서 새롭게 지원되는 SQL 조인으로 이미 잘 알려진 조인을 더욱 간편하게 사용할 수 있고 완벽한 포괄 조인(outer-join) 지원이 가능하게 되었습니다.

SQL/92 표준 조인 구문 지원은 Oracle9i에 새롭게 추가된 많은 기능 중 하나입니다. 이 지원을 통해 휴대용 애플리케이션 작성이 이전보다 훨씬 더 간편해지고 완벽한 포괄 조인이 가능하게 되었습니다.

Oracle9i 이전의 조인 구문

지금까지 Oracle 데이타베이스는 SQL/86에서 정의된 조인 구문을 지원했습니다. Listing 1은 구 표준 조인 구문의 예를 보여 줍니다.

Oracle9i가 발표되기 전 Oracle 데이타베이스는 독자적인 구문을 사용하여 포괄 조인을 지원했습니다. Listing 2 는 COURSE와 ENROLLMENT의 포괄 조인 예를 보여 줍니다.

WHERE 절에서 ENROLLMENT 테이블 열 이름 다음에 "(+)"를 사용하여 이 테이블을 조인에서 옵션 테이블로 표시한다는 점을 유의하십시오.

기존 조인 구문에는 문제가 존재했습니다. 복잡한 질의에서는 WHERE 절을 구문 분석하여, 질의 결과의 제한 사항과 조인 조건을 분리하는 것이 어려운 경우가 많았습니다. 그리고 프로그래머가 조인 조건을 지정하는 것을 간과하여 데카르트 곱이 나오는 경우가 가끔 있었습니다.

ANSI 표준 조인 구문

ANSI 표준 SQL 조인 구문은 몇 가지 새로운 키워드와 절을 Oracle9i에게 제공하여, SELECT 문의 FROM 절에서 조인을 완벽하게 지정할 수 있습니다. COURSE와 ENROLLMENT 테이블을 조인하는 문제를 살펴 보겠습니다. 기존 방식을 사용한다면, 다음과 같이 질의의 FROM 절에서 두 테이블을 표시하게 됩니다.

FROM courses c, enrollment e

그러나 새로운 ANSI 구문을 사용하면 쉼표(,) 없이 원하는 조인 타입을 명확하게 지정할 수 있습니다. COURSE와 ENROLLMENT 테이블 사이에 배타 조인을 수행하려면 다음과 같이 작성합니다.

FROM courses c 
   INNER JOIN enrollment e

Jonathan Gennick의 웹 사이트 http://gennick.com/에서는 더 다양한 Oracle 팁을 온라인 제공합니다.
배타 조인을 지정했으므로 그 조인의 조건을 지정해야 합니다. 다른 WHERE 절 제한 사항과 조인 조건을 뒤섞이게 하는 대신 FROM 절에 포함된 ON 절에서 조인 조건을 지정할 수 있습니다.

SELECT c.course_name, c.period,
  e.student_name
FROM course c INNER JOIN enrollment e
     ON c.course_name = e.course_name
        AND c.period = e.period;

기존 조인 구문에 익숙한 사람이라면 새로운 조인 구문에 익숙해지는 데 시간이 걸릴 수 있습니다. 그러나 새 구문은 몇 가지 장점을 제공하기 때문에 학습할 만한 가치가 있습니다.

  • 모든 조인 정보는 한 장소에서 지정됩니다. 복잡한 WHERE 구문 이곳 저곳을 옮겨 다니며 조인 조건을 질의 결과의 제한 사항과 분리하기 위해 애쓸 필요가 없습니다.
  • 조인 조건 지정을 "잊어버리는" 일은 없습니다. 예를 들어 배타 조인을 지정하면 Oracle9iON 절이나 기타 다른 절을 사용하여 조인 조건을 명확하게 지정하도록 요구합니다. 데카르트 곱이 필요하다면 이를 명확하게 표시해야 합니다.
  • 전체 포괄 조인을 수행할 수 있습니다. 이는 기존 Oracle 데이타베이스 조인 구문에서는 불가능했던 것입니다.
  • "(+)" 사용을 빠뜨려 포괄 조인을 배타 조인으로 바꿔버리는 실수를 염려하지 않아도 됩니다.
조인 조건 지정하기

지금까지는 ON 절을 사용하여 조인 조건을 지정했었습니다.
SELECT c.course_name, c.period,
  e.student_name
FROM course c INNER JOIN enrollment e
     ON c.course_name = e.course_name
        AND c.period = e.period;

다양한 ANSI 표준 SQL 자료를 webstore.ansi.org에서 구입할 수 있습니다.
ON 절에서는 조인 조건으로 부울 식을 지정할 수 있습니다. 그러다 대부분의 조인은 동등 조인입니다. 동등 조인에서는 두 테이블의 관련 열을 비교하여 값이 같은지 알아 볼 수 있습니다. 따라서 두 테이블에서 각각 조인을 정의한 열이 같은 이름을 갖고 있다면, 구문이 더 간단해지고 질의가 더 명확하게 됩니다. ON 절을 사용하여 부울 조인 조건을 지정하는 대신, USING 절에서 조인 열을 지정할 수 있습니다. 다음과 같이 작성하는 대신

   ON c.course_name = e.course_name
     AND c.period = e.period;

간단하게 이렇게 작성합니다.

	  USING (course_name, period);

이 예에서 USING 절은 두 테이블의 행이 각 COURSE_NAME과 PERIOD 열에서 동일한 값을 가질 때 조인되도록 지정합니다. Listing 3은 USING 절을 사용하여 동등 조인을 수행하는 경우를 보여 줍니다. 여기에서는 SELECT 문장이 간단하고 이해하기 쉬운 편입니다. 그러나 USING 절을 사용하면 질의의 의미에 미묘한 영향을 미치게 됩니다. ON 절을 사용하여 조인을 작성하면 두 테이블의 모든 열을 사용할 수 있습니다. 따라서 두 테이블로부터 COURSE_NAME 열을 선택할 수 있습니다.

SELECT C.COURSE_NAME, E.COURSE_NAME

Oracle9i SQL Reference 는 조인에 대한 정보 및 사례가 수록되어 있습니다. 이 설명서를 온라인으로 이용하려면 otn.oracle.com/docs/를 방문하십시오.
USING 절이 사용된 문에서 이렇게 열 별칭을 사용하면 "invalid column name" 오류가 발생합니다. USING 절을 지정할 때 데이타베이스 엔진은 두 개의 COURSE_NAME 열을 병합하고, 결과에서는 한 열만 인식하게 됩니다. 이 열은 조인되는 테이블 어느 쪽과도 연관되지 않으므로, 별칭을 사용할 수 없습니다. 동등 조인의 정의에 따르면 질의 결과 각 행에서는 하나의 COURSE_NAME 값만 존재해야 하므로 이는 타당한 규칙입니다.

USING 절은 동등 조인을 간편하고 쉽게 표현할 수 있는 구문이며, ON 절보다는 더 이해하기 쉬운 질의 결과를 얻을 수 있다고 생각합니다. 그리고 언급하기가 다소 꺼려지고 그렇게 권장하고 싶지 않지만 또 다른 지름길이 있습니다. 바로 NATURAL 절입니다. NATURAL 절을 사용하여 NATURAL 조인을 작성할 수 있습니다. 이 조인은 특별한 타입의 동등 조인으로서, 조인 열이 두 테이블에서 같은 이름을 갖는 모든 열로 구성되어 있습니다. Listing 4에 그 예가 있습니다. 그러나 NATURAL 조인은 권장하고 싶지 않으며, 이는 ANSI 측 실수였다고 생각합니다. NATURAL 조인에서는 한 쪽 테이블에 다른 쪽 테이블과 우연히 이름이 일치하는 열을 추가하면 본의 아니게 NATURAL 조인을 변경하는 셈이 됩니다.

ANSI 스타일의 포괄 조인

새로운 ANSI 구문은 세 가지 타입의 포괄 조인을 인정합니다. 왼쪽 포괄 조인(left outer joins), 오른쪽 포괄 조인(right outer joins) 그리고 전체 포괄 조인입니다. 왼쪽 포괄 조인과 오른쪽 포괄 조인은 사실 동일합니다. 한 테이블의 모든 행이 다른 테이블에서 일치하는 행과 함께 포함됩니다. 왼쪽 포괄 조인과 오른쪽 포괄 조인의 유일한 차이점은 테이블이 나열되는 순서입니다. 다음 세 질의에서 첫번째는 기존 구문을 사용했으며 의미는 동일합니다.

SELECT c.course_name, c.period,
  e.student_name
FROM course c, enrollment e
     WHERE c.course_name = e.course_name(+)
         AND c.period = e.period(+);

SELECT c.course_name, c.period,
  e.student_name
FROM course c LEFT OUTER JOIN enrollment e
     ON c.course_name = e.course_name
        AND c.period = e.period;

SELECT c.course_name, c.period,
  e.student_name
FROM enrollment e RIGHT OUTER JOIN course c
     ON c.course_name = e.course_name
        AND c.period = e.period;

전체 포괄 조인은 새로운 기능으로서, 양쪽 테이블에서 모든 행을 반환합니다. 행들은 조인 열에서 서로 일치시키며, 대상 테이블에 일치시킬 값이 없는 행의 빈 열에는 널 값이 사용됩니다. Listing 5는 Oracle9i 이전 방식으로 전체 포괄 조인을 시뮬레이션한 것 과 Oracle9i 전체 포괄 조인을 보여 줍니다.

Listing 5출력 결과에서 Spanish I 과 U.S. History에서 COURSE 행은 ENROLLMENT 테이블에서 일치하는 행이 없습니다. 따라서 STUDENT_NAME 열에서 이 행은 널 값을 갖습니다. Sky Lynn의 German I 코스 행은 COURSE 테이블에서 일치하는 행이 없습니다. 그러나 COURSE_NAME과 PERIOD 열은 널 값이 아님에 유의하십시오. 이 열은 조인 열이며, USING 절에서 지정된 바 있습니다. 따라서 COURSE 테이블에서 해당하는 행이 없는 경우 Oracle9i 는 ENROLLMENT 테이블에서 해당 열의 값을 가져오게 됩니다. 동등 조인에서는 이런 방식이 타당합니다. 열의 값을 널로 두고 싶다면 ON 절을 사용하여 조인 조건을 지정하면 됩니다. Listing 6을 참고할 수 있습니다.

Listing 6에서는 ON 절이 사용되었기 때문에, Sky Lynn의 COURSE_NAME과 PERIOD 열은 널 값을 갖습니다.

다중 조인

다중 조인 조건을 지정하면 질의에서 두 개 이상의 테이블을 조인할 수 있습니다. 기본적으로 Oracle9i 는 왼쪽에서 오른쪽으로 조인을 처리합니다. 그러나 소괄호를 사용하여 그 순서를 제어할 수 있습니다. 다음 두 질의는 동일합니다.

SELECT course_name, period, student_name,
  s.grade_level

FROM course c INNER JOIN enrollment e
     USING (course_name, period)
     INNER JOIN student s USING (student_name);

SELECT course_name, period, student_name, 
  s.grade_level
FROM (course c INNER JOIN enrollment e
     USING (course_name, period))
     INNER JOIN student s USING (student_name);

첫번째 질의는 COURSE에서 ENROLLMENT로 조인을 수행한 다음 그 결과를 STUDENT에 조인합니다. 두 번째는 소괄호를 사용하여 조인 순서를 동일하게 지정한 것입니다.

결론

저는 새로운 조인 구문이 마음에 듭니다. 그리고 여러분도 모든 조인 질의에서 이 방식을 사용해보도록 권장합니다. 이 표준 방식을 사용하여 코드를 더 이동성 있게 그리고 읽기 쉽게 만들 수 있습니다. 또한 전체 포괄 조인이라는 새로운 기능을 활용할 수 있습니다. 궁극적으로는 실수를 범할 확률이 더 적어지게 됩니다.

Jonathan Gennick (jonathan@gennick.com) 은 풍부한 경험을 갖춘 Oracle DBA 이자 Oracle Certified Professional 로서, 새로운 Oracle 기술 탐구를 즐기는 전문가입니다. 그는 현재 저술 활동을 하고 있으며 최근 발표된 SQL*Loader: The Definitive Guide (O'Reilly & Associates, 2001)의 공동 저자이기도 합니다.

 

출처 : http://www.oracle.com/global/kr/magazine/webcolumns/2001/o61sql.html

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,