12 performance sql-server type-conversion
我们有一个 SQL 生成器,它通常为指定的字段发出 SQL 条件语句(为了讨论:我们将标记为myField)。
如果myField是类型的NVARCHAR,我们能做到说场对像这样的字符串的比较:myField = 'foo'。
但是,这不适用于类型为 的字段NTEXT。因此,我们必须与演员表进行比较: CAST(myField as NVARCHAR(MAX)) = 'foo'。如果myField是NVARCHAR或类型,这实际上会起作用NTEXT。
在已经是类型的字段上进行上述演员表的性能影响是什么NVARCHAR?我希望 SQL Server 足够聪明,可以动态识别myField已经是类型的NVARCHAR(有效地将其CAST转换为无操作)。
Mar*_*ith 12
如果列的强制转换为完全相同的数据类型和长度,并且搜索谓词是文字,它确实似乎忽略它或将其视为无操作,并在相等性上进行索引搜索。
Seek Keys[1]: Prefix: [tempdb].[dbo].[#test].name = Scalar Operator(N'rpc')
Run Code Online (Sandbox Code Playgroud)
如果列的强制转换为相同的数据类型但长度更大,并且搜索谓词是字符串文字,则会导致索引扫描。这显然是要避免的。
如果列的强制转换是相同的数据类型和相同或更大的长度,并且搜索谓词是一个局部变量,它会向执行计划添加一个计算标量运算符。这会调用GetRangeThroughConvert并输出一个范围。
此范围用于进行索引查找,看起来非常有效
Seek Keys[1]:
Start: [tempdb].[dbo].[#test].name > Scalar Operator([Expr1006]),
End: [tempdb].[dbo].[#test].name < Scalar Operator([Expr1007])
Run Code Online (Sandbox Code Playgroud)
测试代码
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
Run Code Online (Sandbox Code Playgroud)
通常,这CAST会降低性能,因为它会使任何索引查找的使用无效,如 Martin Smith 的最后一个示例所示。转换为nvarchar(max)或转换为不同的长度意味着不同的数据类型:这一切nvarchar都无关紧要。
最重要的是,比较右侧的数据类型也很重要。如果它是不同长度的局部变量或参数,那么一侧将隐式地CAST指向 2 种数据类型中最宽的(请参阅数据类型优先级)。
基本上,如果你有一个将军CAST,nvarchar(max)它就会把事情搞砸。ntext在我CAST全部添加之前,我会考虑修复使用。
转换可能不会显示在查询计划中。参见Paul White 的博客文章
小智 5
请注意,像这样进行投射,其中 Datecreated 是日期时间
Cast (Datecreated as date) = cast(@MydatetimeValue as date)
Run Code Online (Sandbox Code Playgroud)
如果索引存在,则不会破坏 SQL 使用索引的能力;如果索引不存在,则可能会导致记录丢失的索引。
类似地,当从inttotinyint或biginttoint等进行转换时,如果优化器知道转换操作不会更改 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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
19258 次 |
| 最近记录: |