기존 (SELECT 1…) 대 기존 (SELECT *…) 하나 또는 다른 것?


37

테이블에 일부 행이 있는지 확인해야 할 때마다 항상 다음과 같은 조건을 작성하는 경향이 있습니다.

SELECT a, b, c
  FROM a_table
 WHERE EXISTS
       (SELECT *  -- This is what I normally write
          FROM another_table
         WHERE another_table.b = a_table.b
       )

다른 사람들은 다음과 같이 작성합니다.

SELECT a, b, c
  FROM a_table
 WHERE EXISTS
       (SELECT 1   --- This nice '1' is what I have seen other people use
          FROM another_table
         WHERE another_table.b = a_table.b
       )

조건이 NOT EXISTS아닌 EXISTS경우 : 경우에 LEFT JOIN따라 추가 조건 (때로는 antijoin 이라고 함 )으로 작성할 수 있습니다 .

SELECT a, b, c
  FROM a_table
       LEFT JOIN another_table ON another_table.b = a_table.b
 WHERE another_table.primary_key IS NULL

나는 의미가 덜 명확하다고 생각하기 때문에, 특히 명확 primary_key하지 않은 경우 또는 기본 키 또는 조인 조건이 여러 열일 때 (열 중 하나를 쉽게 잊을 수 있음) 생각하지 않기 때문에 피하려고 합니다. 그러나 때로는 다른 사람이 작성한 코드를 유지 관리하고 있습니다.

  1. SELECT 1대신에 스타일 이외의 차이점 이 SELECT *있습니까?
    같은 방식으로 작동하지 않는 코너 케이스가 있습니까?

  2. 내가 쓴 것은 (AFAIK) 표준 SQL이지만, 데이터베이스 / 이전 버전마다 다른 점이 있습니까?

  3. 반 결합을 명시 적으로 작성하면 어떤 이점이 있습니까?
    현대의 플래너 / 최적화 담당자는이를 NOT EXISTS조항 과 다르게 취급 합니까?


5
PostgreSQL은 열이없는 선택을 지원하므로 간단히 쓸 수 있습니다 EXISTS (SELECT FROM ...).
rightfold

1
나는 몇 년 전에 SO에 거의 같은 질문을되었습니다 stackoverflow.com/questions/7710153/...
어윈 Brandstetter

답변:


45

아니, 간의 효율 차이가없는 (NOT) EXISTS (SELECT 1 ...)(NOT) EXISTS (SELECT * ...)모든 주요 DBMS에가. 나는 종종 (NOT) EXISTS (SELECT NULL ...)사용되는 것을 보았다 .

어떤 경우에는 글을 쓸 수 (NOT) EXISTS (SELECT 1/0 ...)있으며 결과는 0으로 나눈 오류없이 동일하며 표현식이 평가되지 않음을 나타냅니다.


