2

バックグラウンド

最近、私は SQL Server 2005 の列として XML をより多く使用するようになりました。いくつかの結合をサポートする構造コードをさらに記述します。

SCOPE_IDENTITY()これら 2 つのリンク テーブルのデータを実際に生成するために、2 つの XML フィールドをストアド プロシージャに渡します。ストアド プロシージャは、メイン レコードを書き込み、2 つの XML 変数を @tables に分解し、マスターからの新しい変数とともに実際のテーブルに挿入します。記録。

しばらくして、これらのテーブルを完全に廃止し、XML を XML フィールドに格納することにしました。ここで、一般的なクエリのパフォーマンスGROUP BYが XML データでは機能しないなど、いくつかの落とし穴があることを理解しました。クエリは一般的に少しごちゃごちゃしていますが、全体的にはXElement、データが戻ってきたときに作業できるようになったことが気に入っています。

また、この内容は変更されません。ワンショットなので改造の心配はありません。

このデータを実際に取得するための最良の方法について疑問に思っています。私のクエリの多くは、子レコードまたはサブ子レコードの条件に基づいてマスター レコードを取得することを伴います。データベース内のほとんどの sproc はこれを行いますが、はるかに精巧な規模であり、通常は 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>')

今、私はそれをつかむ2つの方法を見ることができます.

方法 1

DECLARE @PhoneType INT
SET  @PhoneType = 2

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

本当に?sql:variable は少し不健全に感じます。ただし、機能します。ただし、より意味のある方法でデータにアクセスすることは明らかに困難です。

方法 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 データを調査する場合に注意すべきことはありますか? 精巧な検索中にパフォーマンスが低下しますか? また、そのようなマークアップ スタイル データの保存はオーバーヘッドが大きすぎますか?

サイドノート

私は過去にリストをsprocに渡すためだけにsp_xml_preparedocumentOPENXMLのようなものを使用しましたが、これは比較して新鮮な空気のようなものです!

4

1 に答える 1

2

XML 列内に格納された重要な情報項目の一部に対して採用したアプローチの 1 つは、それらを「親」テーブルで計算され、永続化されたプロパティとして「表面化」することです。これは、小さなストアド関数を使用して行われます。

XML が変更されるたびに値が 1 回だけ計算されるため、うまく機能します。値が変更されない限り、再計算は行われず、値は他の列と同様にテーブルに格納されます。

インデックスもできるのでお得です!したがって、そのような分野を探したり、参加したりしている場合は、魅力的です。

したがって、基本的には、次の行に沿ってストアド関数が必要です。

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 に答える