是否可以使用 OPENROWSET 导入固定宽度的 UTF8 编码文件?

Mar*_*ith 9 sql-server azure-sql-database encoding openrowset

我有一个包含以下内容的示例数据文件,并使用 UTF8 编码保存。

\n
oab~opqr\n\xc3\xb6ab~\xc3\xb6pqr\n\xc3\xb6ab~\xc3\xb6pqr\n
Run Code Online (Sandbox Code Playgroud)\n

该文件的格式是固定宽度,第 1 至第 3 列各分配 1 个字符,第 4 列保留 5 个字符。

\n

我创建了一个 XML 格式文件,如下所示

\n
<?xml version = "1.0"?>  \n<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  \n   <RECORD>  \n      <FIELD xsi:type="CharFixed" ID="Col1" LENGTH="1"/>       \n      <FIELD xsi:type="CharFixed" ID="Col2" LENGTH="1"/> \n      <FIELD xsi:type="CharFixed" ID="Col3" LENGTH="1"/> \n      <FIELD xsi:type="CharFixed" ID="Col4" LENGTH="5"/> \n      <FIELD xsi:type="CharTerm" ID="LINE_BREAK" TERMINATOR="\\n"/> \n   </RECORD>  \n   <ROW>  \n      <COLUMN SOURCE="Col1" NAME="Col1" xsi:type="SQLNVARCHAR"/>  \n      <COLUMN SOURCE="Col2" NAME="Col2" xsi:type="SQLNVARCHAR"/> \n      <COLUMN SOURCE="Col3" NAME="Col3" xsi:type="SQLNVARCHAR"/>  \n      <COLUMN SOURCE="Col4" NAME="Col4" xsi:type="SQLNVARCHAR"/> \n   </ROW>  \n</BCPFORMAT>\n
Run Code Online (Sandbox Code Playgroud)\n

令人失望的是运行以下 SQL...

\n
SELECT *\nFROM OPENROWSET\n(\nBULK 'mydata.txt',\nFORMATFILE = 'myformat_file.xml',\nCODEPAGE = '65001'\n) AS X\n
Run Code Online (Sandbox Code Playgroud)\n

产生以下结果

\n
Col1 Col2 Col3 Col4\n---- ---- ---- -----\no    a    b    ~opqr\n\xef\xbf\xbd    \xef\xbf\xbd    a    b~\xc3\xb6p\n\xef\xbf\xbd    \xef\xbf\xbd    a    b~\xc3\xb6p\n
Run Code Online (Sandbox Code Playgroud)\n

由此我得出结论,LENGTH计算的是字节而不是字符。

\n

有什么办法可以让这个工作在UTF8 编码的固定字符宽度下正常工作吗?

\n

(目标环境是从 Blob 存储读取的 Azure SQL 数据库)

\n

注意:评论中建议添加COLLATION="LATIN1_GENERAL_100_CI_AS_SC_UTF8"元素FIELD可能会有所帮助,但结果保持不变。

\n

Mar*_*ith 9

一种解决方法是仅更改格式文件以批量导入整行,并在 TSQL 中进行子字符串化

带格式文件

<?xml version = "1.0"?>  
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
   <RECORD>  
      <FIELD xsi:type="CharTerm" ID="WholeLine" TERMINATOR="\n"/> 
   </RECORD>  
   <ROW>  
      <COLUMN SOURCE="WholeLine" NAME="WholeLine" xsi:type="SQLNVARCHAR"/> 
   </ROW>  
</BCPFORMAT>
Run Code Online (Sandbox Code Playgroud)

以下确实返回了期望的结果

SELECT SUBSTRING(WholeLine, 1,1) AS Col1,
       SUBSTRING(WholeLine, 2,1) AS Col2,
       SUBSTRING(WholeLine, 3,1) AS Col3,
       SUBSTRING(WholeLine, 4,5) AS Col4
FROM OPENROWSET
(
BULK 'mydata.txt',
FORMATFILE = 'myformat_file.xml',
CODEPAGE = '65001'
) AS X
Run Code Online (Sandbox Code Playgroud)


Pau*_*ite 5

由此我得出结论,LENGTH计算的是字节而不是字符。

这是正确的,但没有办法将其改为字符。

这种情况类似于char( n )、varchar( n )、nchar( n ) 和 nvarchar( n ) 中的 n 其中n表示字节不是字符数。请参阅文档

一个常见的误解是认为 CHAR(n) 和 VARCHAR(n),n 定义字符数。但在 CHAR(n) 和 VARCHAR(n) 中,n 定义以字节为单位的字符串长度 (0-8,000)。n 从不定义可以存储的字符数。这与 NCHAR(n) 和 NVARCHAR(n) 的定义类似。产生这种误解是因为使用单字节编码时,CHAR和VARCHAR的存储大小为n个字节,字符数也是n。但是,对于 UTF-8 等多字节编码,较高的 Unicode 范围 (128-1,114,111) 会导致一个字符使用两个或更多字节。

这让许多人感到困惑,尤其是自从引入UTF-8 支持之后。以前使用 n(var)char 和增补字符是可能的,但我想说相对很少遇到。

如果 SQL Server将来在多个领域(包括 OPENROWSET)扩展对字符而不是字节的支持,那就太好了。

与此同时,我也可能会使用您的解决方法。