Phi*_*ord 12 sql-server t-sql sql-server-2016
我有一个名为 的表[User]
。下面是一个从表中查找一个用户的简单查询:
SELECT [Username]\n ,[FirstName]\n ,[LastName]\n ,[EmailAddress]\n ,[IsStaff]\n ,[ExternalID]\n ,[LastLogin]\nFROM [dbo].[User]\nWHERE [Username] = 'pskalhaq' -- 1\n--WHERE [Username] = '\xe2\x80\x8fpskalhaq' -- 2\n
Run Code Online (Sandbox Code Playgroud)\n当我运行此查询时,我得到一条记录,这正是我所期望的。当我注释掉WHERE
标记为“1”的子句并取消注释标记为“2”的子句时,我没有得到任何结果。这很奇怪,因为引号之间的值是相同的。
我发现这个问题是因为我编写的一个 Web 应用程序使用实体框架从另一个表获取记录,[Appointment]
由于表中没有相关记录而抛出错误[User]
,即使该[Appointment]
表包含不可为空的记录外键连接到[User]
表。这似乎是不可能的。我在 SSMS 中运行了等效查询(2892 是给我带来麻烦的约会记录的 ID):
SELECT a.[ID] AS [AppointmentID]\n ,a.[StudentUsername] -- This is the foreign key in the Appointment table\n ,u.[Username] -- This is the primary key in the User table\nFROM [dbo].[Appointment] a\nJOIN [dbo].[User] u \nON u.[Username] = a.[StudentUsername]\nWHERE a.[ID] = 2892\n
Run Code Online (Sandbox Code Playgroud)\n这返回了一个结果,正如预期的那样。所以[Appointment]
记录确实有一个链接[User]
记录。此时,我认为这一定是实体框架中的一个错误(剧透:似乎不是,这就是我在这里发帖而不是在 Stack Overflow 上发帖的原因!)。
但奇怪的是,如果我从查询结果中的[StudentUsername]
或列中复制值并将该值粘贴到上面第一个查询的子句中,我不会从[Username]
WHERE
[User]
表中得到任何结果。如果我在查询中手动输入用户名,情况也是如此。
此时我以为我疯了,所以我在上面进行了查询[User]
表上运行了一个查询,以返回按用户名排序的所有记录:
SELECT [Username]\nFROM [dbo].[User]\nORDER BY [Username]\n
Run Code Online (Sandbox Code Playgroud)\n我向下滚动到我看似失踪的用户应该在的位置,你瞧,记录就在那里!所以我从结果[Username]
中的列中复制了值,将其粘贴到我的第一个查询中,突然该查询按预期返回了单个用户结果。
这里发生了什么?这是 SQL Server 中的某种错误吗?我说 SQL Server 而不是 SSMS,因为似乎很可能同样的问题导致实体框架也说约会没有关联的用户。除非我碰巧复制了值,否则同一个查询怎么可能不返回结果WHERE
除非我碰巧从同一个表上另一个查询的结果中
PS 如果相关的话, 和[Appointment].[StudentUsername]
列[User].[Username]
都是NVARCHAR(200)
。该数据库在 SQL Server 2016 SP2 上运行。
Dav*_*oft 23
它们是不同的字符串。
\nselect cast(N\'pskalhaq\' as varbinary(200)) \nunion all\nselect cast(N\'\xe2\x80\x8fpskalhaq\' as varbinary(200)) \n
Run Code Online (Sandbox Code Playgroud)\n输出
\n 0x700073006B0061006C00680061007100\n0x0F20700073006B0061006C00680061007100\n
Run Code Online (Sandbox Code Playgroud)\n第二个将 U+200F* unicode RIGHT-TO-LEFT MARK 作为第一个字符,因此它在二进制排序规则中不会比较为相等,也许在其他排序规则中也是如此。
\n并且在转换为 时,字符串也不比较相等varchar
,如在此查询中,其中字符串是非 unicode 文字。因此,查询文本中的 unicode 字符将被转换为数据库排序规则的代码页作为varchar
.
select \'\xe2\x80\x8fpskalhaq\' \n
Run Code Online (Sandbox Code Playgroud)\n输出
\n?pskalhaq\n
Run Code Online (Sandbox Code Playgroud)\nU+200F 映射到 0x3F,即普通的“?” 在单字节拉丁代码页中。然后,在与nvarchar
列进行比较之前,必须根据数据类型优先级规则varchar
将值转换为nvarchar
值。
当转换为nvarchar
“?”时 保留为“?”,字符串变为 N\'?pskalhaq\',与 N\'pskalhaq\' 或 N\'\xe2\x80\x8fpskalhaq\' 都不匹配。
*0x0F20 = U+200F,因为字符以小端字节顺序存储。
\n