SQL EXISTS 문은 어떻게 작동합니까?


88

SQL을 배우려고하는데 EXISTS 문을 이해하는 데 어려움을 겪고 있습니다. 나는 "존재한다"에 대한이 인용구를 보았는데 이해가되지 않는다.

exist 연산자를 사용하면 하위 쿼리가 0 개, 1 개 또는 여러 행을 반환 할 수 있으며 조건은 단순히 하위 쿼리가 행을 반환했는지 여부를 확인합니다. 하위 쿼리의 선택 절을 보면 단일 리터럴 (1)로 구성되어 있음을 알 수 있습니다. 포함 쿼리의 조건은 반환 된 행 수만 알면되므로 하위 쿼리가 반환 한 실제 데이터는 관련이 없습니다.

내가 이해하지 못하는 것은 외부 쿼리가 하위 쿼리가 확인하는 행을 어떻게 알 수 있다는 것입니다. 예를 들면 :

SELECT *
  FROM suppliers
 WHERE EXISTS (select *
                 from orders
                where suppliers.supplier_id = orders.supplier_id);

공급자와 주문 테이블의 ID가 일치하면 하위 쿼리가 true를 반환하고 공급자 테이블에서 일치하는 행의 모든 ​​열이 출력된다는 것을 이해합니다. 내가 얻지 못하는 것은 true 또는 false 만 반환되는 경우 하위 쿼리가 어떤 특정 행 (공급 업체 ID가 25 인 행이라고 말함)을 인쇄해야하는지 통신하는 방법입니다.

외부 쿼리와 하위 쿼리 사이에 관계가없는 것 같습니다.

답변:


98

다음과 같이 생각하십시오.

의 '각'행의 Suppliers경우 Order테이블에 조건을 충족 하는 행이 '존재' 하는지 확인합니다 Suppliers.supplier_id(이는 외부 쿼리 현재 '행'에서 가져옴) = Orders.supplier_id. 일치하는 첫 번째 행을 찾으면 바로 거기에서 중지하십시오 WHERE EXISTS.

외부 쿼리와 하위 쿼리 사이의 매직 링크는 Supplier_id평가 된 각 행에 대해 외부 쿼리에서 하위 쿼리로 전달되는 사실에 있습니다.

또는 다른 방법으로 말하자면, 하위 쿼리는 외부 쿼리의 각 테이블 행에 대해 실행됩니다.

하위 쿼리가 전체적으로 실행되고 'true / false'를 얻은 다음이 'true / false'조건을 외부 쿼리와 일치시키려는 것과는 다릅니다.


7
감사! "이것은 전체적으로 실행되는 하위 쿼리와는 다르며 'true / false'를 얻은 다음이 'true / false'조건을 외부 쿼리와 일치 시키려고합니다." 저를 위해 정말로 정리 한 것입니다. 하위 쿼리가 작동하는 방식 (그리고 여러 번 수행)이라고 계속 생각하지만 하위 쿼리가 외부 쿼리에 의존하므로 행당 한 번 실행되어야하기 때문에 귀하가 말한 내용이 의미가 있습니다.
Clarence Liu

32

외부 쿼리와 하위 쿼리 사이에 관계가없는 것 같습니다.

EXISTS 예제 내의 WHERE 절이 무엇을하고 있다고 생각하십니까? SUPPLIERS 참조가 EXISTS 절의 FROM 또는 JOIN 절에없는 경우 어떻게 결론을 내릴 수 있습니까?

EXISTS는 TRUE / FALSE에 대해 평가하고 기준의 첫 번째 일치에서 TRUE로 종료합니다. 이것이 IN. 또한 EXISTS의 SELECT 절이 무시된다는 점에 유의하십시오-IE :

SELECT s.*
  FROM SUPPLIERS s
 WHERE EXISTS (SELECT 1/0
                 FROM ORDERS o
                WHERE o.supplier_id = s.supplier_id)

... 0으로 나누기 오류가 발생해야하지만 그렇지 않습니다. WHERE 절은 EXISTS 절에서 가장 중요한 부분입니다.

또한 JOIN은 EXISTS에 대한 직접적인 대체가 아닙니다. 부모에 연결된 자식 레코드가 둘 이상인 경우 중복 부모 레코드가 있기 때문입니다.


1
아직도 뭔가 빠졌어요. 첫 번째 일치에서 종료되면 어떻게 출력이 o.supplierid = s.supplierid 인 모든 결과가됩니까? 대신 첫 번째 결과를 출력하지 않습니까?
Dan

3
@Dan : EXISTS공급자가 ORDERS 테이블에 적어도 한 번 존재하기 때문에 첫 번째 일치에서 TRUE를 반환 하는 종료합니다. ORDERS에 둘 이상의 자식 관계가 있기 때문에 SUPPLIER 데이터의 중복을 보려면 JOIN을 사용해야합니다. 그러나 대부분은 이러한 중복을 원하지 않으며 GROUP BY / DISTINCT를 실행하면 쿼리에 오버 헤드가 추가 될 수 있습니다. SQL Server EXISTS보다 효율적이며 SELECT DISTINCT ... FROM SUPPLIERS JOIN ORDERS ...최근에 Oracle 또는 MySQL에서 테스트하지 않았습니다.
OMG Ponies

