【问题标题】:Stored procedure which writes xml into a DB, T-SQL将xml写入数据库的存储过程,T-SQL
【发布时间】:2020-05-27 03:35:12
【问题描述】:

我正在尝试编写一个将 xml 写入 DB、T-SQL 的存储过程。

这是我的示例 xml(在 prod 环境中将有大量 <RECORD>s):

<?xml version="1.0" encoding="windows-1251"?>
<DATA FORMAT_VERSION="1.0">
    <RECORD>
        <NAME>МІЖНАРОДНА ГРОМАДСЬКА ОРГАНІЗАЦІЯ МІЖНАРОДНА АКАДЕМІЯ БІОЕНЕРГОТЕХНОЛОГІЙ</NAME>
        <SHORT_NAME>МАБЕТ</SHORT_NAME>
        <EDRPOU>00011601</EDRPOU>
        <ADDRESS>01001, м.Київ, Шевченківський район, ВУЛИЦЯ ПРОРІЗНА, будинок 8, офіс 426</ADDRESS>
        <STAN>зареєстровано</STAN>
    </RECORD>
</DATA>

我在@pathToXml参数中传递了xml文件的路径。

这是我的存储过程:

CREATE PROCEDURE [dbo].[LegalContractorsDataSynchronize] 
(
    @pathToXml varchar
)
AS
BEGIN

BEGIN TRANSACTION

DELETE FROM [dbo].[LegalContractors];

INSERT INTO [dbo].[LegalContractors]([Code], [ShortName], [Name], [LegalAddress], [Status])
SELECT CONVERT([Code], [ShortName], [Name], [LegalAddress], [Status])
FROM OPENROWSET(BULK, @pathToXml, SINGLE_CLOB) as x

COMMIT TRANSACTION

END

我正在使用实体框架来调用存储过程。调用刚刚发生(没有错误),但数据库没有更新。我很确定我在INSERT 语句中打错了一些东西。我遵循this 的例子。

有人能指出我如何使用 xml 中各个元素的数据来填充我的数据库中的三列吗?这些列是CodeShortNameNameLegalAddressStatus

更新

发布答案后,我尝试了建议的解决方案。我收到错误消息:

Msg 102,级别 15,状态 1,过程 LegalContractorsDataSynchronize,第 15 行 [批处理开始行 0] '@pathToXml' 附近的语法不正确。

这是我的代码:

CREATE PROCEDURE [dbo].[LegalContractorsDataSynchronize] 
(
    @pathToXml varchar
)
AS
BEGIN

BEGIN TRANSACTION

DELETE FROM [dbo].[LegalContractors];

