在nvarchar字符串中搜索时,SQL Server使用高CPU

时间:2011-01-17 20:25:57

标签: sql-server tsql

查看以下示例。它表明在unicode字符串(nvarchar)中搜索几乎是在varchar字符串中搜索的八倍。与隐含转换相提并论。寻找解释。或者更有效地在nvarchar字符串中搜索的方法。

use tempdb
create table test
(
    testid int identity primary key,
    v varchar(36),
    nv nvarchar(36),
    filler char(500)
)
go

set nocount on
set statistics time off
insert test (v, nv)
select CAST (newid() as varchar(36)),
    CAST (newid() as nvarchar(36))
go 1000000

set statistics time on
-- search utf8 string
select COUNT(1) from test where v like '%abcd%' option (maxdop 1)
-- CPU time = 906 ms,  elapsed time = 911 ms.

-- search utf8 string using unicode (uses convert_implicit)
select COUNT(1) from test where v like N'%abcd%' option (maxdop 1)
-- CPU time = 6969 ms,  elapsed time = 6970 ms.

-- search unicode string
select COUNT(1) from test where nv like N'%abcd%' option (maxdop 1)
-- CPU time = 6844 ms,  elapsed time = 6911 ms.

5 个答案:

答案 0 :(得分:21)

  

寻找解释。

NVarchar是16位且Unicode比较规则比ASCII要复杂得多 - 同时支持的各种语言的特殊字符需要引用更多处理。

答案 1 :(得分:2)

我的猜测是LIKE使用O(n ^ 2)算法而不是O(n)算法实现;它可能必须是领先的%才能发挥作用。由于Unicode字符串的长度是其两倍,因此这与您的数字一致。

答案 2 :(得分:2)

LIKE %%搜索实现为>和< 。现在更多的行数,更多的处理时间,因为SQL无法真正有效地使用%%的统计数据,如搜索。

此外,unicode搜索需要额外的存储空间以及整理复杂性,它通常不如普通的vanilla varchar搜索效率高。您观察到的最快的整理搜索是二进制整理搜索。

这类搜索最适合全文搜索,或者使用带有内存哈希表的FuzzyLookup实现,以防您拥有大量RAM和非常静态的表。

HTH

答案 3 :(得分:1)

我在SQL Server中看到过类似的问题。有一种情况我使用参数化查询,我的参数是UTF-8(默认在.net),字段是varchar(所以不是utf-8)。最后将每个索引值转换为utf-8只是为了进行简单的索引查找。这可能与整个字符串可能被转换为另一个字符集以进行比较有关。同样对于nvarchar,“a”将与“á”相同,这意味着还有更多的工作可以确定unicode中的2个字符串是否相等。此外,您可能希望使用全文索引,但我不确定这是否可以解决您的问题。

答案 4 :(得分:0)

这是因为Unicode字符的排序规则比非Unicode字符的排序规则复杂。

但是,事情并不像varchar vs nvarchar那样简单

您还必须考虑here中所述的SQL整理与Windows整理。

  

SQL Server对定义的非Unicode数据执行字符串比较   Windows归类使用Unicode排序规则。因为这些   规则比非Unicode排序规则复杂得多,它们是   更耗费资源。因此,尽管Unicode排序规则是   往往更昂贵,通常在   Unicode数据和使用Unicode定义的非Unicode数据之间的性能   Windows整理。

如上所述,对于Windows排序规则,SQL Server将对varchar使用unicode排序规则,因此不会提高性能。

这里是一个例子:

-- Server default collation is Latin1_General_CI_AS
create table test
(
    testid int identity primary key,
    v varchar(36) COLLATE Latin1_General_CI_AS, --windows collation
    v_sql varchar(36) COLLATE SQL_Latin1_General_CP1_CI_AS, --sql collation
    nv nvarchar(36),
    filler char(500)
)
go

set nocount on
set statistics time off
insert test (v, nv)
select CAST (newid() as varchar(36)),
    CAST (newid() as nvarchar(36))
go 1000000

set statistics time on

-- search utf8 string
select COUNT(1) from test where v_sql like '%abcd%' option (maxdop 1)
-- CPU time = 625 ms,  elapsed time = 620 ms.

-- search utf8 string
select COUNT(1) from test where v like '%abcd%' option (maxdop 1)
-- CPU time = 3141 ms,  elapsed time = 3389 ms.

-- search utf8 string using unicode (uses convert_implicit)
select COUNT(1) from test where v like N'%abcd%' option (maxdop 1)
-- CPU time = 3203 ms,  elapsed time = 3209 ms.

-- search unicode string
select COUNT(1) from test where nv like N'%abcd%' option (maxdop 1)
-- CPU time = 3156 ms,  elapsed time = 3151 ms.

如您所见,使用Windows排序规则的varchar和nvarchar没有区别。

注意:似乎只包括SQL排序规则用于遗留目的,不应将其用于新项目(即使它们似乎具有更好的性能)。