[정보 LEFT JOIN / IS NULLantijoin있어서, 보정 :이 동일하다 NOT EXISTS (SELECT ...).

이 경우에 NOT EXISTSvsLEFT JOIN / IS NULL다른 실행 계획이있을 수 있습니다. 예를 들어 MySQL과 대부분 이전 버전 (5.7 이전)에서는 계획이 상당히 유사하지만 동일하지는 않습니다. 내가 아는 한 다른 DBMS (SQL Server, Oracle, Postgres, DB2)의 옵티마이 저는이 두 가지 방법을 재 작성할 수 있고 두 계획에 대해 동일한 계획을 고려할 수 있습니다. 그럼에도 불구하고 그러한 보장은 없으며 최적화를 수행 할 때 각 옵티마이 저가 다시 쓰지 않는 경우 (예 : 많은 조인 및 / 또는 파생 테이블이있는 복잡한 쿼리 / 하위 쿼리 내의 하위 쿼리 (여러 테이블의 조건, 조인 조건에 사용 된 복합 열) 또는 옵티 마이저 선택 및 계획은 사용 가능한 인덱스, 설정 등에 따라 다르게 영향을받습니다.

또한 USING모든 DBMS (예 : SQL Server)에서 사용할 수는 없습니다. JOIN ... ON모든 곳에서 더 일반적으로 작동합니다.
그리고 SELECT조인 할 때 오류 / 모호성을 피하기 위해에 열 이름 앞에 테이블 이름 / 별칭을 붙여야 합니다.
또한 일반적으로 조인 된 열을 IS NULL확인 에 넣는 것을 선호합니다 (PK 또는 null이 아닌 열은 괜찮을지라도 계획 LEFT JOIN이 클러스터되지 않은 인덱스 를 사용할 때 효율성에 유용 할 수 있음 ).

SELECT a_table.a, a_table.b, a_table.c
  FROM a_table
       LEFT JOIN another_table 
           ON another_table.b = a_table.b
 WHERE another_table.b IS NULL ;

antijoin을 사용하는 세 번째 방법도 NOT IN있지만 내부 테이블의 열이 Null을 허용하는 경우 의미가 다릅니다 (결과!). 그러나로 행을 제외 NULL하여 쿼리를 이전 2 버전과 동일하게하여 사용할 수 있습니다 .

SELECT a, b, c
  FROM a_table
 WHERE a_table.b NOT IN 
       (SELECT another_table.b
          FROM another_table
         WHERE another_table.b IS NOT NULL
       ) ;

이것은 또한 대부분의 DBMS에서 유사한 계획을 산출합니다.


1
최신 버전의 MySQL까지는 [NOT] IN (SELECT ...)동일하지만 성능이 매우 떨어졌습니다. 피하십시오!
Rick James

3
이것은 PostgreSQL에는 해당되지 않습니다 . SELECT *확실히 더 많은 일을하고 있습니다. 나는 간단하게하기 위해 다음을 사용하여 조언 할 것이다SELECT 1
Evan Carroll

11

경우 하나 개의 범주가 SELECT 1SELECT *더 구체적으로, 하나는 항상 다른있는 동안 대부분하지 않습니다 이러한 경우에 허용됩니다 - 호환되지 않습니다는.

그룹화 된 집합 의 행이 있는지 확인 해야하는 경우에 대해 이야기하고 있습니다 . 테이블 T에 열이 C1있고 C2특정 조건과 일치하는 행 그룹이 있는지 확인하는 경우 다음 SELECT 1과 같이 사용할 수 있습니다 .

EXISTS
(
  SELECT
    1
  FROM
    T
  GROUP BY
    C1
  HAVING
    AGG(C2) = SomeValue
)

그러나 SELECT *같은 방식으로 사용할 수 없습니다 .

그것은 단지 구문 적 측면 일뿐입니다. 두 옵션이 구문 상 허용되는 경우 다른 답변 에서 설명한 것처럼 성능 측면에서 또는 결과 결과에 차이가 없을 입니다.

주석 다음에 추가 된 메모

실제로이 차이를 지원하는 데이터베이스 제품은 많지 않습니다. SQL Server, Oracle, MySQL 및 SQLite와 같은 제품 SELECT *은 위의 쿼리에서 오류없이 기꺼이 받아들 입니다. 이는 아마도 EXISTS SELECT를 특별한 방식으로 취급한다는 의미 일 것입니다 .

PostgreSQL은 SELECT *실패 할 수 있는 RDBMS 중 하나 이지만 일부 경우에는 여전히 작동 할 수 있습니다. 특히 PK별로 그룹화하는 경우 SELECT *제대로 작동합니다. 그렇지 않으면 메시지와 함께 실패합니다.

오류 : "T.C2"열이 GROUP BY 절에 나타나거나 집계 함수에 사용되어야합니다.


1
좋은 점이지만 이것이 내가 걱정하는 정확한 것은 아닙니다. 이것은 개념적 차이를 보여줍니다 . 왜냐하면, 당신 GROUP BY의 개념 *은 의미가 없기 때문입니다 (또는 적어도 분명하지 않습니다).
joanolo

5

EXISTS적어도 SQL Server에서 더 깔끔하고 잘못된 쿼리를 유발 하는 절을 다시 작성하는 논쟁의 여지가있는 흥미로운 방법은 다음 과 같습니다.

SELECT a, b, c
  FROM a_table
 WHERE b = ANY
       (
          SELECT b
          FROM another_table
       );

그 반-세미-조인 버전은 다음과 같습니다.

SELECT a, b, c
  FROM a_table
 WHERE b <> ALL
       (
          SELECT b
          FROM another_table
       );

둘 다 일반적으로 같은 계획에 최적화되어 WHERE EXISTSWHERE NOT EXISTS,하지만 의도는 명백하고, 당신은 어떤 "이상한"가 없다 1거나 *.

흥미롭게도, 관련된 null 점검 문제 NOT IN (...)는 문제가되는 <> ALL (...)반면, NOT EXISTS (...)그 문제는 겪지 않습니다. 널 입력 가능 컬럼이있는 다음 두 테이블을 고려하십시오.

IF OBJECT_ID('tempdb..#t') IS NOT NULL
BEGIN
    DROP TABLE #t;
END;
CREATE TABLE #t 
(
    ID INT NOT NULL IDENTITY(1,1)
    , SomeValue INT NULL
);

IF OBJECT_ID('tempdb..#s') IS NOT NULL
BEGIN
    DROP TABLE #s;
END;
CREATE TABLE #s 
(
    ID INT NOT NULL IDENTITY(1,1)
    , SomeValue INT NULL
);

일치하는 행과 일치하지 않는 행을 사용하여 둘 다에 데이터를 추가합니다.

INSERT INTO #t (SomeValue) VALUES (1);
INSERT INTO #t (SomeValue) VALUES (2);
INSERT INTO #t (SomeValue) VALUES (3);
INSERT INTO #t (SomeValue) VALUES (NULL);

SELECT *
FROM #t;
+ -------- + ----------- +
| 아이디 | SomeValue |
+ -------- + ----------- +
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | NULL |
+ -------- + ----------- +
INSERT INTO #s (SomeValue) VALUES (1);
INSERT INTO #s (SomeValue) VALUES (2);
INSERT INTO #s (SomeValue) VALUES (NULL);
INSERT INTO #s (SomeValue) VALUES (4);

SELECT *
FROM #s;
+ -------- + ----------- +
| 아이디 | SomeValue |
+ -------- + ----------- +
| 1 | 1 |
| 2 | 2 |
| 3 | NULL |
| 4 | 4 |
+ -------- + ----------- +

NOT IN (...)쿼리 :

SELECT *
FROM #t 
WHERE #t.SomeValue NOT IN (
    SELECT #s.SomeValue
    FROM #s 
    );

다음과 같은 계획이 있습니다.

여기에 이미지 설명을 입력하십시오

NULL 값으로 인해 동등성을 확인할 수 없으므로 쿼리는 행을 반환하지 않습니다.

이 쿼리는와 <> ALL (...)같은 계획 을 보여주고 행을 반환하지 않습니다.

SELECT *
FROM #t 
WHERE #t.SomeValue <> ALL (
    SELECT #s.SomeValue
    FROM #s 
    );

