为什么在linq-to-sql选择中比较UTF16字符串时会得到错误的结果?

本文关键字:错误 结果 字符串 linq-to-sql 选择 UTF16 比较 为什么 | 更新日期: 2023-09-27 18:08:03

我使用c#和。net 4.0与MS SQL 2008。

我正在运行一个集成测试来验证数据是否得到正确的存储和检索。它往往会失败。当我查看它时,我发现从linq-to-sql调用返回的值是错误的。我对linq-to-sql语句进行了分析,发现在Server Management Studio中,被分析的SQL返回错误的值,而具有相同参数的手动输入查询工作正确。

sql查询和结果:

exec sp_executesql N'SELECT TOP (1) [t0].[ID], [t0].[UserName], [t0].TCID
FROM [dbo].[Users] AS [t0]
WHERE ([t0].[TCID] = @p0) AND ([t0].[UserName] = @p1)',N'@p0 int,@p1
nvarchar(4000)',@p0=8,@p1=N'ҭРӱґѻ'

搜索结果

ID        UserName    TCID
2535      ҭРґѻӱ       8

如您所见,UserName与相等性检查中的内容不匹配。

如果我这样做,我得到预期的结果:

SELECT TOP 1000 [ID]
    ,[UserName]
    ,[TCID]
FROM [dbo].[Users]
where TCID=8 and username = 'ҭРӱґѻ'

I get back:

ID        UserName    TCID

正确。

UserName is nvarchar(50), ID和TCID为int。

知道为什么第一个查询得到错误的结果吗?

为什么在linq-to-sql选择中比较UTF16字符串时会得到错误的结果?

你没有得到第二个查询的结果,因为你忘记了前缀n的参数,我打赌你得到的结果就像与动态SQL如果你使用:

SELECT TOP 1000 [ID]
    ,[UserName]
    ,[TCID]
FROM [dbo].[Users]
where TCID=8 and username = N'ҭРӱґѻ'; -- note the N prefix here

现在,我并不是说您应该得到一个结果,但这应该使您的两个测试方法之间的行为一致。列的排序规则是什么?您可以通过指定二进制排序来"修复"这个问题。例如,这应该产生正确的行为:

SELECT COUNT(*) 
  FROM [dbo].[Users]
  WHERE [UserName] = N'ҭРӱґѻ' COLLATE Latin1_General_BIN;
-- 0
SELECT COUNT(*) 
  FROM [dbo].[Users]
  WHERE [UserName] = N'ҭРґѻӱ' COLLATE Latin1_General_BIN;
-- 1

对于正在使用的排序规则(可能是SQL server特定的排序规则),没有定义一些Unicode代码点。因此,SQL Server将它们视为空字符串:

SELECT CASE WHEN N'ӱ' COLLATE SQL_Latin1_General_CP1_CI_AS = N " THEN 'YES' ELSE 'NO' END

如果我们使用较新的Windows排序规则,如Cyrillic_General_100_CI_AS,我们会看到这些字符串不匹配:

当N'ӱ' COLLATE Cyrillic_General_100_CI_AS = N " THEN 'YES' ELSE 'NO' END