使用 SQL 服务器修改('insert')将数据附加到 xml 列
Use SQL Server modify('insert') to append data to xml column
考虑以下情况。我有以下 table
CREATE TABLE [dbo].[GoldenEgg]
(
rowIndex int NOT NULL IDENTITY(1,1),
AccountNumber varchar(256) NULL,
SubscriptionID int NOT NULL,
SubscriptionData_XML xml NULL,
SubscriptionData_AFTER_XML NULL
CONSTRAINT [PK_GoldenEgg]
PRIMARY KEY CLUSTERED ([rowIndex] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GoldenEgg样本数据:
SubscriptionData_XML
SubscriptionID 6070 的数据:
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
</Value>
</Item>
</NVPList>
我想将每个 SubscriptionID 的所有帐号附加到 SubscriptionData_XML 列中已经存在的 xml <Value>
节点,我不想添加已经存在的帐号在 xml.
所以对于 SubscriptionID 6070 帐号 39448474 应该只在 xml 中列出一次,如下所示:
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem>56936495</ValueItem>
<ValueItem>70660044</ValueItem>
<ValueItem>41447395</ValueItem>
</Value>
</Item>
</NVPList>
我能够通过 sql UPDATE
语句使用 xml modify()
方法完成此任务,而无需使用任何循环。以下是解决方案的细分:
1) 我必须获取 SubscriptionID 的所有 AccountNumbers 并将它们格式化为
进入 xml <ValueItem>
个节点。
SQL 查询 1:
SELECT
ge.SubscriptionID,
CAST((SELECT DISTINCT ValueItem = ISNULL(ge2.AccountNumber,'')
FROM dbo.GoldenEgg ge2
WHERE ge2.SubscriptionID = ge.SubscriptionID
FOR XML PATH('')) AS xml) AS AccountNumberXml
FROM dbo.GoldenEgg ge
WHERE ge.SubscriptionData_XML IS NOT NULL
SQL 查询 1 结果:
SQL 查询 1 XML 结果(订阅 ID 6070):
<ValueItem>39448474</ValueItem>
<ValueItem>41447395</ValueItem>
<ValueItem>56936495</ValueItem>
<ValueItem>70660044</ValueItem>
2) 现在我的 AccountNumbers 是单个值,我现在可以使用 xml modify()
方法并将 AccountNumberXml
值插入 <Value>
xml节点。我将使用带有 INNER JOIN
的 UPDATE
语句来执行此操作。另请注意,在执行任何操作之前,我最初将 SubscriptionData_AFTER_XML 设置为 SubscriptionData_XML。
SQL 查询 2:
UPDATE ge
SET SubscriptionData_AFTER_XML.modify
('declare default element namespace "http://www.whatevernamspace.com/v1";
insert sql:column("t1.AccountNumberXml") as last into (/NVPList/Item/Value)[1]')
FROM dbo.GoldenEgg ge
INNER JOIN (SELECT
ge2.SubscriptionID,
CAST((SELECT DISTINCT ValueItem = ISNULL(ge1.AccountNumber,'')
FROM dbo.GoldenEgg ge1
WHERE ge1.SubscriptionID = ge2.SubscriptionID
FOR XML PATH('')) AS xml) as AccountNumberXml
FROM dbo.GoldenEgg ge2
WHERE ge2.SubscriptionData_AFTER_XML IS NOT NULL) t1 ON t1.SubscriptionID = ge.SubscriptionID
WHERE ge.SubscriptionData_AFTER_XML IS NOT NULL
SQL 查询 2 结果:
SQL 查询 2 XML 结果(订阅 ID 6070 SubscriptionData_AFTER_XML 列):
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem xmlns="">39448474</ValueItem>
<ValueItem xmlns="">41447395</ValueItem>
<ValueItem xmlns="">56936495</ValueItem>
<ValueItem xmlns="">70660044</ValueItem>
</Value>
</Item>
</NVPList>
如您所见,SubscriptionData_AFTER_XML 列中的最终 xml 结果现在有两个问题。
问题 1
对于 subscriptionID 6070,AccountNumber 39448474 在 <ValueItem>
节点列表中重复出现,这是我不想要的。要解决此问题,我必须查询 xml 中的当前 AccountNumber 值,并从先前的 INNER JOIN
中排除那些 AccountNumbers
SQL 查询 3:
此查询将给我一个结果集,其中包含 SubscriptionData_XML 列中的所有当前 AccountNumbers,然后我可以使用它从 SQL QUERY 1 中排除这些 AccountNumbers结果集
SELECT SubscriptionID, t.c.value('.', 'varchar(MAX)') as CurrentValueItems
FROM dbo.GoldenEgg
CROSS APPLY SubscriptionData_XML.nodes('declare default element namespace "http://www.whatevernamspace.com/v1";
/NVPList/Item/Value/ValueItem') as t(c)
WHERE SubscriptionData_XML IS NOT NULL
SQL 查询 3 结果:
现在将它们放在一起以获得正确的最终结果
SQL 查询 4:
UPDATE ge
SET SubscriptionData_AFTER_XML.modify
('declare default element namespace "http://www.whatevernamspace.com/v1";
insert sql:column("t1.AccountNumberXml") as last into (/NVPList/Item/Value)[1]')
FROM dbo.GoldenEgg ge
INNER JOIN (SELECT
ge2.SubscriptionID,
CAST((SELECT DISTINCT ValueItem = ISNULL(ge1.AccountNumber,'')
FROM dbo.GoldenEgg ge1
--make sure we are not inserting AccountNumbers that already exists in the subscription data
WHERE ge1.AccountNumber NOT IN (SELECT t.c.value('.', 'varchar(MAX)') as CurrentValueItems
FROM dbo.GoldenEgg
CROSS APPLY SubscriptionData_XML.nodes('declare default element namespace "http://www.whatevernamspace.com/v1";
/NVPList/Item/Value/ValueItem') as t(c)
WHERE SubscriptionData_XML IS NOT NULL
AND SubscriptionID = ge2.SubscriptionID)
AND ge1.SubscriptionID = ge2.SubscriptionID
FOR XML PATH('')) AS xml) as AccountNumberXml
FROM dbo.GoldenEgg ge2
WHERE ge2.SubscriptionData_AFTER_XML IS NOT NULL) t1 ON t1.SubscriptionID = ge.SubscriptionID
WHERE ge.SubscriptionData_AFTER_XML IS NOT NULL
SQL 查询 4 XML 结果(订阅 ID 6070 SubscriptionData_AFTER_XML 列):
如您所见,AccountNumber 39448474 现在仅在 xml
中列出一次
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem xmlns="">41447395</ValueItem>
<ValueItem xmlns="">56936495</ValueItem>
<ValueItem xmlns="">70660044</ValueItem>
</Value>
</Item>
</NVPList>
问题2
插入带有 AccountNumber 节点列表时,插入的是一个空的 xmlns=""
命名空间。这是我用来删除空 xmlns=""
命名空间的查询。
SQL 查询 5:
UPDATE dbo.GoldenEgg
SET SubscriptionData_AFTER_XML = CONVERT(XML, REPLACE(CONVERT(NVARCHAR(MAX), SubscriptionData_AFTER_XML), N'xmlns=""',''))
WHERE SubscriptionData_AFTER_XML IS NOT NULL
SQL 查询 5 XML 结果(订阅 ID 6070):
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem>41447395</ValueItem>
<ValueItem>56936495</ValueItem>
<ValueItem>70660044</ValueItem>
</Value>
</Item>
</NVPList>
我希望这可以帮助任何可能需要做类似事情的人
如果您的 XML 中没有其他节点,您可以选择 FLWOR-query。
一些提示:
- 首先我创建了一个 模型 table 并用数据填充它
- 我使用 可更新的 CTE 来收集数据
- 我使用
FOR XML
-sub-select 没有命名空间 来构建 <Value>
节点,而不用担心已经存在的 ID你的实际 XML
- 我使用
FLWOR-query()
从刚刚创建的值节点 中构建完整的 XML
- 由于这个 CTE 是可更新的,我可以直接将它用于
UPDATE
- 最后的
SELECT * FROM @tbl
向您展示,所有 AFTER_XML
都已填充
试试这个:
DECLARE @tbl TABLE(rowIndex INT IDENTITY,AccountNumber INT,SubscriptionID INT, SubscriptionData_XML XML,SubscriptionData_AFTER_XML XML);
INSERT INTO @tbl VALUES
(1111,6070,N'<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
</Value>
</Item>
</NVPList>',NULL)
,(2222,6070,NULL,NULL)
,(3333,6070,NULL,NULL)
,(4444,6070,NULL,NULL)
,(5555,6071,N'<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
</Value>
</Item>
</NVPList>',NULL)
,(6666,6071,NULL,NULL)
,(7777,6071,NULL,NULL)
,(8888,6071,NULL,NULL);
--此处启动可更新的CTE
WITH UpdateableCTE AS
(
SELECT t1.rowIndex
,t1.SubscriptionData_AFTER_XML
,(
SELECT t2.AccountNumber AS ValueItem
FROM @tbl AS t2
WHERE t2.SubscriptionID=t1.SubscriptionID
FOR XML PATH(''),ROOT('Value'),TYPE
).query
(N'declare default element namespace "http://www.whatevernamspace.com/v1";
let $nd:=/*:Value
return
<NVPList>
<Item>
<Name>{sql:column("XmlName")}</Name>
<Value>
{
for $vi in $nd/*:ValueItem
return <ValueItem>{$vi/text()}</ValueItem>
}
</Value>
</Item>
</NVPList>
'
) AS NewXML
FROM @tbl AS t1
CROSS APPLY( SELECT t1.SubscriptionData_XML.value('(//*:Name)[1]','nvarchar(max)') AS XmlName) AS x
WHERE SubscriptionData_XML IS NOT NULL
)
--更新语句
UPDATE UpdateableCTE SET SubscriptionData_AFTER_XML=NewXML
FROM UpdateableCTE;
--SELECT检查成功
SELECT * FROM @tbl
考虑以下情况。我有以下 table
CREATE TABLE [dbo].[GoldenEgg]
(
rowIndex int NOT NULL IDENTITY(1,1),
AccountNumber varchar(256) NULL,
SubscriptionID int NOT NULL,
SubscriptionData_XML xml NULL,
SubscriptionData_AFTER_XML NULL
CONSTRAINT [PK_GoldenEgg]
PRIMARY KEY CLUSTERED ([rowIndex] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GoldenEgg样本数据:
SubscriptionData_XML
SubscriptionID 6070 的数据:
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
</Value>
</Item>
</NVPList>
我想将每个 SubscriptionID 的所有帐号附加到 SubscriptionData_XML 列中已经存在的 xml <Value>
节点,我不想添加已经存在的帐号在 xml.
所以对于 SubscriptionID 6070 帐号 39448474 应该只在 xml 中列出一次,如下所示:
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem>56936495</ValueItem>
<ValueItem>70660044</ValueItem>
<ValueItem>41447395</ValueItem>
</Value>
</Item>
</NVPList>
我能够通过 sql UPDATE
语句使用 xml modify()
方法完成此任务,而无需使用任何循环。以下是解决方案的细分:
1) 我必须获取 SubscriptionID 的所有 AccountNumbers 并将它们格式化为
进入 xml <ValueItem>
个节点。
SQL 查询 1:
SELECT
ge.SubscriptionID,
CAST((SELECT DISTINCT ValueItem = ISNULL(ge2.AccountNumber,'')
FROM dbo.GoldenEgg ge2
WHERE ge2.SubscriptionID = ge.SubscriptionID
FOR XML PATH('')) AS xml) AS AccountNumberXml
FROM dbo.GoldenEgg ge
WHERE ge.SubscriptionData_XML IS NOT NULL
SQL 查询 1 结果:
SQL 查询 1 XML 结果(订阅 ID 6070):
<ValueItem>39448474</ValueItem>
<ValueItem>41447395</ValueItem>
<ValueItem>56936495</ValueItem>
<ValueItem>70660044</ValueItem>
2) 现在我的 AccountNumbers 是单个值,我现在可以使用 xml modify()
方法并将 AccountNumberXml
值插入 <Value>
xml节点。我将使用带有 INNER JOIN
的 UPDATE
语句来执行此操作。另请注意,在执行任何操作之前,我最初将 SubscriptionData_AFTER_XML 设置为 SubscriptionData_XML。
SQL 查询 2:
UPDATE ge
SET SubscriptionData_AFTER_XML.modify
('declare default element namespace "http://www.whatevernamspace.com/v1";
insert sql:column("t1.AccountNumberXml") as last into (/NVPList/Item/Value)[1]')
FROM dbo.GoldenEgg ge
INNER JOIN (SELECT
ge2.SubscriptionID,
CAST((SELECT DISTINCT ValueItem = ISNULL(ge1.AccountNumber,'')
FROM dbo.GoldenEgg ge1
WHERE ge1.SubscriptionID = ge2.SubscriptionID
FOR XML PATH('')) AS xml) as AccountNumberXml
FROM dbo.GoldenEgg ge2
WHERE ge2.SubscriptionData_AFTER_XML IS NOT NULL) t1 ON t1.SubscriptionID = ge.SubscriptionID
WHERE ge.SubscriptionData_AFTER_XML IS NOT NULL
SQL 查询 2 结果:
SQL 查询 2 XML 结果(订阅 ID 6070 SubscriptionData_AFTER_XML 列):
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem xmlns="">39448474</ValueItem>
<ValueItem xmlns="">41447395</ValueItem>
<ValueItem xmlns="">56936495</ValueItem>
<ValueItem xmlns="">70660044</ValueItem>
</Value>
</Item>
</NVPList>
如您所见,SubscriptionData_AFTER_XML 列中的最终 xml 结果现在有两个问题。
问题 1
对于 subscriptionID 6070,AccountNumber 39448474 在 <ValueItem>
节点列表中重复出现,这是我不想要的。要解决此问题,我必须查询 xml 中的当前 AccountNumber 值,并从先前的 INNER JOIN
SQL 查询 3:
此查询将给我一个结果集,其中包含 SubscriptionData_XML 列中的所有当前 AccountNumbers,然后我可以使用它从 SQL QUERY 1 中排除这些 AccountNumbers结果集
SELECT SubscriptionID, t.c.value('.', 'varchar(MAX)') as CurrentValueItems
FROM dbo.GoldenEgg
CROSS APPLY SubscriptionData_XML.nodes('declare default element namespace "http://www.whatevernamspace.com/v1";
/NVPList/Item/Value/ValueItem') as t(c)
WHERE SubscriptionData_XML IS NOT NULL
SQL 查询 3 结果:
现在将它们放在一起以获得正确的最终结果
SQL 查询 4:
UPDATE ge
SET SubscriptionData_AFTER_XML.modify
('declare default element namespace "http://www.whatevernamspace.com/v1";
insert sql:column("t1.AccountNumberXml") as last into (/NVPList/Item/Value)[1]')
FROM dbo.GoldenEgg ge
INNER JOIN (SELECT
ge2.SubscriptionID,
CAST((SELECT DISTINCT ValueItem = ISNULL(ge1.AccountNumber,'')
FROM dbo.GoldenEgg ge1
--make sure we are not inserting AccountNumbers that already exists in the subscription data
WHERE ge1.AccountNumber NOT IN (SELECT t.c.value('.', 'varchar(MAX)') as CurrentValueItems
FROM dbo.GoldenEgg
CROSS APPLY SubscriptionData_XML.nodes('declare default element namespace "http://www.whatevernamspace.com/v1";
/NVPList/Item/Value/ValueItem') as t(c)
WHERE SubscriptionData_XML IS NOT NULL
AND SubscriptionID = ge2.SubscriptionID)
AND ge1.SubscriptionID = ge2.SubscriptionID
FOR XML PATH('')) AS xml) as AccountNumberXml
FROM dbo.GoldenEgg ge2
WHERE ge2.SubscriptionData_AFTER_XML IS NOT NULL) t1 ON t1.SubscriptionID = ge.SubscriptionID
WHERE ge.SubscriptionData_AFTER_XML IS NOT NULL
SQL 查询 4 XML 结果(订阅 ID 6070 SubscriptionData_AFTER_XML 列):
如您所见,AccountNumber 39448474 现在仅在 xml
中列出一次<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem xmlns="">41447395</ValueItem>
<ValueItem xmlns="">56936495</ValueItem>
<ValueItem xmlns="">70660044</ValueItem>
</Value>
</Item>
</NVPList>
问题2
插入带有 AccountNumber 节点列表时,插入的是一个空的 xmlns=""
命名空间。这是我用来删除空 xmlns=""
命名空间的查询。
SQL 查询 5:
UPDATE dbo.GoldenEgg
SET SubscriptionData_AFTER_XML = CONVERT(XML, REPLACE(CONVERT(NVARCHAR(MAX), SubscriptionData_AFTER_XML), N'xmlns=""',''))
WHERE SubscriptionData_AFTER_XML IS NOT NULL
SQL 查询 5 XML 结果(订阅 ID 6070):
<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
<ValueItem>41447395</ValueItem>
<ValueItem>56936495</ValueItem>
<ValueItem>70660044</ValueItem>
</Value>
</Item>
</NVPList>
我希望这可以帮助任何可能需要做类似事情的人
如果您的 XML 中没有其他节点,您可以选择 FLWOR-query。
一些提示:
- 首先我创建了一个 模型 table 并用数据填充它
- 我使用 可更新的 CTE 来收集数据
- 我使用
FOR XML
-sub-select 没有命名空间 来构建<Value>
节点,而不用担心已经存在的 ID你的实际 XML - 我使用
FLWOR-query()
从刚刚创建的值节点 中构建完整的 XML
- 由于这个 CTE 是可更新的,我可以直接将它用于
UPDATE
- 最后的
SELECT * FROM @tbl
向您展示,所有AFTER_XML
都已填充
试试这个:
DECLARE @tbl TABLE(rowIndex INT IDENTITY,AccountNumber INT,SubscriptionID INT, SubscriptionData_XML XML,SubscriptionData_AFTER_XML XML);
INSERT INTO @tbl VALUES
(1111,6070,N'<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
</Value>
</Item>
</NVPList>',NULL)
,(2222,6070,NULL,NULL)
,(3333,6070,NULL,NULL)
,(4444,6070,NULL,NULL)
,(5555,6071,N'<NVPList xmlns="http://www.whatevernamspace.com/v1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Item>
<Name>AccountNumbers</Name>
<Value>
<ValueItem>39448474</ValueItem>
</Value>
</Item>
</NVPList>',NULL)
,(6666,6071,NULL,NULL)
,(7777,6071,NULL,NULL)
,(8888,6071,NULL,NULL);
--此处启动可更新的CTE
WITH UpdateableCTE AS
(
SELECT t1.rowIndex
,t1.SubscriptionData_AFTER_XML
,(
SELECT t2.AccountNumber AS ValueItem
FROM @tbl AS t2
WHERE t2.SubscriptionID=t1.SubscriptionID
FOR XML PATH(''),ROOT('Value'),TYPE
).query
(N'declare default element namespace "http://www.whatevernamspace.com/v1";
let $nd:=/*:Value
return
<NVPList>
<Item>
<Name>{sql:column("XmlName")}</Name>
<Value>
{
for $vi in $nd/*:ValueItem
return <ValueItem>{$vi/text()}</ValueItem>
}
</Value>
</Item>
</NVPList>
'
) AS NewXML
FROM @tbl AS t1
CROSS APPLY( SELECT t1.SubscriptionData_XML.value('(//*:Name)[1]','nvarchar(max)') AS XmlName) AS x
WHERE SubscriptionData_XML IS NOT NULL
)
--更新语句
UPDATE UpdateableCTE SET SubscriptionData_AFTER_XML=NewXML
FROM UpdateableCTE;
--SELECT检查成功
SELECT * FROM @tbl