여기에 이미지 설명을 입력하십시오

을 사용하는 변형 NOT EXISTS (...)은 약간 다른 평면 모양을 나타내며 행을 반환합니다.

SELECT *
FROM #t 
WHERE NOT EXISTS (
    SELECT 1
    FROM #s 
    WHERE #s.SomeValue = #t.SomeValue
    );

계획:

여기에 이미지 설명을 입력하십시오

해당 쿼리의 결과 :

+ -------- + ----------- +
| 아이디 | SomeValue |
+ -------- + ----------- +
| 3 | 3 |
| 4 | NULL |
+ -------- + ----------- +

이처럼 <> ALL (...)문제가 발생하기 쉬운 결과를 얻을 수 있습니다 NOT IN (...).


3
나는 *이상 하지 않다고 말해야 한다 : 나는 EXISTS (SELECT * FROM t WHERE ...) AS를 읽는다 there is a _row_ in table _t_ that.... 어쨌든, 나는 대안이 있고, 당신의 것이 명확하게 읽을 수 있습니다. 한가지 의심 / 캐비티 : b널 입력이 가능한 경우 어떻게 작동 합니까? [난으로 인한 x IN (SELECT something_nullable FROM a_table)
실수

EXISTS는 테이블에 행이 있는지 여부를 알려주고 true 또는 false를 반환합니다. EXISTS (SELECT x FROM (값 (null))는 true입니다. IN = ANY & NOT IN <> ALL입니다.이 4 개는 일치 할 수 있도록 NULL이있는 RHS 행을 가져옵니다. (x) = ANY (값 (null)) & (x) <> ALL (값 (null))은 알 수 없거나 널 (null)이지만 EXISTS (값 (null))는 참입니다 (IN & = ANY는 동일한 "NOT IN (...) ] <> ALL (...) ". ANY & ALL 반복 OR & AND. 그러나 의미를 의도 한대로 구성하지 않으면"문제 "만 있습니다.) 이러한 항목을 기존에 사용하지 않는 것이 좋습니다. "오해의 소지가 적은"
philipxy

@philliprxy-내가 틀렸다면 인정하는 데 아무런 문제가 없습니다. 마음에 든다면 자신의 답변을 자유롭게 추가하십시오.
맥스 버논

4

(MySQL에서) 동일하다는 "증거"는

EXPLAIN EXTENDED
    SELECT EXISTS ( SELECT * ... ) AS x;
SHOW WARNINGS;

그런 다음로 반복하십시오 SELECT 1. 두 경우 모두 '확장'출력은로 변환되었음을 나타냅니다 SELECT 1.

마찬가지로 COUNT(*)로 바뀝니다 COUNT(0).

참고할 사항 : 최근 버전에서는 최적화가 개선되었습니다. EXISTS대-조인을 비교할 가치가 있습니다 . 버전이 다른 버전보다 더 나은 작업을 수행 할 수 있습니다.


4

일부 데이터베이스에서는이 최적화가 아직 작동하지 않습니다. 예를 들어 PostgreSQL 의 경우 버전 9.6부터는 실패합니다.

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT *
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  HAVING count(*) > 1
);

그리고 이것은 성공할 것입니다.

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT 1  -- This changed from the first query
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  HAVING count(*) > 1
);

