您的位置:首页 > 产品设计 > UI/UE

sqlserver xml.modify('replace value of .... with....') 函数改造

2017-12-23 14:03 816 查看
  背景: sqlserver 对xml 节点操作会使用xml.modify(‘replace value of …. with….’)函数,但是在使用的过程中发现,该modify函数不支持对空值简写节点的修改. 具体如下

<person>
<name>小明</name>
<addr></addr>
<org/>
</person>


修改
<name>
节点和
<addr>
节点的值可以成功, 但是修改
<org/>
节点的值不能成功. modify不支持对空值简写节点的修改.

  

  思路: 自定义一个带有三个参数的函数,(@xml,@node,@value), 函数中先插入一个节点,然后把原来的节点删除掉.最终把@xml 返回即可. 因为参数都是未知的,所以必然会涉及到执行动态sql (EXECUTE sp_executesql…..). 理想是美好的,现实是残酷的. 在function 中并不支持动态sql的执行, 含有动态sql的function 虽然编译能通过,但执行会报错(开始处理的时候掉坑里面去了.). 上帝关闭一扇门,就会再打开一扇门. 动态sql可以在procedure 中执行,因此自定义一个存储过程即可

  

  代码如下

IF EXISTS (SELECT 1 FROM sysobjects t WHERE t.name = 'proc_replace_node_value')
BEGIN
DROP PROCEDURE proc_replace_node_value;
END;

SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[proc_replace_node_value]
@xml XML OUTPUT,--xml 对象
@node NVARCHAR(1000),--节点表达式
@value NVARCHAR(MAX)--value
AS
BEGIN
---思路:先在原来的节点后面增加带@value节点,然后删除原来的节点
DECLARE @temp NVARCHAR(1000);--临时变量
DECLARE @nodePath NVARCHAR(1000);--节点路径
DECLARE @nodeShort NVARCHAR(100);--节点名称
DECLARE @replaceSql NVARCHAR(MAX);--替换语句
DECLARE @insertSql NVARCHAR(MAX);--插入节点语句
DECLARE @deleteSql NVARCHAR(MAX);--删除语句
--格式如下(*text())[1]
IF CHARINDEX('text())[1]',@node)>0 AND CHARINDEX('(',@node)=1
BEGIN
--值为NULL,那么就置为''
IF @value IS NULL
BEGIN
SET @value = '';
END
SELECT @nodePath = SUBSTRING(@node,2,CHARINDEX('text()',@node)-3);
--需要操作的节点必须存在,否则不处理
IF @xml.exist('sql:variable("@nodePath")')=1
BEGIN
IF LEN(@value)=0
BEGIN
--如果value 为空, 直接使用replace. 如果原来的值为空,replace什么也没操作,如果原来有值,就刷成了空
SET @replaceSql = 'SET @xml.modify(''replace value of '+@node +' with "'+@value+'"'''+');';
EXECUTE sp_executesql @replaceSql,N'@xml xml OUTPUT',@xml OUTPUT;
END
ELSE
BEGIN
--找到节点名称
SELECT @nodeShort =  STUFF(@nodePath,1,LEN(@nodePath)-CHARINDEX('/',REVERSE(@nodePath))+1,'');
--拼装路径
SET @nodePath = '('+@nodePath+')[1]';

--如果value中含有单引号必须处理,否则凭借字符串会报错.
SET @value = REPLACE(@value,'''','''''');
SET @temp = '<'+@nodeShort+'>'+@value + '</'+@nodeShort+'>';

--在原有节点后面加一个节点
SET @insertSql= 'SET @xml.modify(''insert '+@temp + ' after '+@nodePath +''')';
EXECUTE sp_executesql @insertSql,N'@xml xml OUTPUT',@xml OUTPUT;

--删除原有节点
SET @deleteSql = 'SET @xml.modify(''delete '+@nodePath+''')';
EXECUTE sp_executesql @deleteSql,N'@xml xml OUTPUT',@xml OUTPUT;
END
END;
END;
END;
GO


测试结果. (注:下面的脚本好有一个自定义函数FN_GETXML() 点击获取该函数(文章开头处)

DECLARE @xml XML;
SET @xml =  dbo.FN_GETXML();
SELECT @xml.query('data/budget_list[1]/PrpManpowers/PrpManpower[1]/ename');--原始值为aaa

EXECUTE proc_replace_node_value @xml OUTPUT,'(data/budget_list[1]/PrpManpowers/PrpManpower[1]/ename/text())[1]','88''8';
SELECT @xml.query('data/budget_list[1]/PrpManpowers/PrpManpower[1]/ename');--第一次把aaa 刷成了88'8

EXECUTE proc_replace_node_value @xml OUTPUT,'(data/budget_list[1]/PrpManpowers/PrpManpower[1]/ename/text())[1]','';
SELECT @xml.query('data/budget_list[1]/PrpManpowers/PrpManpower[1]/ename');--第二次把88'8 刷成了空

EXECUTE proc_replace_node_value @xml OUTPUT,'(data/budget_list[1]/PrpManpowers/PrpManpower[1]/ename/text())[1]','666';
SELECT @xml.query('data/budget_list[1]/PrpManpowers/PrpManpower[1]/ename');--第三次把空 刷成了666
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息