【问题标题】:Fastest way to convert NVARCHAR value to XML将 NVARCHAR 值转换为 XML 的最快方法
【发布时间】:2016-05-26 15:24:08
【问题描述】:

在我继承以支持的数据库中,有几个表的 NVARCHAR(MAX) 列包含一团无类型 XML 数据。我的任务是在这些 XML blob 中搜索对给定 ID 的引用,然后在找到第一个引用时停止并报告找到的内容。我目前正在遍历每一行,将 blob 转换为 XML,然后使用 XQuery 在 blob 中搜索 ID。

但是,由于表中的行数过多,将 NVARCHAR 值初始转换为 XML 的时间过长。下面是我用来转换每个 blob 的代码示例,HistoryID 是 PK,值发生变化以从 XMLData 获取下一个 XML blob:

SELECT @XML = CONVERT(XML, XMLData)
FROM dbo.History
WHERE HistoryID = 1;

我也尝试过利用 C# 和 LINQ,但 XML blob 太大而无法从 .NET 到 SQL Server 来回传递。不幸的是,我也无法在不破坏应用程序的情况下修改现有表。有没有更好的方法将这些 XML blob 转换为可搜索的形式,或者我只是 SOL?

【问题讨论】:

  • 需要转成xml吗?你不能简单地用 patindex 搜索 nvarchar 吗?
  • 你能展示一个xml结构的例子吗?..
  • 每行典型的 XML blob 有多大?
  • @JamieD77,不幸的是,XML 包含大量 PII 数据,但我可以说它们似乎有 3,000-4,000 行长,并且嵌套深度为 8 层。
  • @WillC 看起来最小为 22 字节,最大为 340 MB,平均为 6 MB。

标签: c# sql-server xml linq tsql


【解决方案1】:

SQL 对数据库中的 XML 有值、存在、节点、查询和修改等方式。然后可以查询 XML 元素的 value 属性。更多信息:https://www.simple-talk.com/sql/learn-sql-server/the-xml-methods-in-sql-server/

类似:

SELECT 
CONVERT(XML, XMLData).value('(/Updated/UserId)[1]', 'int') AS UpdatedId 
FROM History
WHERE CONVERT(XML, XMLData).exist('(/Updated/UserId)[1]') > 0

如果某些 xmldata blob 大至 360mb,将不得不尝试看看会发生什么...

为了提高性能,可以将 XML 索引(如果可以修改架构)添加到表 https://msdn.microsoft.com/en-us/library/ms191497.aspx。

【讨论】:

  • 我已经在为搜索部分做这样的事情,但这取决于它已经在 SQL Server 中作为 XML 存储。目前数据位于 NVARCHAR 字段中,问题是将该值转换为 XML,以便我可以搜索它,而不是搜索本身。
  • 好的。 “无法修改现有表”是重要部分。
  • 你试过在隐蔽的 XmlData 列上使用 .value 吗?
  • “找不到列“XMLData”或用户定义的函数或聚合“XMLData.value”,或者名称不明确。看起来“.value”不适用于 NVARCHAR 数据。
  • 是这样的吗? SELECT CONVERT(XML, XMLData).value('(/Updated/UserId)[1]', 'int') AS UpdatedId FROM History
【解决方案2】:

不需要循环...试试这样

CREATE TABLE #tbl (ID INT,SomeColumn VARCHAR(100),YourXMLasString NVARCHAR(MAX));
INSERT INTO #tbl VALUES
 (1,'Test 11','<root><id>100</id><more>abc</more></root>')
,(2,'Test 12','<root><id>100</id><more>abc</more></root>')
,(3,'Test 21','<root><id>200</id><more>def</more></root>')
,(4,'Test 22','<root><id>200</id><more>def</more></root>')
,(5,'Test 23','<root><id>200</id><more>def</more></root>')
,(6,'Test  3','<root><id>300</id><more>ghi</more></root>');

--Just take the id 200
SELECT *
FROM #tbl
CROSS APPLY(SELECT CAST(YourXMLasString AS XML)) AS A(RealXml)
WHERE RealXml.exist('/root[id=200]') = 1

--Now stop at the first with 200
SELECT TOP 1 *
FROM #tbl
CROSS APPLY(SELECT CAST(YourXMLasString AS XML)) AS A(RealXml)
WHERE RealXml.exist('/root[id=200]') = 1
ORDER BY ID
GO

DROP TABLE #tbl;

一些提示

每当您搜索 ID 时您都会一遍又一遍地进行转换 ...当然,如果您存储转换后的 XML,它会快得多 。

如果您更频繁地搜索相同的值,最好从 XML 中读取这些值并将其存储在专用的索引列中...

有了这个,你可以在现有的表中添加一列

ALTER TABLE #tbl ADD RealXml XML;
GO
UPDATE #tbl SET RealXml=CAST(YourXMLasString AS XML);
SELECT * FROM #tbl;

这样你就可以创建一个边桌

CREATE TABLE #SideTable(RefID INT,RealXML XML);
INSERT INTO #SideTable
SELECT ID,CAST(YourXMLasString AS XML)
FROM #tbl;

SELECT * FROM #SideTable;

如果数据发生变化,您可以考虑使用触发器立即对侧列/表进行转换和更新。

【讨论】:

    【解决方案3】:
        // xmlElement is the column read from DB , consider having an index on the field as well as batching the reads if its too big   
        public static List<History> DeserializeHistory(XElement xmlElement)
        {
    
            System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(List<History>));
    
            using (System.Xml.XmlReader reader = xmlElement.CreateReader())
            {
    
                return (List<History>)serializer.Deserialize(reader);
    
            }
    
        }
    

    【讨论】:

      猜你喜欢
      • 2018-06-08
      • 1970-01-01
      • 2017-10-07
      • 1970-01-01
      • 2011-05-20
      • 2010-12-21
      • 1970-01-01
      • 2012-01-10
      相关资源
      最近更新 更多