다음에 실패하기 때문에 실패하지만 여전히 차이가 있음을 의미합니다.

SELECT *
FROM ( VALUES (1),(1) ) AS t(x)
HAVING count(*) > 1;

질문에 대한 답변 에서이 특정 문제와 사양 위반에 대한 자세한 내용을 찾을 수 있습니다 .SQL 사양에 EXISTS ()의 GROUP BY가 필요합니까?


드문 코너 사례, 조금 이상 하지만 어쩌면 다시 데이터베이스 데이터베이스를 설계 할 때 많은 타협을해야한다는 것을 증명합니다.
joanolo

-1

나는 항상 select top 1 'x'(SQL Server)를 사용했다

이론적으로, 한정 행의 존재에 대한 상수를 선택한 후 전자가 완료되고 후자는 모든 것을 선택하므로 전자가 select top 1 'x'더 효율적입니다 select *.

그러나 초기에는 관련성이 있었지만 최적화는 아마도 모든 주요 RDBS에서 그 차이를 무의미하게 만들었습니다.


맞는 말이다. 이것이 top n없는 order by좋은 아이디어가 될 수있는 몇 안되는 사례 중 하나 일 수도 있습니다 .
joanolo

3
"이론적으로 ...."아니오, 이론적 으로는 표현 select top 1 'x'보다 더 효율적이지 않아야합니다 . 실제로 옵티마이 저가 차선책으로 작동하지만 이론적으로 두 표현식이 동일하면 더 효율적일 수 있습니다. select *Exist
miracle173

-4

IF EXISTS(SELECT TOP(1) 1 FROM현재 플랫폼 / 버전이 얼마나 좋은지 나쁜지를 걱정할 필요가 없기 때문에 장기적으로 그리고 플랫폼 전반에 걸쳐 더 나은 습관입니다. 그리고 SQL은 TOP n매개 변수화 가능한쪽으로 이동 하고 있습니다 TOP(n). 한 번만 배우는 기술이어야합니다.


3
당신은 무엇을 의미합니까 "플랫폼" ? TOP유효한 SQL조차도 아닙니다.
ypercubeᵀᴹ

"SQL이 움직이고있다."는 명백한 잘못이다. 더 없다 TOP (n)표준 쿼리 언어 - "SQL"에. T-SQL에는 Microsoft SQL Server가 사용하는 방언이 있습니다.
a_horse_with_no_name

원래 질문의 태그는 "SQL Server"입니다. 그러나 내가 말한 것을 다운 투표하고 논쟁하는 것은 괜찮습니다.이 사이트는 쉬운 다운 투표를 가능하게하는 목적입니다. 세부 사항에 지루한 관심을 가지고 누가 당신의 퍼레이드에 비를 줍니까?
ajeh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.