Reputation: 47
I got an XML like this:
<root>
<child1>
<...../>
<...../>
<...../>
<child2>
<child3>Value1</child3>
<child3>Value2</child3>
<child3>Value3</child3>
</child2>
</child1>
</root>
I've been searching for a few days but can't quite find an appropriate answer to this problem I've encountered.
Essentially, I want to delete the node child1
from the root
and all its children based on a value
(In this case, Value1
) from child3
, but there may be multiple child3
nodes and I want to preserve child3
if it has values I want (In this case, it could be Value2
or Value3
), even if it has a value (Value1
) I'm trying to delete.
Currently, this is what I am using.
set @xml.modify('delete root/child1[child2[child3 = "Value1"]]')
What I want to do is
set @xml.modify('delete root/child1[child2[child3 = "Value1"] AND !(child2[child3 = "Value2"] OR child2[child3 = "Value3"])]')
So essentially, Don't delete nodes that have the data in the child that I want, even if it has a specific value I do not want, and then delete the nodes I don't want in every other case.
There are other ways I can do this, by querying an ID
from the root
node and doing a ton of prework, but I'd like to make the code as consolidated as possible or otherwise have more intelligent code than that. Any thoughts?
Upvotes: 2
Views: 1492
Reputation: 67321
I do not know, if I really got this correctly, but you might solve this with a FLWOR-query()
-cascade:
DECLARE @xml xml =
N'<root>
<child1 id="1" type="Is to be deleted completely">
<child2>
<child3>Value1</child3>
</child2>
<child2>
<child3>Value1</child3>
</child2>
</child1>
<child1 id="2" type="Should remain but those with the search value should be deleted">
<child2>
<child3>Value1</child3>
<child3>Other a</child3>
<child3>Other c</child3>
</child2>
</child1>
<child1 id="3" type="First child2 delete, second just delete the one with delete value, third untouched">
<child2>
<child3>Value1</child3>
</child2>
<child2>
<child3>Value1</child3>
<child3>Other a</child3>
<child3>Other c</child3>
</child2>
<child2>
<child3>Other a</child3>
<child3>Other c</child3>
</child2>
</child1>
<child1 id="4" type="Untouched, no search value">
<child2>
<child3>Other m</child3>
</child2>
<child2>
<child3>Other n</child3>
</child2>
</child1>
</root>';
DECLARE @DeleteValue NVARCHAR(100)='Value1';
the first FLWOR-query() will rebuild the XML without the nodes carrying the search value
the second FLWOR-query() will remove all child2 elements with no content
the third FLWOR-query() will remove all child1 elements with no content
This is the query:
SELECT @xml.query
(
N'
<root>
{
for $c1 in /root/child1
return
<child1> {$c1/@*}
{
for $c2 in $c1/child2
return
<child2> {$c2/@*}
{
for $c3 in $c2/child3[text()!=sql:variable("@DeleteValue")]
return $c3
}
</child2>
}
</child1>
}
</root>'
).query
(
N'
<root>
{
for $c1 in /root/child1
return
<child1> {$c1/@*}
{
for $c2 in $c1/child2[string-length(.)!=0]
return $c2
}
</child1>
}
</root>'
).query
(
N'
<root>
{
for $c1 in /root/child1[string-length(.)!=0]
return $c1
}
</root>'
);
This is the result
<root>
<child1 id="2" type="Should remain but those with the search value should be deleted">
<child2>
<child3>Other a</child3>
<child3>Other c</child3>
</child2>
</child1>
<child1 id="3" type="First child2 delete, second just delete the one with delete value, third untouched">
<child2>
<child3>Other a</child3>
<child3>Other c</child3>
</child2>
<child2>
<child3>Other a</child3>
<child3>Other c</child3>
</child2>
</child1>
<child1 id="4" type="Untouched, no search value">
<child2>
<child3>Other m</child3>
</child2>
<child2>
<child3>Other n</child3>
</child2>
</child1>
</root>
Upvotes: 0
Reputation: 15997
You can use XML.modify() method with contains function (not(contains..)
):
DECLARE @x xml = N'<root>
<child1>
<child2>
<child3>Value1</child3>
<child3>Value2</child3>
<child3>Value3</child3>
</child2>
</child1>
<child1>
<child2>
<child3>Value1</child3>
<child3>Value4</child3>
<child3>Value5</child3>
</child2>
</child1>
<child1>
<child2>
<child3>Value5</child3>
<child3>Value2</child3>
<child3>Value6</child3>
</child2>
</child1>
</root>'
SET @x.modify('delete /root/child1[contains(.,"Value1") and (not(contains(.,"Value2")) or not(contains(.,"Value3")))]')
SELECT @x
Output will be:
<root>
<child1>
<child2>
<child3>Value1</child3>
<child3>Value2</child3>
<child3>Value3</child3>
</child2>
</child1>
<child1>
<child2>
<child3>Value5</child3>
<child3>Value2</child3>
<child3>Value6</child3>
</child2>
</child1>
</root>
Upvotes: 3