XML 节点在现有实例中的动态插入位置使用 XML-DML
Dynamic Insertion Location of XML Node in Existing Instance Using XML-DML
如果可以在 SQL 服务器 (2012) 中的现有 xml 实例中动态插入 xml 节点,我还没有找到任何文档。我知道您可以插入或替换条件值,但如果可以根据某些条件动态完成插入位置,似乎没有任何文档。例如,假设我有这个 XML-DML 调用:
SET @xml.modify('insert <SecondaryContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></SecondaryContact>
after (/Project/PrimaryContact)[1]');
after
关键字后列出的节点是有条件的修改是否是有效语法?以下是我所指的示例:
SET @xml.modify('insert <TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (
if(count(/Project/SecondaryContact) = 0)
then (/Project/PrimaryContact)[1]
else (/Project/SecondaryContact)(1)
)');
动态选择位置以在 XML DML 语句之外包含 IF..ELSE
语句的唯一方法是,还是我的示例 XML-DML 有效?
编辑 例子XML:
<root>
...
<PrimaryContact Id="1234">
<Name>John Doe</Name>
<Phone>555-555-5555</Phone>
<Email>somewhere@test.com</Email>
</PrimaryContact>
<SecondaryContact Id="1236"> <--OPTIONAL
<Name>John Doe1</Name>
<Phone>555-555-5556</Phone>
<Email>somewhere1@test.com</Email>
</SecondaryContact>
<TechnicalContact Id="2234"> <--OPTIONAL
<Name>John Doe2</Name>
<Phone>555-555-5255</Phone>
<Email>somewhere3@test.com</Email>
</TechnicalContact>
...
</root>
我知道结构不理想。它应该是 <Contacts><Contact Type="PRIMARY" Id="1234">...</Contact>...</Contacts>
,但想看看 DML 语句中是否可以动态插入位置。对于这个问题,可以使用游标,因为它适用于一次性更新。
在您提供更多示例(请参阅我的评论)之前,我能想到的最好的方法是创建要从外部插入的节点并将其插入 as last
但是 - 可以肯定 - 有更好的方法...
DECLARE @contactName NVARCHAR(100)='TestName';
DECLARE @contactPhone NVARCHAR(100)='TestPhone';
DECLARE @contactEmail NVARCHAR(100)='TestEmail';
DECLARE @tbl TABLE(ID INT IDENTITY,Descr VARCHAR(100),XmlColumn XML);
INSERT INTO @tbl VALUES
('With secondary'
,N'<Project>
<PrimaryContact>test Primary</PrimaryContact>
<SecondaryContact>test Secondary</SecondaryContact>
</Project>')
,('Only primary'
,N'<Project>
<PrimaryContact id="prim">test Primary</PrimaryContact>
</Project>');
UPDATE @tbl SET XmlColumn.modify
(
N'insert sql:column("x.NodeToInsert") as last into (/Project)[1]'
)
FROM @tbl
CROSS APPLY
(
SELECT
(
SELECT(
SELECT @contactName AS [Name]
,@contactPhone AS [Phone]
,@contactEmail AS [Email]
WHERE XmlColumn.exist('/Project/SecondaryContact')=0
FOR XML PATH('SecondaryContact'),TYPE
) AS [node()]
,(
SELECT @contactName AS [Name]
,@contactPhone AS [Phone]
,@contactEmail AS [Email]
WHERE XmlColumn.exist('/Project/SecondaryContact')=1
FOR XML PATH('TechnicalContact'),TYPE
) AS [node()]
FOR XML PATH(''),TYPE
) AS NodeToInsert
) AS x
SELECT * FROM @tbl
更新
另一种方法是:使用 CTE 分解您的 XML,使用 .query()
获取所有未受影响的节点,使用 .value()
提取受影响的值。然后使用简单的 SELECT ... FOR XML PATH() 语句按照您需要的方式重建 XML...
这个建议不太一样,因为它会在文档中后面出现的那个之后插入,而不是在 SecondaryContact
之后,但我怀疑你的情况是一样的:
SET @xml.modify('insert
<TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (/Project/*[
local-name(.) = "SecondaryContact"
or local-name(.) = "PrimaryContact"
])[last()]
');
或者:
if @xml.value('count(/Project/SecondaryContact)', 'int') = 0
begin
SET @xml.modify('insert <TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (/Project/PrimaryContact)[1]
');
end else begin
SET @xml.modify('insert <TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (/Project/SecondaryContact)[1]
');
end
您可以按 SecondaryContact
和 PrimaryContact
的顺序 construct a sequence,并在第一次出现后添加节点。
insert
<TechnicalContact>
<Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email>
</TechnicalContact>
after (
/Project/SecondaryContact,
/Project/PrimaryContact
)[1]
如果可以在 SQL 服务器 (2012) 中的现有 xml 实例中动态插入 xml 节点,我还没有找到任何文档。我知道您可以插入或替换条件值,但如果可以根据某些条件动态完成插入位置,似乎没有任何文档。例如,假设我有这个 XML-DML 调用:
SET @xml.modify('insert <SecondaryContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></SecondaryContact>
after (/Project/PrimaryContact)[1]');
after
关键字后列出的节点是有条件的修改是否是有效语法?以下是我所指的示例:
SET @xml.modify('insert <TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (
if(count(/Project/SecondaryContact) = 0)
then (/Project/PrimaryContact)[1]
else (/Project/SecondaryContact)(1)
)');
动态选择位置以在 XML DML 语句之外包含 IF..ELSE
语句的唯一方法是,还是我的示例 XML-DML 有效?
编辑 例子XML:
<root>
...
<PrimaryContact Id="1234">
<Name>John Doe</Name>
<Phone>555-555-5555</Phone>
<Email>somewhere@test.com</Email>
</PrimaryContact>
<SecondaryContact Id="1236"> <--OPTIONAL
<Name>John Doe1</Name>
<Phone>555-555-5556</Phone>
<Email>somewhere1@test.com</Email>
</SecondaryContact>
<TechnicalContact Id="2234"> <--OPTIONAL
<Name>John Doe2</Name>
<Phone>555-555-5255</Phone>
<Email>somewhere3@test.com</Email>
</TechnicalContact>
...
</root>
我知道结构不理想。它应该是 <Contacts><Contact Type="PRIMARY" Id="1234">...</Contact>...</Contacts>
,但想看看 DML 语句中是否可以动态插入位置。对于这个问题,可以使用游标,因为它适用于一次性更新。
在您提供更多示例(请参阅我的评论)之前,我能想到的最好的方法是创建要从外部插入的节点并将其插入 as last
但是 - 可以肯定 - 有更好的方法...
DECLARE @contactName NVARCHAR(100)='TestName';
DECLARE @contactPhone NVARCHAR(100)='TestPhone';
DECLARE @contactEmail NVARCHAR(100)='TestEmail';
DECLARE @tbl TABLE(ID INT IDENTITY,Descr VARCHAR(100),XmlColumn XML);
INSERT INTO @tbl VALUES
('With secondary'
,N'<Project>
<PrimaryContact>test Primary</PrimaryContact>
<SecondaryContact>test Secondary</SecondaryContact>
</Project>')
,('Only primary'
,N'<Project>
<PrimaryContact id="prim">test Primary</PrimaryContact>
</Project>');
UPDATE @tbl SET XmlColumn.modify
(
N'insert sql:column("x.NodeToInsert") as last into (/Project)[1]'
)
FROM @tbl
CROSS APPLY
(
SELECT
(
SELECT(
SELECT @contactName AS [Name]
,@contactPhone AS [Phone]
,@contactEmail AS [Email]
WHERE XmlColumn.exist('/Project/SecondaryContact')=0
FOR XML PATH('SecondaryContact'),TYPE
) AS [node()]
,(
SELECT @contactName AS [Name]
,@contactPhone AS [Phone]
,@contactEmail AS [Email]
WHERE XmlColumn.exist('/Project/SecondaryContact')=1
FOR XML PATH('TechnicalContact'),TYPE
) AS [node()]
FOR XML PATH(''),TYPE
) AS NodeToInsert
) AS x
SELECT * FROM @tbl
更新
另一种方法是:使用 CTE 分解您的 XML,使用 .query()
获取所有未受影响的节点,使用 .value()
提取受影响的值。然后使用简单的 SELECT ... FOR XML PATH() 语句按照您需要的方式重建 XML...
这个建议不太一样,因为它会在文档中后面出现的那个之后插入,而不是在 SecondaryContact
之后,但我怀疑你的情况是一样的:
SET @xml.modify('insert
<TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (/Project/*[
local-name(.) = "SecondaryContact"
or local-name(.) = "PrimaryContact"
])[last()]
');
或者:
if @xml.value('count(/Project/SecondaryContact)', 'int') = 0
begin
SET @xml.modify('insert <TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (/Project/PrimaryContact)[1]
');
end else begin
SET @xml.modify('insert <TechnicalContact><Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email></TechnicalContact>
after (/Project/SecondaryContact)[1]
');
end
您可以按 SecondaryContact
和 PrimaryContact
的顺序 construct a sequence,并在第一次出现后添加节点。
insert
<TechnicalContact>
<Name>{ sql:variable("@contactName") }</Name>
<Phone>{ sql:variable("@contactPhone") }</Phone>
<Email>{ sql:variable("@contactEmail") }</Email>
</TechnicalContact>
after (
/Project/SecondaryContact,
/Project/PrimaryContact
)[1]