查看this answer 寻找有类似问题的人。对不起,如果这有点啰嗦:
SQL Server 似乎通过将无法表示的字符(没有合适的替换)映射到问号来将 Unicode 扁平化为 ASCII。要复制这一点,请尝试打开 Character Map Windows 程序(应该安装在大多数机器上),选择 Arial 作为字体并找到 U+034f “Combining Grapheme Joiner”。选择此字符,复制到剪贴板并将其粘贴在下面的单引号之间:
declare @t nvarchar(10)
set @t = '͏'
select rtrim(ltrim(@t)) -- we can try and trim it, but by this stage it's already a '?'
你会得到一个问号,因为它在将这个非 ASCII 字符转换为varchar 时不知道如何表示它。如前所述,要强制它接受它作为双字节字符 (nvarchar),您需要改用 N''。在上面的引号之前添加一个N,问号就会消失(但原始的不可见字符会保留在输出中 - 并且ltrim 和rtrim 不会将其删除,如下所示):
declare @t nvarchar(10),
@s varchar(10) -- note: single-byte string
set @t = rtrim(ltrim(N'͏')) -- trimming doesn't work here either
set @s = @t
select @s -- still outputs a question mark
导入的数据肯定可以做到这一点,我以前见过,像我上面展示的那样的字符特别难以诊断,因为你看不到它们!你需要创建某种清理过程以删除这些不可打印的内容(以及任何其他垃圾字符,就此而言),并确保您在任何地方都使用nvarchar,否则您最终会遇到这个问题。更糟糕的是,那些虚幻的问号会变成真正的问号,你将无法与合法的问号区分开来。
要查看您正在处理的字符代码,您可以转换为 varbinary,如下所示:
declare @t nvarchar(10)
set @t = N'͏test?'
select cast(@t as varbinary) -- returns 0x4F0374006500730074003F00
-- Returns:
-- 0x4F03 7400 6500 7300 7400 3F00
-- badchar t e s t ?
现在摆脱它:
declare @t nvarchar(10)
set @t = N'͏test?'
select cast(@t as varbinary) -- bad char
set @t = replace(@t COLLATE Latin1_General_100_BIN2, nchar(0x034f), N'');
select cast(@t as varbinary) -- gone!
注意我必须将字节顺序从0x4f03 交换到0x034f(同样的原因“t”出现在输出中作为0x7400,而不是0x0074)。有关我们为什么使用二进制排序规则的一些说明,请参阅this answer。
这有点混乱,因为你不知道脏字符是什么,它们可能是成千上万种可能性中的一种。一种选择是使用like 甚至unicode() function 遍历字符串,并丢弃不在可接受字符列表中的字符串中的字符,但这可能会很慢。可能您的大多数坏字符都位于字符串的开头或结尾,如果您认为可以做出这样的假设,这可能会加快此过程。
如果要导入大量数据,您可能需要构建 SQL Server 外部或作为 SSIS 导入的一部分的附加流程,以根据我在上面向您展示的内容快速去除这些流程。如果您不确定执行此操作的最佳方法,最好在新问题中回答。
希望对你有帮助。