首选访问SQL Server中XML列中数据的方法
-
10-10-2019 - |
题
背景
最近,我开始在SQL Server 2005中使用更多的XML作为专栏。在昨天的一些停机时间里,我注意到我确实使用了两个链接表为几个连接编写更多支持的结构代码。
为了实际生成这两个链接表的数据,我将两个XML字段传递给我的存储过程,该过程写入主要记录,将两个XML变量划分为@Tables,并将它们插入实际表中,并将其插入新的表中 SCOPE_IDENTITY()
从主记录。
经过一番,我决定完全取消这些桌子,然后将XML存储在XML字段中。现在我知道这里有一些陷阱,例如一般查询性能, GROUP BY
在XML数据上不起作用。查询通常有点混乱,但总的来说,我喜欢我现在可以使用 XElement
当我收回数据时。
另外,这些东西不会改变。这是一件镜头,所以我不必担心修改。
我想知道真正获取此数据的最佳方法。我的许多疑问都涉及根据孩子的标准甚至子童记录获得主记录。数据库中的大多数Sprocs都可以做到这一点,但是在更精细的规模上,通常需要UDF和子征服才能有效工作,但我已经敲了一个琐碎的示例来测试查询一些数据...
INSERT INTO Customers VALUES ('Tom', '', '<PhoneNumbers><PhoneNumber Type="1" Value="01234 456789" /><PhoneNumber Type="2" Value="01746 482954" /></PhoneNumbers>')
INSERT INTO Customers VALUES ('Andy', '', '<PhoneNumbers><PhoneNumber Type="2" Value="07948 598348" /></PhoneNumbers>')
INSERT INTO Customers VALUES ('Mike', '', '<PhoneNumbers><PhoneNumber Type="3" Value="02875 482945" /></PhoneNumbers>')
INSERT INTO Customers VALUES ('Steve', '', '<PhoneNumbers></PhoneNumbers>')
现在,我可以看到两种抓住它的方法。
方法1
DECLARE @PhoneType INT
SET @PhoneType = 2
SELECT ct.*
FROM Customers ct
WHERE ct.PhoneNumbers.exist('/PhoneNumbers/PhoneNumber[@Type=sql:variable("@PhoneType")]') = 1
真的吗? SQL:变量感觉有点不健康。但是,它确实有效。但是,以更有意义的方式访问数据更加困难。
方法2
SELECT ct.*, pt.PhoneType
FROM Customers ct
CROSS APPLY ct.PhoneNumbers.nodes('/PhoneNumbers/PhoneNumber') AS nums(pn)
INNER JOIN PhoneTypes pt ON pt.ID = nums.pn.value('./@Type[1]', 'int')
WHERE nums.pn.value('./@Type[1]', 'int') = @PhoneType
这是更喜欢它。我已经可以轻松地将其扩展到加入和所有其他好东西。我用过了 CROSS APPLY
在桌上有价值的功能之前,这很好。与先前查询相反的执行计划更为先进。诚然,我没有在这些桌子上做任何索引,但这是整个批次成本的97%。
方法2(扩展)
SELECT ct.ID, ct.CustomerName, ct.Notes, pt.PhoneType
FROM Customers ct
CROSS APPLY ct.PhoneNumbers.nodes('/PhoneNumbers/PhoneNumber') AS nums(pn)
INNER JOIN PhoneTypes pt ON pt.ID = nums.pn.value('./@Type[1]', 'int')
WHERE nums.pn.value('./@Type[1]', 'int') IN (SELECT ID FROM PhoneTypes)
好的 IN
子句在这里。我也可以做类似的事情 pt.PhoneType = 'Work'
最后
因此,我从本质上获得了想要的结果,但是在使用这种机制询问少量XML数据时,我应该知道什么?在精心搜索期间的性能会下降吗?这种标记样式数据的存储是否太多的开销?
边注
我用过类似的东西 sp_xml_preparedocument
和 OPENXML
过去,只是将列表传输到Sprocs,但相比之下,这就像新鲜空气!
解决方案
对于存储在XML列中的一些关键信息项目,我们采取的一种方法是按照计算的“表面”,“ parent”表上的持久属性。这是使用一个少量存储的函数完成的。
它效果很好,因为每次XML更改时仅计算一次值 - 只要没有变化,没有重新计算,该值就会像其他任何列一样存储在表上。
它也很棒,因为它可以被索引!因此,如果您正在搜索和/或在这样的领域上加入 - 就像魅力一样!
因此,您基本上需要按照此目的存储的功能:
CREATE FUNCTION [dbo].[GetPhoneNo1](@DataXML XML)
RETURNS VARCHAR(50)
WITH SCHEMABINDING
AS BEGIN
DECLARE @result VARCHAR(20)
SELECT
@result = @DataXML.value('(/PhoneNumbers/PhoneNumber[@Type="1"]/@Value)[1]', 'VARCHAR(50)')
RETURN @result
END
如果您没有类型1的电话号码,那么您只会恢复一个空。
然后,您需要使用计算的,持久的列扩展您的父表:
ALTER TABLE dbo.Customers
ADD PhoneNumberType1 AS dbo.GetPhoneNo1(PhoneNumbers)
如您所见,它对单个条目的工作正常,但是不幸的是,您无法呈现整个属性列表。但是,如果您有一些关键项目,例如ID或某些东西,您期望大多数行都拥有,那么这可能是一种非常精美而光滑的方法,可以更轻松,更有效地获取该信息。