特定の要素に特定の値があるかどうかを確認するためにXMLをクエリすることは可能ですか?たとえば、以下のXMLの<ContactFName>
の値が「ブランド」であるかどうかを確認したい場合は、.
ただし、要素の場所は変更される場合があります。場合によっては、/root/MCTLocations/MCTLocation
にあるか、ルートの下にジャンプするか、別の場所に表示されることがあります...
そして、要素名をパラメータ化することは可能ですか?
DECLARE @table TABLE (XmlCol XML)
INSERT INTO @table (XmlCol) VALUES ('
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
<MCTLocation>
<Address>1234 Main Street</Address>
<ContactFName>Chris</ContactFName>
<ContactLName>Brandt</ContactLName>
</MCTLocation>
</MCTLocations>
</root>')
SELECT * FROM @table WHERE ??
このためには、XQueryが何かを検出するかどうかを示すBIT(つまりブール値)値を返すため、.exist()
XML関数を使用します。
要素の静的でない場所を処理するには、*
(特定のレベルのすべてのノードをチェックし、他のレベルはチェックしないことを示す)、または//
(それが必要であることを示す)のいずれかを使用します。そのレベル以下のすべてのノードを確認してください)。
次の例では、質問のクエリ例をベースとして使用し、いくつかのテストケースを追加して要素をさまざまなレベルに配置し、名前を変更してXQueryがすべてを選択しているわけではないことを示すテストケースを追加します。
テスト設定(1回実行)
SET NOCOUNT ON;
CREATE TABLE #Table (ID INT NOT NULL, XmlCol XML);
INSERT INTO #Table (ID, XmlCol) VALUES (1, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
<MCTLocation>
<Address>1234 Main Street</Address>
<ContactFName>Chris</ContactFName>
<ContactLName>Brandt</ContactLName>
</MCTLocation>
</MCTLocations>
</root>');
INSERT INTO #Table (ID, XmlCol) VALUES (2, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
<MCTLocation>
<Address>1234 Main Street</Address>
<ContactFName>Chris</ContactFName>
<ContactLName>Grandt</ContactLName>
</MCTLocation>
</MCTLocations>
</root>');
INSERT INTO #Table (ID, XmlCol) VALUES (3, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
<MCTLocation>
<Address>1234 Main Street</Address>
<ContactFName>Chris</ContactFName>
</MCTLocation>
</MCTLocations>
<ContactLName>Brandt</ContactLName>
</root>');
INSERT INTO #Table (ID, XmlCol) VALUES (4, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
<MCTLocation>
<Address>1234 Main Street</Address>
<ContactFName>Chris</ContactFName>
</MCTLocation>
<NewElement>
<SubElement>
<ContactLName>Brandt</ContactLName>
</SubElement>
</NewElement>
</MCTLocations>
</root>');
INSERT INTO #Table (ID, XmlCol) VALUES (5, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
<MCTLocation>
<Address>1234 Main Street</Address>
<ContactFName>Chris</ContactFName>
</MCTLocation>
<NewerElement>
<ContactLName>Brandt</ContactLName>
</NewerElement>
</MCTLocations>
</root>');
INSERT INTO #Table (ID, XmlCol) VALUES (6, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
<MCTLocation>
<Address>1234 Main Street</Address>
<ContactFName>Chris</ContactFName>
</MCTLocation>
<NewerElement>
</NewerElement>
</MCTLocations>
</root>
<ContactLName>Brandt</ContactLName>
');
テスト1(ノード名の代わりに*
)
これにより、指定したレベルのすべてのノードがチェックされます。この場合は、<root>
のすぐ下にあります。しかし、それは他のレベルをチェックしません。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'/*/ContactLName[text()="Brandt"]') = 1;
ID
値が3の行を返します。
テスト2(ノード名の代わりに*
)
これにより、指定したレベルのすべてのノードがチェックされます。この場合は、<root><MCTLocations>
のすぐ下にあります。しかし、それは他のレベルをチェックしません。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'/root/MCTLocations/*/ContactLName[text()="Brandt"]') = 1;
ID
値が1および5の行を返します。
テスト3(ノード名の代わりに//
)
これは、指定されたレベル(この場合は<root><MCTLocations>
の直下)にあるすべてのノードstartingをチェックします以下。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'/root/MCTLocations//ContactLName[text()="Brandt"]') = 1;
ID
値が1、4、および5の行を返します。
テスト4(ノード名の代わりに/*
または*/
)
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'*//ContactLName[text()="Brandt"]') = 1;
-- and:
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//*/ContactLName[text()="Brandt"]') = 1;
どちらも、ID
値が1、3、4、5の行を返します。
*
が単一ノードのプレースホルダーであるため、これらは行ID 6を返しません。したがって、許可される最高レベルは<root>
(または任意のトップレベルノード)の下になります。
テスト5(最上位の//
)
これにより、最上位のすべてのノードstartingがチェックされます。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//ContactLName[text()="Brandt"]') = 1;
ID
値が1、3、4、5、6の行を返します。
テスト6(XQueryの要素テキストにローカル変数値を使用)
DECLARE @Name NVARCHAR(50) = N'Brandt';
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//ContactLName[text()=sql:variable("@Name")]') = 1;
SET @Name = N'Grandt';
-- exact same query, just different value in the variable
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//ContactLName[text()=sql:variable("@Name")]') = 1;
最初のクエリは、ID
値が1、3、4、5、6の行を返します。
2番目のクエリは、ID
値が2の行を返します。
テスト7(XQueryの要素名に関数と文字列リテラルを使用)
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//.[local-name()="NewerElement"]') = 1;
ID
値が5および6の行を返します。
テスト8(XQueryの要素名にローカル変数値を使用して関数を使用)
DECLARE @Node NVARCHAR(50) = N'SubElement';
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//.[local-name()=sql:variable("@Node")]') = 1;
ID
値が4の行を返します。
テスト9(すべてのピースを1つにまとめる)
DECLARE @NodeName NVARCHAR(50) = N'ContactLName',
@NodeText NVARCHAR(500) = N'Brandt';
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//.[local-name()=sql:variable("@NodeName")]
[text()=sql:variable("@NodeText")]') = 1;
ID
値が1、3、4、5、6の行を返します。
一般的なXML注:
(SQL Serverの)XMLデータは、NVARCHAR
/NCHAR
と同じように、UTF-16リトルエンディアンとしてエンコードされます。したがって、値が実際にXMLである場合は、文字列リテラルの前に大文字のN
を付けるのが最善です。