SQL Server 2008 R2と、非常に大きなテーブルのいくつかのXML列を使用しています。 SQL Serverのバージョンが2012未満であるため、 Selective XML Indexes を使用できないことを理解しています。XML列にインデックスを付けるという考えにまったく新しいことをしています。
XMLにクエリを実行する必要があるシナリオが2つあります。
columnName.exist('Parent[1]/Child[1]/DifferentChild[1]') = 1'
SELECT columnName.value('Parent[1]/FilePath[1]', 'nvarchar(max)') as FilePath, columnName.value('Parent[1]/FileName[1]', 'nvarchar(255)') as FileName
SQL ServerでXMLクエリを調整する方法はいくつかあります。プロパティのプロモーションは良いものですが、私は次のものも定期的に使用しています。
XMLインデックスはXMLクエリのパフォーマンスを変換できますが、コストがかかります。 SQL Server 2012より前のバージョンには、プライマリXMLインデックスとセカンダリXMLインデックスの2つのタイプがあります。常にプライマリXMLインデックスが必要であり、オプションでPATH、PROPERTY、またはVALUEインデックスを追加して、目的が少し異なるようにすることができます。特定のクエリの場合、セカンダリPATHインデックスを使用すると、以下の簡単なリグでステップ変更のパフォーマンスが向上します。例:
CREATE PRIMARY XML INDEX xmlidx_largeTable ON dbo.largeTable ( yourXML )
GO
CREATE XML INDEX xpthidx_largeTable ON dbo.largeTable ( yourXML )
USING XML INDEX xmlidx_largeTable FOR PATH
GO
今度はコストです。 XMLインデックス(選択的XMLインデックスの前)は、ストレージに大きな影響を与えます。テーブルのサイズが最大5倍になるのを見てきました。以下の私のテストリグでは、300万行と非常に単純なXMLを持つテーブルは、プライマリXMLインデックスで0.7GBから2GBになり、次にPATHセカンダリインデックスで2.7GBになります。 SQL Server 2012以降の選択的XMLインデックスは、これを大幅に改善できます。
左から右にドリルダウンするXMLのレベルが複数ある場合は、 CROSS APPLY を使用します。以下の私のリグでCROSS APPLYの使用を参照してください。また、親軸(..)を使用してドリルアップしないでください。これは、特に ここ に示すように、XMLのより大きな部分でパフォーマンスの問題を引き起こす可能性があります。
また、型なしのXMLでは常にtext()アクセサーを使用します。たとえば、
SELECT
p.c.value('(FilePath/text())[1]', 'nvarchar(max)') AS FilePath,
p.c.value('(FileName/text())[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
CROSS APPLY t.yourXML.nodes('Parent') p(c)
これは ここで言及されており 、この手法によりパフォーマンスが最大15%向上することがわかりました。 YMMV。序数([1]
)を式の最後に移動する方が効率的で、構文的にはParent[1]/FilePath[1]/SomeOtherElement[1]
と同等です。
これらはパフォーマンスの向上をもたらす傾向はありませんが、制約のようにXMLに特定の構造を強制するので、良い習慣です。
フルテキストインデックスとXMLを組み合わせて、良い結果が得られることもあります(例: here )。基準がないように思われるため、この例ではおそらく適切ではありません。
私の単純なテストリグでは、300万行の単純なテーブルを作成し、各行に単純なXMLを入れています。次に、構文とXMLインデックスのさまざまな組み合わせを試して、違いを確認します。
USE tempdb
GO
SET NOCOUNT ON
GO
------------------------------------------------------------------------------------------------
-- Setup START
------------------------------------------------------------------------------------------------
-- Create a large table
IF OBJECT_ID('dbo.largeTable') IS NOT NULL DROP TABLE dbo.largeTable
CREATE TABLE dbo.largeTable (
rowId INT IDENTITY PRIMARY KEY,
someData UNIQUEIDENTIFIER DEFAULT NEWID(),
dateAdded DATETIME DEFAULT GETDATE(),
addedBy VARCHAR(30) DEFAULT SUSER_NAME(),
yourXML XML,
ts ROWVERSION
)
GO
-- Add 3 million rows to the table; with simple piece of XML in each row
;WITH cte AS (
SELECT TOP 1000000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
CROSS JOIN master.sys.columns c2
CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.largeTable ( someData, yourXML )
SELECT NEWID(),
(
SELECT
rn AS Child,
'DifferentChild' + CAST( CASE WHEN rn % 9999 = 0 THEN rn % 33 ELSE NULL END AS VARCHAR(10) ) AS "Child/DifferentChild",
'FilePath' + CAST( rn AS VARCHAR(10) ) AS FilePath,
'FileName' + CAST( rn AS VARCHAR(10) ) AS [FileName]
FOR XML PATH('Parent'), TYPE
)
FROM cte
GO 3
-- Setup END
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
-- Queries START
------------------------------------------------------------------------------------------------
-- Query 1:
SELECT *
FROM dbo.largeTable t
WHERE t.yourXML.exist('Parent[1]/Child[1]/DifferentChild[1]') = 1
-- Improve query
SELECT *
FROM dbo.largeTable t
WHERE t.yourXML.exist('(Parent/Child/DifferentChild)[1]') = 1
GO
-- Query 2:
SELECT
yourXML.value('Parent[1]/FilePath[1]', 'nvarchar(max)') AS FilePath,
yourXML.value('Parent[1]/FileName[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
SELECT
yourXML.value('(Parent/FilePath/text())[1]', 'nvarchar(max)') AS FilePath,
yourXML.value('(Parent/FileName/text())[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
SELECT
p.c.value('(FilePath/text())[1]', 'nvarchar(max)') AS FilePath,
p.c.value('(FileName/text())[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
CROSS APPLY t.yourXML.nodes('Parent') p(c)
GO
-- Queries END
------------------------------------------------------------------------------------------------
CREATE PRIMARY XML INDEX xmlidx_largeTable ON dbo.largeTable ( yourXML )
GO
------------------------------------------------------------------------------------------------
-- Queries START
------------------------------------------------------------------------------------------------
-- Query 1:
SELECT *
FROM dbo.largeTable t
WHERE t.yourXML.exist('Parent[1]/Child[1]/DifferentChild[1]') = 1
-- Improve query
SELECT *
FROM dbo.largeTable t
WHERE t.yourXML.exist('(Parent/Child/DifferentChild)[1]') = 1
GO
-- Query 2:
SELECT
yourXML.value('Parent[1]/FilePath[1]', 'nvarchar(max)') AS FilePath,
yourXML.value('Parent[1]/FileName[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
SELECT
yourXML.value('(Parent/FilePath/text())[1]', 'nvarchar(max)') AS FilePath,
yourXML.value('(Parent/FileName/text())[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
SELECT
p.c.value('(FilePath/text())[1]', 'nvarchar(max)') AS FilePath,
p.c.value('(FileName/text())[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
CROSS APPLY t.yourXML.nodes('Parent') p(c)
GO
-- Queries END
------------------------------------------------------------------------------------------------
CREATE XML INDEX xpthidx_largeTable ON dbo.largeTable ( yourXML )
USING XML INDEX xmlidx_largeTable FOR PATH
GO
------------------------------------------------------------------------------------------------
-- Queries START
------------------------------------------------------------------------------------------------
-- Query 1:
SELECT *
FROM dbo.largeTable t
WHERE t.yourXML.exist('Parent[1]/Child[1]/DifferentChild[1]') = 1
-- Improve query
SELECT *
FROM dbo.largeTable t
WHERE t.yourXML.exist('(Parent/Child/DifferentChild)[1]') = 1
GO
-- Query 2:
SELECT
yourXML.value('Parent[1]/FilePath[1]', 'nvarchar(max)') AS FilePath,
yourXML.value('Parent[1]/FileName[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
SELECT
yourXML.value('(Parent/FilePath/text())[1]', 'nvarchar(max)') AS FilePath,
yourXML.value('(Parent/FileName/text())[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
SELECT
p.c.value('(FilePath/text())[1]', 'nvarchar(max)') AS FilePath,
p.c.value('(FileName/text())[1]', 'nvarchar(255)') AS [FileName]
FROM dbo.largeTable t
CROSS APPLY t.yourXML.nodes('Parent') p(c)
GO
-- Queries END
------------------------------------------------------------------------------------------------
私の結果:
つまり、要約すると、XMLクエリを適切な機能と組み合わせて使用することで段階的なパフォーマンスを得ることができますが、ストレージコストは非常に高くなります。
SQL Server 2005のXMLデータ型のパフォーマンスの最適化
http://msdn.Microsoft.com/en-us/library/ms345118.aspx
SQL Server 2005のXMLインデックス
http://msdn.Microsoft.com/en-us/library/ms345121(SQL.90).aspx
Microsoft SQL Server 2005のXMLベストプラクティス のプロパティプロモーションを確認する必要があります。
必要な値を抽出するユーザー定義のスカラー値関数を作成し、その関数をテーブルの計算列として使用します。好きな列を永続化したり、列に通常のインデックスを作成したりできます。インデックスを作成するために列を永続化する必要はありません。