不显示从文件导入的特殊字符

Fei*_*len 3 sql-server import encoding bulk-insert

该场景是一个 SQL Server 实例,一个主要使用 BULK INSERT 操作提供数据的数据库,并且插入的一些文本包含特殊字符,例如\xc3\xb1因为我在西班牙语环境中工作。

\n\n

因此,在最初的小测试之后,我意识到当我运行简单的时,这些特殊字符没有正确显示select,所以我开始检查我能想到的所有内容:

\n\n
    \n
  • 文件编码:要批量插入的文件具有正确的编码:ANSI
  • \n
  • 数据库编码:数据库具有正确的编码(感谢上帝):select collation_name from sys.databases where name='DBNAME';结果为SQL_Latin1_General_CP1_CI_AS\n\n
      \n
    • Latin1:使用的字符集。这很适合我
    • \n
    • 一般:这里没什么真正有趣的
    • \n
    • CP1:这意味着它使用代码页 1,简而言之,意味着用于编码 WIN-1252 的代码页 1252 <=> 代码页,与 Latin1 非常相似
    • \n
    • CI:不区分大小写
    • \n
    • AS:区分重音,因此 \xc3\xa1 与 a 不同
    • \n
  • \n
  • 将数据导出到文件并检查:文件编码是ANSI ,但数据显示不正确,没有特殊字符,而是我发现一些其他字符使文本难以阅读。
  • \n
\n\n

通过这些测试,我得出结论,数据未正确存储,这就是数据未正确显示和导出的原因。我在互联网上找到的几乎每个解决方案都建议使用nvarchar而不是varchar字符串数据类型字段,但这并不能解决这种情况。是什么破坏了我的插入?

\n

Sol*_*zky 5

如果字符导入不正确,则以下一个(或两个)区域存在问题,因为这实际上是一个两步过程:

\n\n
    \n
  1. BCP / BULK INSERT 不知道文件是如何编码的并且错误地解释它

  2. \n
  3. 目标列是VARCHAR(或CHAR{or TEXT,但不要使用TEXT} ),并且该目标(不是数据库)的排序规则使用的代码页没有错误导入的字符的映射。

  4. \n
\n\n

这里需要注意的是,导入表所在数据库的默认排序规则是不相关的。这里唯一重要的排序规则是导入的每个特定字符串列的排序规则。并且每个字符串列的排序规则可以不同,并且它们都不需要与数据库的默认排序规则相同。通常,数据库中大多数列的排序规则与数据库的默认排序规则相匹配,因为这将是创建新表和列时使用的排序规则,而不是通过关键字指定排序规则COLLATE

\n\n

步骤 1:文件编码
\nBCP / BULK INSERT(或读取文件的大多数其他代码)将不知道文件正在使用什么编码,除非文件使用少数几种具有字节顺序标记(BOM) 的编码之一。但扩展 ASCII 编码不使用字节顺序标记,因此无法以编程方式确定(至少不能确定)。使用扩展 ASCII 编码时,您需要指定代码页,否则将采用默认值。

\n\n

根据bcp Utility的 MSDN 页面,在-C选项下:

\n\n
\n

OEM -> 客户端使用的默认代码页。如果未指定-C,则这是使用的默认代码页。

\n
\n\n

您可以通过打开命令提示符并运行mode或来确定默认代码页chcp(我chcp更喜欢,因为如果您传入一个值,它还允许更改代码页)。

\n\n

如果您的默认代码页是 850,但文件是使用 Windows-1252 Latin1 (ANSI) 编码保存的,则解释文件时很容易出现问题,因为这两个代码页之间的字符映射不同。这与 SQL Server 没有任何关系。

\n\n

\xc3\xb1字符在代码页 1252 上的值为 241。但是,在代码页 850 上,同一字符的值为 164。无论其他如何,该文件都是一系列字节,其中一个字节具有十进制值241(因为保存时,被告知使用代码页 1252,这确定\xc3\xb1需要存储为 241)。现在,当 BCP 读取文件时,如果它使用默认的 MS-DOS 代码页 850,则相同的字节值 241 映射到字符\xc2\xb1。如果您要通过开关指定ACP1252(相同的东西)的代码页-C,那么 BCP 将知道值 241 的字节实际上是\xc3\xb1。或者,您可以指定代码页 1255(Windows 希伯来语),然后 BCP / BULK INSERT 会将相同的字节值 241 解释为字符\xd7\xa1

\n\n

第 2 步:目标列数据类型和排序规则

\n\n

一旦 BCP / BULK INSERT(或任何客户端应用程序)读取数据,它就会作为这些映射而存在,而不仅仅是基本字节值。无论读入 BCP / BULK INSERT 的字符都将作为该字符存储在目标列中,只要该字符可以映射到目标数据类型和排序规则中。目标数据类型NVARCHARNCHARNTEXT(但不使用NTEXT)可以保存所有字符,因此排序规则是什么并不重要。但是,如果目标数据类型是VARCHARCHAR、 或TEXT,则排序规则将确定代码页,而代码页又确定字符映射。如果目标数据类型是最后提到的三种数据类型之一,并且使用与文件所用的相同代码页关联的排序规则,那么一切都应该正常工作。或者,如果排序规则与不同的代码页关联,则将尝试映射字符而不是字节值。

\n\n

这意味着,如果 BCP / BULK INSERT 使用代码页 1252 和字符\xc3\xb1(代码页 1252 上的值 241),那么如果将其导入到排序VARCHAR规则为SQL_Latin1_General_CP850_CI_AS-- 的列中,该列使用代码页 850 -- 那么您将看到一个字符\xc3\xb1(相同的字符,但代码页 850 上的值为 164),而不是\xc2\xb1,它在代码页 850 上具有相同的 241 值。但是,如果您导入到VARCHAR排序规则为Hebrew_CI_AS-- 的列,该列使用代码页1255——那么您将看到?而不是\xd7\xa1(代码页 1255 上的值 241),因为代码页 1255 上没有映射\xc3\xb1

\n