질문이 있는데, 외부 쿼리에서 SELECTED 인 모든 레코드에 대해 일치하는 것입니다. 에서와 같이 공급 업체에서 선택한 5 개의 행이있는 경우 주문에서 5 번 가져옵니다.
Rahul Kadukar

24

당신이 중 하나를 사용하여 동일한 결과를 생성 할 수 있습니다 JOIN, EXISTS, IN, 또는 INTERSECT:

SELECT s.supplier_id
FROM suppliers s
INNER JOIN (SELECT DISTINCT o.supplier_id FROM orders o) o
    ON o.supplier_id = s.supplier_id

SELECT s.supplier_id
FROM suppliers s
WHERE EXISTS (SELECT * FROM orders o WHERE o.supplier_id = s.supplier_id)

SELECT s.supplier_id 
FROM suppliers s 
WHERE s.supplier_id IN (SELECT o.supplier_id FROM orders o)

SELECT s.supplier_id
FROM suppliers s
INTERSECT
SELECT o.supplier_id
FROM orders o

1
좋은 대답은하지만, 또한 사용이 상관 관계를 피하기 위해 존재하지에 더 나은 것을 마음
플로리안 Fröhlich

1
공급 업체에 1 천만 개의 행이 있고 주문에 1 억 개의 행이있는 경우 어떤 쿼리가 더 빨리 실행될 것이라고 생각합니까? 그 이유는 무엇입니까?
Teja

7

다음과 같은 where 절이있는 경우 :

WHERE id in (25,26,27) -- and so on

일부 행은 반환되고 일부는 반환되지 않는 이유를 쉽게 이해할 수 있습니다.

where 절이 다음과 같을 때 :

WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);

그것은 단지 의미합니다 : 동일한 ID를 가진 orders 테이블에 기존 레코드가있는 행을 반환합니다.


2

이것은 매우 좋은 질문 이므로이 주제에 대한 자세한 기사 를 블로그에 작성하기로 결정했습니다 .

데이터베이스 테이블 모델

데이터베이스에 일대 다 테이블 관계를 형성하는 다음 두 테이블이 있다고 가정 해 보겠습니다.

SQL EXISTS 테이블

student테이블은 부모이며,student_grade 이 학생 테이블의 ID를 기본 키 열을 참조하는 student_id 외래 키 열이 있기 때문에 자식 테이블입니다.

student table다음과 같은 두 개의 레코드를 포함 :

| id | first_name | last_name | admission_score |
|----|------------|-----------|-----------------|
| 1  | Alice      | Smith     | 8.95            |
| 2  | Bob        | Johnson   | 8.75            |

그리고 student_grade테이블에는 학생이받은 성적이 저장됩니다.

| id | class_name | grade | student_id |
|----|------------|-------|------------|
| 1  | Math       | 10    | 1          |
| 2  | Math       | 9.5   | 1          |
| 3  | Math       | 9.75  | 1          |
| 4  | Science    | 9.5   | 1          |
| 5  | Science    | 9     | 1          |
| 6  | Science    | 9.25  | 1          |
| 7  | Math       | 8.5   | 2          |
| 8  | Math       | 9.5   | 2          |
| 9  | Math       | 9     | 2          |
| 10 | Science    | 10    | 2          |
| 11 | Science    | 9.4   | 2          |

SQL 존재

수학 수업에서 10 점을받은 모든 학생을 모집한다고 가정 해 보겠습니다.

학생 식별자에만 관심이 있다면 다음과 같은 쿼리를 실행할 수 있습니다.

SELECT
    student_grade.student_id
FROM
    student_grade
WHERE
    student_grade.grade = 10 AND
    student_grade.class_name = 'Math'
ORDER BY
    student_grade.student_id

그러나 응용 프로그램은 student식별자뿐만 아니라의 전체 이름을 표시하는 데 관심이 있으므로student 테이블의 합니다.

student수학에서 점수가 10 인 레코드 를 필터링하기 위해 다음과 같이 EXISTS SQL 연산자를 사용할 수 있습니다.

SELECT
    id, first_name, last_name
FROM
    student
WHERE EXISTS (
    SELECT 1
    FROM
        student_grade
    WHERE
        student_grade.student_id = student.id AND
        student_grade.grade = 10 AND
        student_grade.class_name = 'Math'
)
ORDER BY id

위의 쿼리를 실행하면 Alice 행만 선택되었음을 알 수 있습니다.

| id | first_name | last_name |
|----|------------|-----------|
| 1  | Alice      | Smith     |

외부 쿼리는 student클라이언트에 반환하려는 행 열을 선택합니다 . 그러나 WHERE 절은 연결된 내부 하위 쿼리와 함께 EXISTS 연산자를 사용합니다.

