作为我正在执行的集成任务的一部分,我的数据库中有一个类型为 nvarchar(30) 的列,我需要调用 WCF 服务并传递此列(以及其他列)中的值,然后将其存储在另一个数据库中类型为 varchar(30) 的列中。
我应该怎么做(大概是在调用 WCF 服务的代码中)将我的字符串转换为“varchar 友好”字符串?
更新:还没有出现任何问题。不过,我将通过此服务对 120,000 条记录进行初始迁移,然后每天将通过此服务推送约 300 条新记录。因此,任何类型的手动干预都是非常不可取的,我只是提前考虑可能会出现什么问题。我无法控制目标数据库(带有 VARCHAR 列),但我确实知道应用程序是 SQL Server 和 C#(不确定它们是否使用 ADO.NET)。
您无需做任何事:.NET 中的所有字符串都是 UTF-16 编码:
NVARCHAR
列(我推测使用 ADO .NET)时,您会自动获得一个 .NET 字符串(UTF-16,任何必要的转换都是自动的)。
VARCHAR
列中时,也会自动执行任何必要的 UTF-16 转换。
请参阅此处,了解有关 ADO .NET 中 Sql Server 数据类型映射的更多信息。
您只需确保所有字符串都可以完美地从源字符集转换为目标字符集。
示例:
Encoding.ASCII.GetString(Encoding.Convert(Encoding.Unicode, Encoding.ASCII, bytes))
如果您不能这样做,那么您需要在发送数据之前检查数据,以确保它是可以转换的数据。根据您遇到的数据问题的类型,您可能需要删除不会转换的字符或将其替换为其他字符,或者将记录放入某种类型的保存表中,而不是发送它以手动修复。艾哈迈德的建议看起来很有可能,我还没有尝试过,所以我不知道它是否会起作用。您可能很幸运,没有任何数据问题(并非每个使用 nvarchar 的数据库都使用具有正确国家/地区排序规则的 varchar 所没有的任何字符),但您必须针对该问题做好计划。
首先,VarChar 比单一类型更复杂。它有多个子集,Sql Server 将其称为排序规则,在 UTF 世界中大致称为代码页。* 我的 en-us 计算机上 VarChar 的默认排序规则是:SQL_Latin1_General_CP1_CI_AS。
这个问题的答案对此做了很多解释:
“COLLATE SQL_Latin1_General_CP1_CI_AS”有什么作用?
重要的一点是它是标准 ASCII 0-127,然后 ASCII 扩展中的其余位必须位于特定的代码页中 - 在本例中为代码页 1252,这也恰好是 Visual Studio en-us 默认值。还有一个有趣的细节,这个默认值的存在是为了向后兼容,但是对性能有害,应该避免(哎呀,从来不知道,它在我的数据库中无处不在)。
现在我们已经介绍了太多相关内容,您应该能够查看您的列并确定您的代码页。我不需要在代码中做任何工作来做到这一点 - 所有 VarChar 都是上面的默认(有害)。因此,在我的系统上,在 C# 中检查列中的值从 NVarChar 转换为 VarChar 时是否会丢失信息,如下所示:
var enc = System.Text.Encoding.GetEncoding(1252);
string converted = enc.GetString(enc.GetBytes(s));
if (s != converted)
...react to the change/loss in info in the string
基于此:
这就是您工作所需的全部内容,但出于工作中的好奇,我根据实际数据测试了上述内容,发现 1000 个字符串中有 40 个不同的字符串,因此,将此列转换为 VarChar 会丢失 4% 的行中的信息(可能很重要)我碰巧采样了。就我而言,大部分变化是对那些空白块或问号的表情符号。有时,表情符号信息对于确定数据的含义很重要。我决定保留 NVarChar 并使用上述检查来查看当数据前往另一个我知道是 VarChar 的系统时数据是否会丢失。我可能会探索寻找一个库或其他东西来转换这种情况下的常见表情符号,例如 🤷u200d♂️ ->(耸肩表情符号)。*排序规则比正在使用的代码页更复杂 - 它们主要是关于如何比较列。这个细节超出了这个问题的范围,所以,实际上,,,我兄弟。