将 XML 解析为 sql 参数、datetime2 和位问题
Posted
技术标签:
【中文标题】将 XML 解析为 sql 参数、datetime2 和位问题【英文标题】:Parse XML to sql parameters, datetime2 and bit questions 【发布时间】:2014-02-27 11:05:15 【问题描述】:我有一个无法解决的难题和一个一般性问题:我正在解析来自供应商的 XML 文件,但无法修改 API 输出。感谢这里所有出色的人,我设法编写代码以将数据转储到 SQL Server 2008 R2 中。我不知道如何完成的一件事是将<date>
节点从其当前形式转换为datetime2
(或您可以推荐的任何datetime
格式)。我尝试了许多不同的方法,每一种都比前一次失败得更惨。
我得到的错误是
将数据类型 nvarchar 转换为 datetime2 时出错
另外,我认为必须有一种更简单的方法来循环遍历子节点。还有 15 个文本字段,如果我可以让 SQL 服务器以某种方式完成繁重的工作,我是否应该能够遍历每个子注释并将名称/值对作为字符串参数发送?
提前致谢。
XML:
<results>
<result>
<title>Application Developer</title>
...
<date>Thu, 30 Jan 2014 14:09:00 GMT</date>
<expired>false</expired>
</result>
sp:
INSERT INTO dbo.StaffWriteImport
(
title ,
...
jbdate ,
expired
)
VALUES
(
@title ,
...
@jbdate ,
CAST(CASE @expired WHEN 1 THEN 1 ELSE 0 END AS BIT)
)
C#:
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream dataStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(dataStream))
string responseFromServer = reader.ReadToEnd();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(responseFromServer);
XmlNodeList Xn = xmlDoc.SelectNodes("//response/results/result");
using (SqlConnection conn = new SqlConnection("data source=myconnectionstring yada yada yada;"))
using (SqlCommand cmd = new SqlCommand(sqlStmt, conn))
foreach (XmlNode xnode in Xn)
XmlElement resultElement = (XmlElement) xnode;
conn.Open();
cmd.CommandType = CommandType.StoredProcedure;
if(!string.IsNullOrEmpty(resultElement.GetElementsByTagName("title")[0].InnerText))
cmd.Parameters.AddWithValue("title", resultElement.GetElementsByTagName("title")[0].InnerText);
...
if (!string.IsNullOrEmpty(resultElement.GetElementsByTagName("date")[0].InnerText))
var dateformat = "yyyy-MM-dd HH:mm:ss:fff";
cmd.Parameters.AddWithValue("jbdate", resultElement.GetElementsByTagName("date")[0].InnerText);
if (!string.IsNullOrEmpty(resultElement.GetElementsByTagName("expired")[0].InnerText))
int exprd = 0;
if (resultElement.GetElementsByTagName("expired")[0].InnerText == "true") exprd = 1;
cmd.Parameters.AddWithValue("expired", exprd) ;
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
conn.Close();
【问题讨论】:
你没有对你的 dateFormat 变量做任何事情,你只是创建它。这可能是一个问题。还使用 XDocument 将使代码更漂亮/更易于使用。最后,如果要插入大量这些结果,请考虑使用 TVP(表值参数)。您将创建一个包含所有结果的 DataTable 并将整个内容传递给 SP。 这是我混淆示例中的一个错字。实际上,我已经尝试过无数种不同的方式。 谢谢大卫。您能否指出一个与我正在做的类似的 XDocument 示例?我自己找不到任何例子。谢谢 你运行的是什么版本的sql server? 问题中说的是 SQL 2008 R2... 【参考方案1】:我会这样做:
-
我会让我的存储过程使用表值参数来一次获取所有结果。此功能自 SQL 2008 起提供,因此您可以使用它。
我将使用 XLinq 进行解析,然后创建结果的 DataTable(需要将其传递给采用表值参数的存储过程)。
SQL
我这里只使用了 3 列作为示例,您可以添加所有需要的列。
-- This is needed to use as a parameter in an SP
CREATE TYPE XmlResultType AS TABLE
(
Title varchar(50),
[Date] datetime2(7),
Expired bit
)
GO
-- Simple stored procedure just copies results from TVP into actual table
Create Procedure usp_InsertXmlResultsByTable
(
@XmlResultTable dbo.XmlResultType READONLY
)
As
Insert Into XmlResult
Select Title, [Date], Expired
From @XmlResultTable
还有 C# 示例:
注意,我知道您正在调用 Web 服务,所以我的示例中,假设 xml
变量是您的 Web 服务调用的结果。无论哪种情况,您都可以使用 XDocument.Parse(xml)
来创建 XDocument
对象。
string xml = @"
<results>
<result>
<title>Application Developer</title>
<date>Thu, 30 Jan 2014 14:09:00 GMT</date>
<expired>false</expired>
</result>
<result>
<title>Incomplete</title>
<date></date>
<expired></expired>
</result>
<result>
<title>Professional Time-waster</title>
<date>Fri, 31 Jan 2014 18:35:00 GMT</date>
<expired>false</expired>
</result>
</results>";
XDocument xDoc = XDocument.Parse(xml);
// Create a DataTable that mimics the Table Valued Parameter structure.
DataTable dt = new DataTable();
dt.Columns.Add("Title", typeof(string));
dt.Columns.Add("Date", typeof(DateTime));
dt.Columns.Add("Expired", typeof(bool));
// Just make the code cleaner
Func<string, string> getString = s => string.IsNullOrEmpty(s) ? null : s;
Func<string, DateTime?> getDateTime = d => string.IsNullOrEmpty(d) ? null : (DateTime?)DateTime.Parse(d);
Func<string, bool?> getBool = b => string.IsNullOrEmpty(b) ? null : (bool?)Convert.ToBoolean(b);
// here we populate the table variable, using above Funcs<> to handle nulls
// This code does assume that your date format, when present, will always be parseable.
xDoc.Descendants("result")
.ToList()
.ForEach(r =>
dt.Rows.Add(
getString(r.Element("title").Value),
getDateTime(r.Element("date").Value),
getBool(r.Element("expired").Value)
)
);
// Now all that's left is to call the stored procedure and pass the DataTable to it
string connString = "your connection string";
using(var conn = new SqlConnection(connString))
using(var cmd = new SqlCommand())
conn.Open();
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "usp_InsertXmlResultsByTable";
SqlParameter tableParameter = cmd.Parameters.AddWithValue("XmlResultTable", dt);
tableParameter.SqlDbType = SqlDbType.Structured;
cmd.ExecuteNonQuery();
conn.Close();
这是一种稍微不同的方法,但它的性能会更好,因为您只需从 Web 服务中调用一次数据库即可获取整个 XML 有效负载。我希望这会有所帮助!
【讨论】:
非常感谢,就像一个魅力!我需要一周的时间才能弄清楚其中的一半,但我很欣赏它利用 .Net 和 SQL 服务器更新的方式!【参考方案2】:因此您无需在应用程序端对其进行粉碎。如果您可以将日期修复为 sql 可以读取的内容,您可以将 xml 传递给 sql,让 sql server 将其切碎并将其插入您需要的表中。这样做的好处是只调用存储过程一次
DECLARE @testXML AS XML = '<Root>
<node>
<id>1</id>
<name>test</name>
<result>cool</result>
</node>
<node>
<id>2</id>
<name>sweet</name>
<result>uncool</result>
</node>
</Root>'
SELECT i.value('(id)[1]','INT') AS id,
i.value('(name)[1]','VARCHAR(50)') AS name,
i.value('(result)[1]','varchar(50)') AS result
FROM @testXML.nodes('Root/node')x(i)
【讨论】:
以上是关于将 XML 解析为 sql 参数、datetime2 和位问题的主要内容,如果未能解决你的问题,请参考以下文章
将 DATETIME 从 SQLite 解析为 java.SQL.Timestamp 时出错
从 SQL 数据库导入表并按日期过滤行时,将 Pandas 列解析为 Datetime
从 SQL 数据库导入表并按日期过滤行时,将 Pandas 列解析为 Datetime
如何将 DateTime 的 SQL Server XML 类型值 (xsi:nil) 转换为 null