EXISTS 연산자는 하위 쿼리가 하나 이상의 레코드를 반환하면 true를 반환하고 행을 선택하지 않으면 false를 반환합니다. 데이터베이스 엔진은 하위 쿼리를 완전히 실행할 필요가 없습니다. 단일 레코드가 일치하면 EXISTS 연산자가 true를 반환하고 연결된 다른 쿼리 행이 선택됩니다.

내부 하위 쿼리는 student_grade테이블 의 student_id 열 이 외부 학생 테이블의 id 열과 일치 하기 때문에 상관 관계 가 있습니다.


대단한 대답입니다. 잘못된 예를 사용했기 때문에 개념을 얻지 못한 것 같습니다. EXIST상관 된 하위 쿼리에서만 작동 합니까 ? 나는 같은 테이블을 하나만 포함하는 쿼리를 가지고 놀았습니다 SELECT id FROM student WHERE EXISTS (SELECT 1 FROM student WHERE student.id > 1). 나는 내가 쓴 것을 하나의 간단한 WHERE 쿼리로 얻을 수 있다는 것을 알고 있지만 EXISTS를 이해하기 위해 그것을 사용했습니다. 나는 모든 행을 얻었다. 실제로 상관 하위 쿼리를 사용하지 않았기 때문입니까? 감사.
Bowen Liu

외부 쿼리의 레코드를 필터링하려는 경우 상관 된 하위 쿼리에만 의미가 있습니다. 귀하의 경우 내부 쿼리는 WHERE TRUE
Vlad Mihalcea

감사합니다 Vlad. 그것이 내가 생각했던 거죠. 내가 엉망이되었을 때 일어난 이상한 생각 일 뿐이다. 솔직히 상관 하위 쿼리의 개념을 몰랐습니다. 이제 내부 쿼리를 사용하여 외부 쿼리의 행을 필터링하는 것이 훨씬 더 합리적입니다.
Bowen Liu

0

EXISTS는 하위 쿼리가 적어도 하나의 행을 반환한다는 것을 의미합니다. 이 경우 외부 테이블의 supplier_id와 내부 테이블의 supplier_id를 확인하므로 상관 된 하위 쿼리입니다. 이 쿼리는 실제로 다음과 같이 말합니다.

모든 공급 업체 선택 각 공급 업체 ID에 대해이 공급 업체에 대한 주문이 있는지 확인 공급 업체가 주문 테이블에없는 경우 결과에서 공급 업체를 제거합니다. 주문 테이블에 해당 행이있는 모든 공급 업체 반환

이 경우 INNER JOIN을 사용하여 동일한 작업을 수행 할 수 있습니다.

SELECT suppliers.* 
  FROM suppliers 
 INNER 
  JOIN orders 
    ON suppliers.supplier_id = orders.supplier_id;

조랑말 댓글이 맞습니다. 해당 조인으로 그룹화하거나 필요한 데이터에 따라 구별을 선택해야합니다.


4
둘 이상의 하위 레코드가 상위에 연결되어있는 경우 내부 조인은 EXISTS와 다른 결과를 생성합니다.
OMG Ponies

나는 EXISTS가있는 하위 쿼리가 true 또는 false를 반환한다는 것을 읽었을 때 혼란 스러울 수 있습니다. 하지만 이것이 반환되는 유일한 것은 아닙니다. 하위 쿼리가 "orders 테이블에 해당 행이있는 공급 업체"를 모두 반환합니까? 그러나 그렇다면 EXISTS 문은 어떻게 부울 결과를 반환합니까? 내가 교과서에서 읽는 모든 것은 부울 결과 만 반환한다는 것이므로 코드의 결과를 반환하는 것과 일치시키는 데 어려움을 겪고 있습니다.
Dan

함수처럼 EXISTS 읽기 ... EXISTS (resultset). EXISTS 함수는 결과 집합에 행이 있으면 true를 반환하고 비어 있으면 false를 반환합니다. 그게 기본입니다.
David Fells

3
@Dan, EXISTS ()가 모든 소스 행에 대해 논리적으로 독립적 으로 평가된다는 것을 고려하십시오 . 전체 쿼리에 대한 단일 값이 아닙니다.
Arvo

-1

설명하는 것은 상관 하위 쿼리가있는 소위 쿼리입니다 .

(일반적으로) 대신 조인을 사용하여 쿼리를 작성하여 피해야합니다.

SELECT suppliers.* 
FROM suppliers 
JOIN orders USING supplier_id
GROUP BY suppliers.supplier_id

그렇지 않으면 외부 쿼리의 각 행에 대해 하위 쿼리가 실행됩니다.


2
이 두 솔루션은 동일하지 않습니다. orders조인 조건과 일치하는 행이 둘 이상 있으면 JOIN은 EXISTS 하위 쿼리와 다른 결과를 제공합니다 .
a_horse_with_no_name

1
대체 솔루션에 감사드립니다. 하지만 상관 하위 쿼리와 조인 사이에 옵션이 주어지면 더 효율적이기 때문에 조인을 사용해야한다고 제안합니까?
sunny_dev 2014-04-23
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.