SARG 카디널리티 추정, 왜 전체 스캔하지 않습니까?


11

전체 스캔이없는 이유는 무엇입니까 (SQL 2008 R2 및 2012)?

테스트 데이터 :

DROP TABLE dbo.TestTable
GO  
CREATE TABLE dbo.TestTable
(
   TestTableID INT IDENTITY PRIMARY KEY,
   VeryRandomText VarChar(50),
   VeryRandomText2 VarChar(50)
)
Go
Set NoCount ON
Declare @i int
Set @i = 0
While @i < 10000
Begin
   Insert Into dbo.TestTable(VeryRandomText, VeryRandomText2)
      Values(Cast(Rand()*10000000 as VarChar(50)), Cast(Rand()*10000000 as VarChar(50)));
   Set @i = @i + 1;
End
Go
CREATE Index IX_VeryRandomText On dbo.TestTable
(
    VeryRandomText
)
Go

쿼리를 실행할 때 :

Select * From dbo.TestTable Where VeryRandomText = N'111' -- bad

nchar 데이터를 varchar 열과 비교하기 때문에 예상대로 경고를 받으십시오.

<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(nvarchar(50),[DemoDatabase].[dbo].[TestTable].[VeryRandomText],0)" />

그러나 실행 계획을 보았을 때 예상대로 전체 스캔을 사용하지 않고 대신 인덱스 검색을 사용하고 있음을 알 수 있습니다.

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

물론,이 경우에는 전체 스캔이 수행되는 것보다 실행 속도가 훨씬 빠르기 때문에이 방법이 좋습니다.

그러나이 계획을 결정하기 위해 SQL 서버가 어떻게 결정되었는지 이해할 수 없습니다.

또한 서버 데이터 정렬이 서버 수준 및 SQL Server 데이터 정렬 데이터베이스 수준의 Windows 데이터 정렬 인 경우 동일한 쿼리에서 전체 검색이 수행됩니다.

답변:


8

다른 데이터 형식의 값을 비교할 때 SQL Server는 데이터 형식 우선 순위 규칙을 따릅니다 . nvarchar가 varchar보다 우선 순위가 높기 때문에 SQL Server는 값을 비교하기 전에 열 데이터를 nvarchar로 변환해야합니다. 즉, 열에 함수를 적용하면 쿼리를 처리 할 수 ​​없게됩니다.

그러나 SQL Server는 실수로부터 사용자를 보호하는 것이 최선의 방법이므로 블로그 게시물 동적 검색 및 숨겨진 암시 적 변환 에서 Paul White가 설명한 기술을 사용 하여 다양한 값을 찾은 다음 최종 비교를 수행합니다. 모든 오 탐지를 필터링하기 위해 잔존 술어에서 열 값을 nvarchar로 변환

언급했듯이 열의 데이터 정렬이 SQL 데이터 정렬 인 경우에는 작동하지 않습니다. 그 이유는 SQL 데이터 정렬을 Windows 데이터 정렬과 비교 기사에서 찾을 수 있습니다.

기본적으로 Windows 데이터 정렬은 varchar 및 nvarchar에 대해 동일한 알고리즘을 사용합니다. 여기서 SQL 데이터 정렬은 varchar 데이터에 대해 다른 알고리즘을 사용하고 nvarchar 데이터에 대한 Windows 데이터 정렬과 동일한 알고리즘을 사용합니다.

따라서 Windows 데이터 정렬에서 varchar에서 nvarchar로 이동하면 동일한 알고리즘이 사용되며 SQL Server는 nvarchar 리터럴에서 varchar SQL 데이터 정렬 열 인덱스에서 행을 가져 오는 범위의 값을 생성 할 수 있습니다. 그러나 varchar 열의 데이터 정렬이 사용 된 다른 알고리즘으로 인해 불가능한 SQL 데이터 정렬 인 경우.


최신 정보:

windows 및 sql 데이터 정렬을 사용하는 varchar 열의 다른 정렬 순서에 대한 데모.

SQL 바이올린

MS SQL Server 2014 스키마 설정 :

create table T(C varchar(10));

insert into T values('a-b'),('aa'),('ac');

쿼리 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

결과 :

|   C |
|-----|
| a-b |
|  aa |
|  ac |

쿼리 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

결과 :

|   C |
|-----|
|  aa |
| a-b |
|  ac |

0

비 클러스터형 인덱스의 리프 노드는 데이터 행을 찾기 위해 클러스터링 키 또는 RID가 포함 된 인덱스 페이지로 구성되어 있음을 기억해야합니다.

where 절 VeryRandomText = N'111'에서 VeryRandomText에 클러스터되지 않은 인덱스가 있기 때문에 (클러스터를 만들도록 명시 적으로 지시하지 않는 한 인덱스를 만들면 클러스터되지 않은 인덱스가 생성됨) 데이터를 찾는 가장 저렴한 방법은 인덱스를 스캔하여 rowid를 찾고 그런 다음 행의 데이터를 가져옵니다.

클러스터형 인덱스를 만드는 경우

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

VeryRandomText의 기본 키를 사용하면 해당 인덱스를 스캔 할 수 있습니다.

온라인 또는 여기에서 책을 참조하십시오 : http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap


네, 당신이 쓰는 것을 알고 있습니다. 보다시피, TestTableID에는 이미 클러스터 된 인덱스가 있습니다. 그러나 SQL 서버가 열 데이터 분배 통계를 볼 수없는 경우 (이 경우와 같이 모든 행 값 데이터 유형 변환이 필요한 데이터 유형 불일치로 인해)이 경우 인덱스 탐색이 아닌 클러스터형 인덱스 스캔을 선택해야합니다 .
Jānis

비 클러스터형 인덱스를 찾거나 검색하는 것이 항상 가장 저렴한 것은 아닙니다. 값이 충분히 다르거 나 포함되지 않는 인덱스 인 경우 대신 클러스터형 인덱스 스캔을 수행하는 것이 더 저렴할 수 있습니다.
Jānis

@ Jānis가 스크립트 생성 인덱스에 동의하지 않으면 명시 적으로 말해야 할 클러스터 된 인덱스가 생성되지 않습니다. 쿼리 계획을 읽고 인덱스 검색 (비 클러스터)을 읽는 것과 동일
Spörri

"PRIMARY KEY 제약 조건을 만들 때 테이블에 클러스터 된 인덱스가없고 고유 한 비 클러스터형 인덱스를 지정하지 않으면 열에 고유 한 클러스터형 인덱스가 자동으로 만들어집니다." msdn.microsoft.com/ko-kr/library/ms186342.aspx
Jānis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.