Programing

Exists 1 또는 Exists *를 사용하는 하위 쿼리

lottogame 2020. 9. 17. 18:49
반응형

Exists 1 또는 Exists *를 사용하는 하위 쿼리


다음과 같이 내 EXISTS 수표를 작성했습니다.

IF EXISTS (SELECT * FROM TABLE WHERE Columns=@Filters)
BEGIN
   UPDATE TABLE SET ColumnsX=ValuesX WHERE Where Columns=@Filters
END

DBA에의 전생에서의 하나는 내가 할 때 저에게 EXISTS절을 사용하는 SELECT 1대신SELECT *

IF EXISTS (SELECT 1 FROM TABLE WHERE Columns=@Filters)
BEGIN
   UPDATE TABLE SET ColumnsX=ValuesX WHERE Columns=@Filters
END

이것이 정말로 차이를 만들까요?


아니요, SQL Server는 똑똑하고 EXISTS에 사용되고 있음을 알고 시스템에 데이터를 반환하지 않습니다.

Quoth Microsoft : http://technet.microsoft.com/en-us/library/ms189259.aspx?ppud=4

EXISTS에 의해 도입 된 하위 쿼리의 선택 목록은 거의 항상 별표 (*)로 구성됩니다. 하위 쿼리에 지정된 조건을 충족하는 행이 있는지 여부를 테스트하기 때문에 열 이름을 나열 할 이유가 없습니다.

자신을 확인하려면 다음을 실행하십시오.

SELECT whatever
  FROM yourtable
 WHERE EXISTS( SELECT 1/0
                 FROM someothertable 
                WHERE a_valid_clause )

실제로 SELECT 목록으로 무언가를 수행하는 경우 div 0 오류가 발생합니다. 그렇지 않습니다.

편집 : 참고, SQL 표준은 실제로 이것에 대해 이야기합니다.

ANSI SQL 1992 표준, 191 페이지 http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt

3) 케이스 :
인 경우] a) <select list>"*"단순히 포함되는 <subquery>즉시에 포함되어 그 <exists predicate>후,이 <select list>(A)에 상당 <value expression>임의하다 <literal>.


이 오해의 이유는 아마도 모든 칼럼을 읽게 될 것이라는 믿음 때문일 ​​것입니다. 이것이 사실이 아님을 쉽게 알 수 있습니다.

CREATE TABLE T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)

CREATE NONCLUSTERED INDEX NarrowIndex ON T(Y)

IF EXISTS (SELECT * FROM T)
    PRINT 'Y'

계획 제공

계획

이는 인덱스에 모든 열이 포함되지 않았음에도 불구하고 SQL Server에서 사용 가능한 가장 좁은 인덱스를 사용하여 결과를 확인할 수 있음을 보여줍니다. 인덱스 액세스는 세미 조인 연산자 아래에 있습니다. 즉, 첫 번째 행이 반환되는 즉시 검색을 중지 할 수 있습니다.

따라서 위의 믿음이 잘못된 것이 분명합니다.

그러나 쿼리 최적화 팀의 코너 커닝햄 설명 여기에 그가 일반적으로 사용하는 SELECT 1이 작은 성능 차이를 만들 수 있습니다이 경우에 컴파일에서 쿼리를.

QP는 *파이프 라인의 초기에 모든를 가져와 확장 하여 객체 (이 경우 열 목록)에 바인딩합니다. 그런 다음 쿼리의 특성으로 인해 불필요한 열을 제거합니다.

따라서 EXISTS다음과 같은 간단한 하위 쿼리의 경우 :

SELECT col1 FROM MyTable WHERE EXISTS (SELECT * FROM Table2 WHERE MyTable.col1=Table2.col2)*잠재적으로 큰 열 목록으로 확장 된 다음의 의미론 EXISTS에 해당 열이 필요하지 않다고 판단 되므로 기본적으로 모두 제거 할 수 있습니다.

