我们有以下设置:
- 源服务器版本 12 或 15,排序规则 Latin1_General_CI_AS(在我们的责任范围内,我们可以更改此处的所有内容)
- 链接服务器版本 13,排序规则:Latin1_General_CI_AS(不属于我们的责任,我们无法在此处更改任何内容)
- 链接数据库排序规则:SQL_Latin1_General_CP1_CI_AS
- NHibernate 用作 ORM 来创建查询(这意味着:灵活性较低),仅对链接服务器上的表进行很少的查询。
-
链接服务器选项:
sp_addlinkedserver @server = N'MyLinkedServer', @srvproduct=N'SQL Server'
'排序规则兼容='假',
'数据访问='true',
'距离='假',
'pub='假',
'rpc='假',
'rpc 输出='假',
'子='假',
'连接超时='0',
'排序规则名称=ull,
'惰性模式验证='false',
'查询超时='0',
'使用远程排序规则='true',
'远程进程交易提升='true'
目标表链接服务器上的目标表列看起来像这样:
PrimaryKey varchar(23)
OtherThing varchar(42)
我们发出这样的查询:
SELECT OtherThing FROM TargetTableOnLinkedServer WHERE PrimaryKey = 'Hello World'
我们也尝试与sp_execute
参数为 varchar(8000) - 下面有更详细的查询。
在 SQL Server Profiler 中,我们看到链接查询基本上被转换为:
SELECT OtherThing FROM TargetTableOnLinkedServer WHERE PrimaryKey = N'Hello World'
请注意这里的“N”。 “N”(如 Nvarchar 中的)导致索引扫描,而不是索引查找。
不幸的是,我们找不到抑制N的方法,也不知道原因。
有没有办法防止“链接服务器机制”将参数“转换”为 unicode 字符串(通过添加 N)。
提前致谢!
更详细的查询:
SELECT MyColumn FROM MyLinkedServer.MyDatabase.dbo.MyTable A WHERE A.MyColumn = 'MyValue'
这会导致探查器 SP:StmtCompleted 事件在链接服务器上
SELECT "Tbl1001"."MyColumn" "Col1003" FROM "MyDatabase"."dbo"."MyTable" "Tbl1001" WHERE "Tbl1001"."MyColumn"=N'MyValue'
以及 RPC:Completed 事件:
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,NULL,N'SELECT "Tbl1001"."MyColumn" "Col1003" FROM "MyDatabase"."dbo"."MyTable" "Tbl1001" WHERE "Tbl1001"."MyColumn"=N''MyValue'''
select @p1
NHibernate 创建如下查询:
EXEC sp_executesql N'SELECT MyColumn FROM MyLinkedServer.MyDatabase.dbo.MyTable A WHERE A.MyColumn = @p1',N'@p1 varchar(8000)', @p1='MyValue'
请注意 varchar 和缺少“N”。
这会导致以下查询带有索引扫描(至少这显示了我在 SMSS 中复制粘贴查询时的执行计划):
declare @p1 int
exec sp_prepexec @p1 output,N'@P1 nvarchar(4000)',N'SELECT "Tbl1001"."MyColumn" "Col1004" FROM "MyDatabase"."dbo"."MyTable" "Tbl1001" WHERE "Tbl1001"."MyColumn"=@P1',N'MyValue'
select @p1
正确的做法是使用索引查找:
exec sp_prepexec @p1 output,N'@P1 varchar(4000)',N'SELECT "Tbl1001"."MyColumn" "Col1004" FROM "MyDatabase"."dbo"."MyTable" "Tbl1001" WHERE "Tbl1001"."MyColumn"=@P1','MyValue'
select @p1
我还尝试使用另一个提供程序 MSOLEDBSQL(显然是最现代的),没有任何变化:
EXEC master.dbo.sp_addlinkedserver @server = N'MyServer', @srvproduct=N'', @provider=N'MSOLEDBSQL', @provstr=N'Server=MyServerName'