;WITH XmlFile (xmlData) AS
(
   SELECT TRY_CAST(BulkColumn AS XML) 
   FROM OPENROWSET(BULK @pathToXml, SINGLE_BLOB) AS x
)
INSERT INTO [dbo].[LegalContractors] ([Code], [ShortName], [Name], [LegalAddress], [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(100)') AS [Code]
   , c.value('(SHORT_NAME/text())[1]','NVARCHAR(512)') AS [ShortName]
    , c.value('(NAME/text())[1]','NVARCHAR(2048)') AS [Name]
   , c.value('(ADDRESS/text())[1]','NVARCHAR(2048)') AS [LegalAddress]
   , c.value('(STAN/text())[1]','NVARCHAR(100)') AS [Status]
FROM XmlFile CROSS APPLY xmlData.nodes('/DATA/RECORD') AS t(c);

COMMIT TRANSACTION

END

【问题讨论】:

  • I am using Entity Framework to call the stored procedure. 那你为什么还要使用这个存储过程呢?只需创建适当的 DbContext 和实体并使用 EF 插入数据。现在,您仅在尝试在服务器上模仿 EF 时增加调用开销。如果你真的需要这个存储过程,一个简单的 ADO.NET SqlCommand 会更好。您还可以将数据作为 XML 类型的参数传递
  • 声明一个没有长度的VARCHAR 是一个bad habit。如果您打算在其中填入路径,那就更是如此。
  • @PanagiotisKanavos,xml 大小大于 1Gb。如果我们考虑到应用程序和数据库位于不同的服务器上,那是不可行的(以我手头的基础设施容量)。 xml存储在db服务器端。

标签: sql-server xml tsql stored-procedures


【解决方案1】:

请尝试以下操作。据我所知,OPENROWSET() 不接受文件名参数作为变量。

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (
   ID INT IDENTITY PRIMARY KEY,
   Code NVARCHAR(50) NOT NULL,
   ShortName NVARCHAR(100) NOT NULL,
   [Name] NVARCHAR(100) NOT NULL,
   LegalAddress NVARCHAR(100) NOT NULL,
   [Status] NVARCHAR(50) NOT NULL
);
-- DDL and sample data population, end

-- Method #1
-- XML file is hardcoded
;WITH XmlFile (xmlData) AS
(
    SELECT TRY_CAST(BulkColumn AS XML) 
    FROM OPENROWSET(BULK 'c:\...\Ukraine.xml', /*CODEPAGE = '65001',*/ SINGLE_BLOB) AS x
)
INSERT INTO @tbl (Code, ShortName, [Name], LegalAddress, [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(50)') AS [Code]
   , c.value('(SHORT_NAME/text())[1]','NVARCHAR(100)') AS [ShortName]
    , c.value('(NAME/text())[1]','NVARCHAR(100)') AS [Name]
   , c.value('(ADDRESS/text())[1]','NVARCHAR(100)') AS [LegalAddress]
   , c.value('(STAN/text())[1]','NVARCHAR(50)') AS [Status]
FROM XmlFile CROSS APPLY xmlData.nodes('/DATA/RECORD') AS t(c);

-- test
SELECT * FROM @tbl;

-- Method #2
-- dynamic XML file name as a parameter
DECLARE @xml XML
   , @sql NVARCHAR(MAX)
   , @fileName VARCHAR(256) = 'c:\...\Ukraine.xml';

SET @sql = N'SELECT @xmlOut = XmlDoc FROM OPENROWSET (BULK ' + QUOTENAME(@fileName,NCHAR(39)) + ', SINGLE_BLOB) AS Tab(XmlDoc)';

EXEC master.sys.sp_executesql @sql, N'@xmlOut XML OUTPUT', @xmlOut = @xml OUTPUT;

INSERT INTO @tbl (Code, ShortName, [Name], LegalAddress, [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(50)') AS [Code]
   , c.value('(SHORT_NAME/text())[1]','NVARCHAR(100)') AS [ShortName]
    , c.value('(NAME/text())[1]','NVARCHAR(100)') AS [Name]
   , c.value('(ADDRESS/text())[1]','NVARCHAR(100)') AS [LegalAddress]
   , c.value('(STAN/text())[1]','NVARCHAR(50)') AS [Status]
FROM @xml.nodes('/DATA/RECORD') AS t(c);

-- test
SELECT * FROM @tbl;

【讨论】:

  • 我更新了我的问题。你能帮我解决我偶然发现的问题吗?
  • 正如我之前所说的 "...OPENROWSET() 不接受文件名参数作为变量..."。我更新了答案。
  • 我相信你错了。 OPENROWSET 确实支持文件名。 MSDN: 'data_file' Is the full path of the data file whose data is to be copied into the target table..
  • @hellouworld:它支持文字文件名,而不是变量。语法明确表示('data_file',而不是@string_variable | 'data_file')。该语句必须动态构建。
  • Jeroen,请在 LinkedIn 上与我联系。
猜你喜欢
  • 1970-01-01
  • 2011-04-09
  • 2013-06-26
  • 2018-10-02
  • 1970-01-01
  • 2018-10-03
  • 2013-05-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多