" SELECT 1"을 사용하면 쿼리 컴파일 중에 해당 테이블에 대해 불필요한 메타 데이터를 검사 할 필요가 없습니다.

그러나 런타임시 쿼리의 두 가지 형식은 동일하며 동일한 런타임을 갖습니다.

다양한 수의 열이있는 빈 테이블에서이 쿼리를 표현하는 네 가지 가능한 방법을 테스트했습니다. SELECT 1SELECT *SELECT Primary_KeySELECT Other_Not_Null_Column.

OPTION (RECOMPILE)초당 평균 실행 수를 사용하여 루프에서 쿼리를 실행 하고 측정했습니다. 아래 결과

여기에 이미지 설명 입력

+-------------+----------+---------+---------+--------------+
| Num of Cols |    *     |    1    |   PK    | Not Null col |
+-------------+----------+---------+---------+--------------+
| 2           | 2043.5   | 2043.25 | 2073.5  | 2067.5       |
| 4           | 2038.75  | 2041.25 | 2067.5  | 2067.5       |
| 8           | 2015.75  | 2017    | 2059.75 | 2059         |
| 16          | 2005.75  | 2005.25 | 2025.25 | 2035.75      |
| 32          | 1963.25  | 1967.25 | 2001.25 | 1992.75      |
| 64          | 1903     | 1904    | 1936.25 | 1939.75      |
| 128         | 1778.75  | 1779.75 | 1799    | 1806.75      |
| 256         | 1530.75  | 1526.5  | 1542.75 | 1541.25      |
| 512         | 1195     | 1189.75 | 1203.75 | 1198.5       |
| 1024        | 694.75   | 697     | 699     | 699.25       |
+-------------+----------+---------+---------+--------------+
| Total       | 17169.25 | 17171   | 17408   | 17408        |
+-------------+----------+---------+---------+--------------+

로가 일관된 사이의 승자입니다 볼 수 있습니다 SELECT 1및이 SELECT *와 둘 사이의 차이는 무시할 접근한다. SELECT Not Null col과는 SELECT PK약간 빠른하지만 표시 않습니다.

테이블의 열 수가 증가함에 따라 네 개의 쿼리 모두 성능이 저하됩니다.

As the table is empty this relationship does seem only explicable by the amount of column metadata. For COUNT(1) it is easy to see that this gets rewritten to COUNT(*) at some point in the process from the below.

SET SHOWPLAN_TEXT ON;

GO

SELECT COUNT(1)
FROM master..spt_values

Which gives the following plan

  |--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1004],0)))
       |--Stream Aggregate(DEFINE:([Expr1004]=Count(*)))
            |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))

Attaching a debugger to the SQL Server process and randomly breaking whilst executing the below

DECLARE @V int 

