T-SQL에서 CAST를 사용하여 성능 저하


12

지정된 필드에 대해 일반적으로 SQL 조건문을 생성하는 SQL 생성기가 있습니다 (토론을 위해 레이블을 지정합니다 myField).

myField유형이 인 경우 다음 NVARCHAR과 같이 문자열과 해당 필드를 비교할 수 있습니다 myField = 'foo'.

그러나이 유형의 필드에는 작동하지 않습니다 NTEXT. 따라서 캐스트와 비교해야합니다 CAST(myField as NVARCHAR(MAX)) = 'foo'. 이 myField유형이 NVARCHAR또는 인 경우 실제로 작동합니다 NTEXT.

이미 유형 이 지정된 필드에서 위에서 언급 한 캐스트를 수행하면 어떤 성능이 저하 NVARCHAR됩니까? 내 희망은 SQL Server가 myField이미 유형의 것을 동적으로 인식 할 수있을 정도로 똑똑하다는 것 입니다 NVARCHAR(효과적으로 CASTno-op로 전환).


NTEXT (및 TEXT 및 IMAGE)는 공식적으로 사용되지 않으며 향후 버전의 SQL Server (IIRC는 여전히 SQL1014에서 작동하지만)에서 제거 될 예정이므로 NVARCHR (MAX)을 사용해야합니다. (또는 대신 VARCHAR (MAX) 또는 VARBINARY (MAX)). 이 인스턴스에서 NTEXT 열을 NVARCHAR (MAX) 열로 바꾸면 해당 유형으로 직접 비교할 수 있으므로 캐스트가 필요하지 않으며 여기 및 다른 곳에서도 잠재적으로 효율성이 향상됩니다. 불행히도 * (MAX) 열을 인덱싱 할 수 없지만 TEXT / NTEXT 열을 인덱싱 할 수는 없습니다.
David Spillett

답변:


12

열의 캐스트가 정확히 동일한 데이터 유형과 길이에 있고 탐색 술어가 리터럴 인 경우 실제로이를 무시하거나이를 no-op로 취급하고 인덱스가 동등성을 찾는 것 같습니다.

Seek Keys[1]: Prefix: [tempdb].[dbo].[#test].name = Scalar Operator(N'rpc')

열의 캐스트가 동일한 데이터 유형이지만 길이가 길고 탐색 술어가 문자열 리터럴 인 경우 인덱스 스캔이 발생합니다. 이것은 분명히 피해야합니다.

열의 캐스트가 동일한 데이터 유형이고 길이가 같거나 더 길고 seek 술어가 로컬 변수 인 경우 계산 스칼라 연산자를 실행 계획에 추가합니다. 이것은 GetRangeThroughConvert범위를 호출 하고 출력합니다.

이 범위는 인덱스 검색을 수행하는 데 사용되며 매우 효율적으로 보입니다.

Seek Keys[1]: 
Start: [tempdb].[dbo].[#test].name > Scalar Operator([Expr1006]), 
End: [tempdb].[dbo].[#test].name < Scalar Operator([Expr1007])

테스트 코드

SELECT *
 INTO #test
  FROM [master].[dbo].[spt_values]

CREATE NONCLUSTERED INDEX [ixname] ON #test
(
    [name] ASC
)

DECLARE @name NVARCHAR(MAX)

SET @name = 'rpc'

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))= @name --Cast the same and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))=@name --Cast to longer and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))='rpc' --Cast the same and literal

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))='rpc' --Cast to longer and literal

6

일반적으로, CASTMartin Smith의 마지막 예제에서 보여 주듯이 인덱스 검색 사용을 무효화하기 때문에 성능이 저하됩니다. nvarchar(max)다른 길이 로 또는 다른 길이로 캐스트 하는 것은 다른 데이터 유형을 의미합니다 nvarchar. 모든 것이 사실이라는 것은 관련이 없습니다.

또한 비교의 오른쪽 데이터 유형도 중요합니다. 길이가 다른 지역 변수 또는 매개 변수 인 경우 한쪽은 암시 적 CAST으로 두 가지 데이터 유형 중 가장 넓은 것입니다 ( 데이터 유형 우선 순위 참조 ).

기본적으로, 당신이 그것에 일반 CAST을 가지고 있다면 볼 nvarchar(max)릭스 일을합니다. 나는 모든 ntext것을 추가하기 전에 사용을 수정하는 것을 고려할 것 CAST입니다.

쿼리 계획에 변환이 표시되지 않을 수 있습니다. 참조 폴 화이트의 블로그 기사


2

참고로, Datecreated가 datetime 인 곳에 캐스팅

 Cast (Datecreated as date) = cast(@MydatetimeValue as date)

인덱스가 존재하는 경우 인덱스를 사용하는 SQL의 기능을 손상시키지 않으며 존재하지 않는 경우 인덱스가 누락 될 수 있습니다.

에서 캐스팅 할 때 마찬가지로, inttinyintbigint에게 int등 캐스트 기능은 옵티마이 캐스트 작업이 2 개 비교 데이터 유형의 정렬 순서를 변경하지 않은 것을 알고있는 경우 인덱스를 사용하는 SQL을 멈추지 않습니다.

Adventureworks2008R2를 사용하여 실제 계획을 실행하고 볼 수있는 여러 가지 테스트는 다음과 같습니다.

select count(*) from Sales.SalesOrderDetail where SalesOrderID = 8 --1
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as tinyint) = 8  --2
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as bigint) = 8  --3
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) = '19780322' --4
select top 10 SalesOrderID from Sales.SalesOrderDetail where convert(date,ModifiedDate) = '19780322'  --5
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate as varchar(20)) = '1978'  --6 -- THIS WILL NOT USE INDEX
select  SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) between '19780101' and '19780109'  --7

1
날짜로서의 캐스트는 인덱스 탐색을 수행 할 수 있지만 캐스트없이 범위 탐색으로 표현하는 것과 비교하여 여전히 문제가 있습니다 .
Martin Smith
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.