Joh*_*sey 8 xml sql-server t-sql
是否可以查询 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 ??
Run Code Online (Sandbox Code Playgroud)
Sol*_*zky 15
为此,您希望使用.exist()
XML 函数,因为它将返回一个 BIT(即布尔值)值,指示 XQuery 是否找到任何内容。
要处理元素的非静态位置,您可以使用*
(表示它应该检查特定级别的所有节点,而不是其他级别)或//
(表示它应该检查该级别及以下级别的所有节点)。
以下示例使用问题中的示例查询作为基础,并添加了一些测试用例以将元素放置在不同级别,并添加一个测试用例来更改名称以表明 XQuery 不只是选择所有内容。
测试设置(运行一次)
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>
');
Run Code Online (Sandbox Code Playgroud)
测试 1(*
代替节点名称)
这将检查指定级别的所有节点,在这种情况下,它位于<root>
. 但它不会检查其他级别。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'/*/ContactLName[text()="Brandt"]') = 1;
Run Code Online (Sandbox Code Playgroud)
返回ID
值为 3 的行。
测试 2(*
代替节点名称)
这将检查指定级别的所有节点,在这种情况下,它位于<root><MCTLocations>
. 但它不会检查其他级别。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'/root/MCTLocations/*/ContactLName[text()="Brandt"]') = 1;
Run Code Online (Sandbox Code Playgroud)
返回ID
值为 1 和 5 的行。
测试 3(//
代替节点名称)
这将检查从指定级别开始的所有节点,在这种情况下<root><MCTLocations>
,该级别刚好低于和低于。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'/root/MCTLocations//ContactLName[text()="Brandt"]') = 1;
Run Code Online (Sandbox Code Playgroud)
返回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;
Run Code Online (Sandbox Code Playgroud)
两者都返回ID
值为1、3、4和 5 的行。
由于*
作为单个节点的占位符,这些不会返回行 ID 6 ,因此允许的最高级别将低于<root>
(或任何顶级节点)。
测试 5(//
在顶层)
这将检查从顶层开始的所有节点。
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//ContactLName[text()="Brandt"]') = 1;
Run Code Online (Sandbox Code Playgroud)
返回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;
Run Code Online (Sandbox Code Playgroud)
第一个查询返回ID
值为1、3、4、5和 6 的行。
第二个查询返回ID
值为 2 的行。
测试 7(在 XQuery 中使用函数和字符串字面量作为元素名称)
SELECT *
FROM #Table tmp
WHERE tmp.[XmlCol].exist(N'//.[local-name()="NewerElement"]') = 1;
Run Code Online (Sandbox Code Playgroud)
返回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;
Run Code Online (Sandbox Code Playgroud)
返回ID
值为 4 的行。
测试 9(将所有部分放在一起)
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;
Run Code Online (Sandbox Code Playgroud)
返回ID
值为1、3、4、5和 6 的行。
一般 XML 注意:
XML 数据(在 SQL Server 中)被编码为 UTF-16 Little Endian,与NVARCHAR
/相同NCHAR
。因此,N
当值实际上是 XML 时,最好用大写字母作为 sting 文字的前缀。