WHILE (1=1)
    SELECT @V=1 WHERE EXISTS (SELECT 1 FROM ##T) OPTION(RECOMPILE)

I found that in the cases where the table has 1,024 columns most of the time the call stack looks like something like the below indicating that it is indeed spending a large proportion of the time loading column metadata even when SELECT 1 is used (For the case where the table has 1 column randomly breaking didn't hit this bit of the call stack in 10 attempts)

sqlservr.exe!CMEDAccess::GetProxyBaseIntnl()  - 0x1e2c79 bytes  
sqlservr.exe!CMEDProxyRelation::GetColumn()  + 0x57 bytes   
sqlservr.exe!CAlgTableMetadata::LoadColumns()  + 0x256 bytes    
sqlservr.exe!CAlgTableMetadata::Bind()  + 0x15c bytes   
sqlservr.exe!CRelOp_Get::BindTree()  + 0x98 bytes   
sqlservr.exe!COptExpr::BindTree()  + 0x58 bytes 
sqlservr.exe!CRelOp_FromList::BindTree()  + 0x5c bytes  
sqlservr.exe!COptExpr::BindTree()  + 0x58 bytes 
sqlservr.exe!CRelOp_QuerySpec::BindTree()  + 0xbe bytes 
sqlservr.exe!COptExpr::BindTree()  + 0x58 bytes 
sqlservr.exe!CScaOp_Exists::BindScalarTree()  + 0x72 bytes  
... Lines omitted ...
msvcr80.dll!_threadstartex(void * ptd=0x0031d888)  Line 326 + 0x5 bytes C
kernel32.dll!_BaseThreadStart@8()  + 0x37 bytes 

This manual profiling attempt is backed up by the VS 2012 code profiler which shows a very different selection of functions consuming the compilation time for the two cases (Top 15 Functions 1024 columns vs Top 15 Functions 1 column).

Both the SELECT 1 and SELECT * versions wind up checking column permissions and fail if the user is not granted access to all columns in the table.

An example I cribbed from a conversation on the heap

CREATE USER blat WITHOUT LOGIN;
GO
CREATE TABLE dbo.T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)
GO

GRANT SELECT ON dbo.T TO blat;
DENY SELECT ON dbo.T(Z) TO blat;
GO
EXECUTE AS USER = 'blat';
GO

SELECT 1
WHERE  EXISTS (SELECT 1
               FROM   T); 
/*  ↑↑↑↑ 
Fails unexpectedly with 

The SELECT permission was denied on the column 'Z' of the 
           object 'T', database 'tempdb', schema 'dbo'.*/

GO
REVERT;
DROP USER blat
DROP TABLE T

So one might speculate that the minor apparent difference when using SELECT some_not_null_col is that it only winds up checking permissions on that specific column (though still loads the metadata for all). However this doesn't seem to fit with the facts as the percentage difference between the two approaches if anything gets smaller as the number of columns in the underlying table increases.

In any event I won't be rushing out and changing all my queries to this form as the difference is very minor and only apparent during query compilation. Removing the OPTION (RECOMPILE) so that subsequent executions can use a cached plan gave the following.

여기에 이미지 설명 입력

+-------------+-----------+------------+-----------+--------------+
| Num of Cols |     *     |     1      |    PK     | Not Null col |
+-------------+-----------+------------+-----------+--------------+
| 2           | 144933.25 | 145292     | 146029.25 | 143973.5     |
| 4           | 146084    | 146633.5   | 146018.75 | 146581.25    |
| 8           | 143145.25 | 144393.25  | 145723.5  | 144790.25    |
| 16          | 145191.75 | 145174     | 144755.5  | 146666.75    |
| 32          | 144624    | 145483.75  | 143531    | 145366.25    |
| 64          | 145459.25 | 146175.75  | 147174.25 | 146622.5     |
| 128         | 145625.75 | 143823.25  | 144132    | 144739.25    |
| 256         | 145380.75 | 147224     | 146203.25 | 147078.75    |
| 512         | 146045    | 145609.25  | 145149.25 | 144335.5     |
| 1024        | 148280    | 148076     | 145593.25 | 146534.75    |
+-------------+-----------+------------+-----------+--------------+
| Total       | 1454769   | 1457884.75 | 1454310   | 1456688.75   |
+-------------+-----------+------------+-----------+--------------+

The test script I used can be found here


Best way to know is to performance test both versions and check out the execution plan for both versions. Pick a table with lots of columns.


SQL Server에는 차이가 없으며 SQL Server에서는 문제가 없었습니다. 옵티마이 저는 이들이 동일하다는 것을 알고 있습니다. 실행 계획을 보면 이들이 동일하다는 것을 알 수 있습니다.


개인적으로 동일한 쿼리 계획에 최적화되지 않는다는 사실을 믿기가 매우 어렵습니다. 그러나 특정 상황을 알 수있는 유일한 방법은 테스트하는 것입니다. 그렇다면 다시보고하십시오!


실제 차이는 아니지만 성능에 매우 작은 타격이있을 수 있습니다. 경험상 필요한 것보다 더 많은 데이터를 요청해서는 안됩니다.

참고 URL : https://stackoverflow.com/questions/1597442/subquery-using-exists-1-or-exists

반응형