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는 스마트하고 SQL Server가 EXIST에 사용되고 있음을 알고 시스템에 데이터를 반환하지 않습니다.
Quoth Microsoft: http://technet.microsoft.com/en-us/library/ms189259.aspx?ppud=4입니다.
EXISTES에 의해 도입된 하위 쿼리의 선택 목록은 거의 항상 별표(*)로 구성됩니다.하위 쿼리에 지정된 조건을 충족하는 행이 존재하는지 테스트하는 것일 뿐이므로 열 이름을 나열할 이유가 없습니다.
자신을 확인하려면 다음을 실행하십시오.
SELECT whatever
FROM yourtable
WHERE EXISTS( SELECT 1/0
FROM someothertable
WHERE a_valid_clause )
SELECT 목록으로 실제로 작업을 수행 중이라면 0으로 div를 던집니다.그렇지 않아요.
EDIT: SQL Standard에서 실제로 이에 대해 설명합니다.
ANSI SQL 1992 Standard, 191 페이지 http://www.contrib.andrew.cmu.edu/~sql/sql1992.txt입니다.
) 사례: 3) 례례: 례례:
) 만약 a) 으면요?<select list>
는 간단히 "*"에 들어갑니다.<subquery>
바로 그 안에 들어있어요.<exists predicate>
그럼 이에요, 이에요.<select list>
이 말은 '어느 정도 되다'에 해당합니다.<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가 사용 가능한 가장 좁은 인덱스를 사용하여 결과를 확인할 수 있었음을 나타냅니다.인덱스 액세스는 세미 조인 연산자 아래에 있으므로 첫 번째 행이 반환되는 즉시 검색을 중지할 수 있습니다.
그래서 위의 믿음이 틀린 것이 분명합니다.
그러나 Query Optimiser 팀의 코너 커닝햄은 일반적으로 다음과 같이 설명합니다.SELECT 1
이 경우 쿼리 컴파일에서 약간의 성능 차이가 발생할 수 있습니다.
는 모든 QP를 취하여 확장합니다.
*
는 파이프라인의 초기 단계에 있으며 개체(이 경우 열 목록)에 바인딩합니다.그런 다음 쿼리 특성으로 인해 불필요한 열을 제거합니다.그래서 간단하게 말하자면요.
EXISTS
이겁니다.
SELECT col1 FROM MyTable WHERE EXISTS (SELECT * FROM Table2 WHERE MyTable.col1=Table2.col2)
»는 다음과 같습니다.*
잠재적으로 큰 열 목록으로 확장되고, 이 열 목록의 의미론도 결정됩니다. 이겁니다.EXISTS
에는 이러한 열이 필요하지 않으므로 기본적으로 모든 열을 제거할 수 있습니다."
SELECT 1
"는 쿼리 컴파일 중에 해당 테이블에 대한 불필요한 메타데이터를 검사할 필요가 없습니다.그러나 런타임에는 두 형식의 쿼리가 동일하고 런타임도 동일합니다.
다양한 수의 열이 있는 빈 테이블에서 이 쿼리를 표현할 수 있는 네 가지 방법을 테스트했습니다. SELECT 1
대SELECT *
대SELECT Primary_Key
대SELECT 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
조금 더 빨리 나타나긴 합니다.
네 개의 쿼리 모두 테이블의 열 수가 증가함에 따라 성능이 저하됩니다.
테이블이 비어 있기 때문에 이 관계는 열 메타데이터의 양으로만 설명할 수 있는 것으로 보입니다.위해서COUNT(1)
이것은 쉽게 다시 쓰여지는 것을 볼 수 있습니다.COUNT(*)
아래로부터 프로세스의 어느 시점에 도달해야 합니다.
SET SHOWPLAN_TEXT ON;
GO
SELECT COUNT(1)
FROM master..spt_values
그래서 다음과 같은 계획이 주어집니다.
|--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]))
SQL Server 프로세스에 디버거를 연결하고 아래를 실행하는 동안 임의로 중단합니다.
DECLARE @V int
WHILE (1=1)
SELECT @V=1 WHERE EXISTS (SELECT 1 FROM ##T) OPTION(RECOMPILE)
테이블에 1,024개의 열이 대부분 있는 경우 콜 스택은 다음과 같이 나타나며, 이는 다음과 같은 시간 동안 열 메타데이터를 로드하는 데 많은 시간을 소비하고 있음을 나타냅니다.SELECT 1
사용됨(테이블에 열이 1개 있는 경우 무작위로 끊기는 10회 시도 동안 콜 스택의 이 비트에 도달하지 않음)
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
이 수동 프로파일링 시도는 VS 2012 코드 프로파일러에 의해 백업됩니다. VS 2012 코드 프로파일러는 두 경우(상위 15개 함수 1024개 열과 상위 15개 함수 1개 열)에 대해 컴파일 시간을 소비하는 매우 다른 선택 기능을 보여 줍니다.
둘 다요SELECT 1
그리고.SELECT *
사용자에게 테이블의 모든 열에 대한 액세스 권한이 부여되지 않은 경우 버전에서는 열 사용 권한을 검사하고 실패합니다.
산더미 위에 있는 대화에서 오려낸 예시입니다.
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
그래서 어떤 사람은 사용할 때 사소한 명백한 차이가 있다고 추측할 수 있습니다.SELECT some_not_null_col
특정 열에 대한 사용 권한만 검사하게 됩니다(다만 모든 열에 대한 메타데이터를 로드함).그러나 이 값은 기본 표의 열 수가 증가할수록 더 작아지는 경우 두 값 사이의 백분율 차이가 접근하기 때문에 사실과 맞지 않는 것으로 보입니다.
어떤 경우에도 저는 급하게 모든 쿼리를 이 양식으로 변경하지 않을 것입니다. 차이는 매우 작고 쿼리 컴파일 중에만 명확하기 때문입니다.지우다를 제거합니다.OPTION (RECOMPILE)
따라서 후속 실행 시 다음과 같은 캐시된 계획을 사용할 수 있습니다.
+-------------+-----------+------------+-----------+--------------+
| 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 |
+-------------+-----------+------------+-----------+--------------+
두 버전의 성능을 테스트하고 두 버전의 실행 계획을 확인하는 것이 가장 좋습니다.열이 많은 테이블을 선택합니다.
SQL Server에서는 차이가 없으며 SQL Server에서는 문제가 발생한 적이 없습니다.최적기는 두 항목이 같다는 것을 알고 있습니다.실행 계획을 보면 동일한 것을 알 수 있습니다.
개인적으로, 저는 그들이 같은 질의 계획에 최적화되지 않았다는 것을 믿기 어렵습니다.하지만 당신의 특정한 상황에서 알 수 있는 유일한 방법은 그것을 시험해 보는 것입니다.확인되면 다시 보고하세요!
실제 차이는 없지만 아주 적은 공연 히트가 있을 수 있습니다.경험에 비추어 볼 때 필요한 것보다 더 많은 데이터를 요청해서는 안 됩니다.
언급URL : https://stackoverflow.com/questions/1597442/subquery-using-exists-1-or-exists 입니다.
'source' 카테고리의 다른 글
체크아웃하지 않고 분기 포인터를 다른 커밋으로 이동합니다. (0) | 2023.04.25 |
---|---|
Excel 워크시트에서 sql 삽입 스크립트를 생성합니다. (0) | 2023.04.25 |
Windows 배치 파일의 숨겨진 기능입니다. (0) | 2023.04.25 |
ADO와 DAO의 차이입니다. (0) | 2023.04.25 |
C#의 목록에서 중복 항목을 제거합니다. (0) | 2023.04.25 |