2

背景

最近,我开始更多地使用 XML 作为 SQL Server 2005 中的列。在昨天的一段时间停机期间,我注意到我使用的两个链接表真的很碍事,不得不让我无聊到流泪为几个连接编写更多支持结构代码。

为了实际为这两个链接表生成数据,我将两个 XML 字段传递给我的存储过程,它写入主记录,将两个 XML 变量分解为 @tables 并将它们插入到实际表SCOPE_IDENTITY()中记录。

不过,经过一段时间后,我决定完全取消这些表,并将 XML 存储在 XML 字段中。现在我知道这里有一些缺陷,比如一般查询性能,GROUP BY不适用于 XML 数据。查询通常有点混乱,但总的来说,我喜欢XElement当我取回数据时现在可以使用它。

而且,这些东西不会改变。这是一次性事件,所以我不必担心修改。

我想知道实际获取这些数据的最佳方法。我的很多查询都涉及根据子记录甚至子记录的标准获取主记录。数据库中的大多数存储过程都这样做,但规模要复杂得多,通常需要 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>')

现在我可以看到两种抓取方式。

方法一

DECLARE @PhoneType INT
SET  @PhoneType = 2

SELECT ct.*
FROM Customers ct
WHERE ct.PhoneNumbers.exist('/PhoneNumbers/PhoneNumber[@Type=sql:variable("@PhoneType")]') = 1

真的吗?sql:variable 感觉有点不健康。但是,它确实有效。然而,以更有意义的方式访问数据显然更加困难。

方法二

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_preparedocumentOPENXML只是将列表传递给存储过程,但相比之下,这就像呼吸新鲜空气!

4

1 回答 1

2

我们对存储在 XML 列中的一些关键信息项采取的一种方法是将它们“显示”为“父”表上的计算、持久属性。这是使用一个小存储函数完成的。

它工作得很好,因为每次 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 的电话号码,你只会得到一个 NULL。

然后,您需要使用计算的持久列来扩展父表:

ALTER TABLE dbo.Customers
   ADD PhoneNumberType1 AS dbo.GetPhoneNo1(PhoneNumbers)

如您所见 - 它适用于单个条目,但不幸的是,您无法显示整个属性列表。但是,如果您有一些关键项目,如 ID 或其他东西,您希望大多数行都具有,那么这可能是一种更轻松、更有效地获取这些信息的非常好的和巧妙的方法。

于 2010-12-09T12:06:58.013 回答