【问题标题】:Output XML Files with encoding UTF-8 using SQL Server使用 SQL Server 输出编码为 UTF-8 的 XML 文件
【发布时间】:2018-09-20 14:14:27
【问题描述】:

我有一个查询,它生成 XML 文件并使用 <?xml version="1.0"?> 将它们加载到 FTP。

我需要将编码切换为 UTF-8,如下所示:

<?xml version="1.0" encoding="utf-8"?> 

我可以在文本编辑器中手动完成。但不能在 SQL Server 中做到这一点。

我也阅读了这篇文章,但这并没有帮助找到问题的解决方案。

https://docs.microsoft.com/en-us/sql/relational-databases/xml/create-instances-of-xml-data

我的代码:

USE [Audit_DBA]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- to call the procedure use the code below but assign your own path: 
-- EXEC [Audit_DBA].[dbo].[StateAndLocalAward_ToXML] 'C:\NLP\Test\NewFolder'

CREATE PROCEDURE [dbo].[StateAndLocalAward_ToXML_VC]
    @FileDestinationDir VARCHAR(2000)
AS 
    DECLARE  @FilePath VARCHAR(4000)

    DECLARE @SQLStr VARCHAR(8000),
            @Cmd VARCHAR(1000),
            @Ret INT

    IF EXISTS (SELECT * FROM InputTemp.SYS.TABLES WHERE NAME = 'StateAndLocalAward_output') 
       DROP TABLE InputTemp.dbo.StateAndLocalAward_output;

    ;WITH CTE_StateAndLocalAward_output AS 
    (
        SELECT TOP 200
            sc.stateContractId,
            ca.OnviaId AS [Reference], --AS OnviaID,
            ca.contractTitle AS [Title], 
            nigp.NIGPCodeTx AS [NIPGCode],
            nigp.NIGPDescriptionLongTx AS [NIPGDescription],
            ca.Description,
            vm.parentName AS [VendorName],
            offering.offeringTx AS [PrimaryOffering],
            ca.StartDate,
            ca.AwardDt
        FROM 
            ISCore.dbo.StateContract sc WITH (NOLOCK)
        INNER JOIN 
            ISCore.dbo.ContractAward ca WITH (NOLOCK) ON sc.contractAwardId = ca.contractAwardID
        INNER JOIN 
            ISLibrary.dbo.VendorMaster vm WITH (NOLOCK) ON ca.vendorId = vm.vendorID
        LEFT OUTER JOIN 
            ISCore.dbo.CompanyProfile cp WITH (NOLOCK) ON ca.vendorId = cp.vendorId
        LEFT OUTER JOIN 
            ISCore.dbo.Offering offering WITH (NOLOCK) ON ca.offeringID = offering.offeringID
        LEFT OUTER JOIN 
            ISCore.dbo.NIGPSrvc nigp WITH (NOLOCK) ON ca.NIGPCode = nigp.NIGPCodeTx
        WHERE 
            vm.showUnverified = 1 AND sc.stateContractId = -- 464482 stateContractId
            AND StartDate >= DATEADD(month, -12, GETDATE())
    )
    SELECT 
        *
    INTO 
        InputTemp.dbo.StateAndLocalAward_output 
    FROM 
        CTE_StateAndLocalAward_output;

    --select * from InputTemp.dbo.StateAndLocalAward_output

    DECLARE @StateContractId INT;
    --DECLARE @xml XML;

    DECLARE Cur_StateAndLocalAward_StateContractId CURSOR FOR
        SELECT StateContractId
        FROM inputtemp.dbo.StateAndLocalAward_output t1 WITH (NOLOCK)

    OPEN Cur_StateAndLocalAward_StateContractId;

    FETCH NEXT FROM Cur_StateAndLocalAward_StateContractId INTO @StateContractId -- @xml;

    WHILE @@FETCH_STATUS = 0 
    BEGIN
        SELECT 
            @SQLStr = 'SELECT Body.[Reference], Body.[Title], Body.[NIPGCode], Body.[NIPGDescription], Body.[Description], Body.[VendorName], Body.[PrimaryOffering] FROM InputTemp.dbo.StateAndLocalAward_output AS Body where StateContractId = ''' + str(@StateContractId) + ''' FOR XML AUTO, ELEMENTS;'

        --SELECT N'<?xml version="1.0" encoding="UTF-8"?>' 
        SELECT
            CAST((SELECT N'<?xml version="1.0" encoding="UTF-8"?>' + (@SQLSTr)) as varchar(8000) /*as XML*/) as SQLStr 

        -- select CAST((SELECT N'<?xml version="1.0" encoding="UTF-8"?>' + 
        --  (@SQLSTr)) as XML) as SQLStr 

如果我运行此代码而不是上面相同的代码,但转换为 XML type,我会收到错误:

消息 9402,级别 16,状态 1,过程 StateAndLocalAward_ToXML,第 86 行 [批处理开始第 10 行]
XML解析:第1行,第38个字符,无法切换编码

select @FilePath = @FileDestinationDir+'\NewFolder'+ltrim(rtrim(str(@StateContractId)))+'.xml' 
select @Cmd = ' bcp " ' + @SQLStr + '" queryout '+@FilePath+' -w -r "" -T -S ' +@@ServerName 
exec  @Ret = master.dbo.xp_cmdshell @Cmd 
FETCH NEXT FROM Cur_StateAndLocalAward_StateContractId INTO @StateContractId -- @xml;
END
CLOSE Cur_StateAndLocalAward_StateContractId;
DEALLOCATE Cur_StateAndLocalAward_StateContractId;
GO

【问题讨论】:

    标签: sql-server xml encoding bcp


    【解决方案1】:

    有一些事情要知道:

    • SQL Server 不支持通过 BCP 导出到 2016 版之前的 UTF-8(以及带有 SP2 的 2014 版)
    • 无法将 xml 声明 (&lt;?xml blah ?&gt;) 添加到本机 SQL-Server XML 类型变量或列。这将失败(“...切换编码”)或 xml 声明将消失。
    • 您可以将字符串级别的 xml 声明添加到转换为 NVARCHAR(MAX) 的 xml。但是您不能在不失败或丢失声明的情况下将其重新转换(重新转换)为 XML。
    • 在任何情况下,SQL-Server 在内部都将任何 XML 保留为 UCS-2(非常接近 UTF-16)。
    • SQL-Servers VARCHAR (CHAR) 类型不是utf-8 而是扩展的ASCII(取决于COLLATION
    • 在字符串级别上,您可以将任何内容写入 xml 声明(因为您可以创建一些看起来像 XML,但格式不正确的内容。这只是一个未经检查的字符串。
    • xml 声明中规定的编码很重要仅用于在写入磁盘或作为字节流处理时标记实际文件编码
    • 您可以编写 encoding="x" 并使用编码 y 存储文件 - 但您不应该这样做。
    • SQL-Server 会将带有 utf-8 声明的字符串转换为 XML,当它是 VARCHAR 时,它会转换带有 utf-16 的字符串,当它是 NVARCHAR,但你不能跨越这个 (Read this related answer )。其他编码很可能导致无法切换编码错误。

    关于您的代码

    • 您应该将@SQLStr@cmd 更改为NVARCHAR(MAX),否则您可能会遇到非纯拉丁字符的问题。
    • 当您使用CURSOR 时,您应该使用语句的结果填充一个XML 类型的变量,将其转换为NVARCHAR(MAX) 并将声明添加到此字符串中。不要将结果转换回 XML。
    • 阅读BCP 文档。声明-w 会写成unicode (wide),而不是utf-8(你在声明中写的内容在这里没有作用)。

    提示:

    Read this related answer, showing utf-8 export with BCP on SQL-Server 2016

    【讨论】:

    • 非常感谢 Shnugo。我确实根据您的建议更改了我的代码 - SQLStr nvarchar(max) 和 Cmd nvarchar(4000) 在变量声明级别,我还需要将数据库兼容性级别切换到 2016。生成的 XML 文件将编码切换到 UTF-8 时我在记事本++中打开它们并查找编码。但是当我在 Internet Explorer 中打开 XML 文件时,XML 显示的是 而不是
    • 有没有办法在 XML 文件结构开始之前指定
    • @enigma6205,没有 SQL Server 不会为您执行此操作。但是您可以轻松地将转换后的 XML 与您想要的任何字符串连接起来。使用SET @content=N'&lt;?xml whatever ?&gt;' + CAST(@theXml AS NVARCHAR(MAX)) 并写下来。
    【解决方案2】:
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    
    CREATE PROCEDURE [dbo].[MyXMLTest]
    @FileDestinationDir VARCHAR(2000)
    
    -- to call procedure specify your own file path 
    -- EXEC [Audit_DBA].[dbo].[MyXMLTest] 'E:\NLP\GovwinIQ_Ontology\NewFolder'
    
    AS 
    
    SET QUOTED_IDENTIFIER ON
    
    IF OBJECT_ID (N'InputTemp.dbo.XMLTest', N'U') IS NOT NULL
    DROP TABLE InputTemp.dbo.XMLTest;
    
    CREATE TABLE InputTemp.dbo.XMLTest
    
    (
    [Id] INT NOT NULL,
    [FirstName] VARCHAR(100) NOT NULL,
    [LastName] VARCHAR(100) NOT NULL,
    [Address] VARCHAR(100) NOT NULL
    );
    
    INSERT INTO InputTemp.dbo.XMLTest ([Id], [FirstName], [LastName], [Address])
    VALUES (12, 'Zhuk', 'Termik', '123 Gam Str, Boston, NY');
    
    --SELECT * FROM InputTemp.dbo.XMLTest
    
    DECLARE @FilePath VARCHAR(4000)
    
    DECLARE @SQLStr NVARCHAR(4000),
            @Cmd NVARCHAR(4000),
            @Ret INT
    
    DECLARE @Id INT;
    
    SELECT @Id = 12;
    
    SELECT @SQLStr = 
    'SELECT N''<?xml version=''''1.0'''' encoding=''''UTF-8''''?>'' + (SELECT CAST((SELECT [Id], [FirstName], [LastName], [Address] FROM InputTemp.dbo.XMLTest AS Body WHERE Id = '''  + str(@Id) + ''' FOR XML AUTO, ELEMENTS) AS NVARCHAR(MAX)))'
    
    SELECT @SQLStr AS SQLStr
    
    SELECT @FilePath = @FileDestinationDir+'\NewFolder'+ltrim(rtrim(str(@Id)))+'.xml' 
    
    SELECT @Cmd = ' bcp " ' + @SQLStr + '" queryout '+@FilePath+' -c  -C65001 -r "" -T -S ' +@@ServerName 
    
    EXEC @Ret = master.dbo.xp_cmdshell @Cmd 
    
    IF OBJECT_ID (N'InputTemp.dbo.XMLTest', N'U') IS NOT NULL
    DROP TABLE InputTemp.dbo.XMLTest;
    
    GO
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-20
      • 1970-01-01
      • 2012-02-18
      • 2011-03-16
      • 2016-08-14
      • 1970-01-01
      • 2013-08-04
      相关资源
      最近更新 更多