如何在 SQL 服务器中的用户定义函数中使用 openxml
How to use openxml within a user defined function in SQL Server
我有一个 XML 结构,我在存储过程中使用 OPENXML
解析该结构以检索用于执行查询的参数。此过程是另一个存储过程(过程 2)正在调用的基本过程。过程 2 使用 insert-exec 构造从基本过程中获取数据。只要我们只调用过程 2 或基本过程,这就很好用。
我的第一个问题是我有一个不同的程序(程序 3)现在需要从程序 2 中获取结果(我需要该程序执行的业务规则),但由于消息的原因不能:
An INSERT EXEC statement cannot be nested.
然后我尝试采用基本过程并将其设为 table 值函数,但是当我执行它时,我收到消息:
Only functions and some extended stored procedures can be executed from within a function.
如何解决其中一个或两个问题?
编辑 1
我包括一个代码片段来显示基本过程(过程 1)和根据该过程的结果实现业务需求的过程(过程 2)。如果有第三个过程需要应用业务规则的结果,我们 运行 就会遇到问题。
create procedure dbo.p_Proc
@Xml xml
as
begin
set nocount on;
declare @l_idoc int
, @InfoId int
, @InfoTypeId int
, @Id int
, @Name varchar(50)
, @StatusId int
, @RoleId int
, @XmlBase xml
, @l_path varchar(100);
declare @T_TABLE table(
InfoId int
, InfoTypeId int
);
declare @T_RESULT table
(
Field1 int
, Field2 varchar(50)
, Field3 int
);
EXEC sp_xml_preparedocument @l_idoc OUTPUT, @Xml;
set @l_path = '/xml/Info';
insert into @T_TABLE(InfoId, InfoTypeId)
select InfoId, InfoTypeId
from OPENXML (@l_idoc, @l_path, 1)
with (
InfoId int './@InfoId'
, InfoTypeId int './@InfoTypeId'
);
select @InfoId = InfoId
, @InfoTypeId = InfoTypeId
from @T_TABLE;
-- create the XML to call the base widgets
select @XmlBase =
(
select *
from
(
select t.Id, t.Name, t.StatusId, t.RoleId
from @T_TABLE w
inner join dbo.T_TABLE2 t
on t.InfoId = w.InfoId
and t.InfoTypeId = w.InfoTypeId
) b
for xml raw('Widget'), root('Xml')
);
-- retrieve widgets from base security
insert into @T_RESULT(Field1, Field2, Field3)
exec dbo.p_ProcBase @Xml = @XmlBase;
-- apply business logic here
select w.Field1, w.Field2, w.Field3
from @T_RESULT w;
end;
go
create procedure dbo.p_ProcBase
@Xml xml = null
as
begin
set nocount on;
declare @l_idoc int
, @Id int
, @Name varchar(50)
, @StatusId int
, @RoleId int
, @l_path varchar(100);
declare @T_Table table(
Id int
, Name varchar(50)
, StatusId int
, RoleId int
);
EXEC sp_xml_preparedocument @l_idoc OUTPUT, @Xml;
set @l_path = '/Xml/Widget';
insert into @T_Table(Id, Name, StatusId, RoleId)
select Id, Name, StatusId, RoleId
from OPENXML (@l_idoc, @l_path, 1)
with (
ProjectId int './@Id'
, WidgetTypeName varchar(50) './@Name'
, WorkflowStatusId int './@StatusId'
, UserRoleId bigint './@RoleId'
);
select @Id = w.Id
, @Name = w.Name
, @StatusId = w.StatusId
, @RoleId = w.RoleId
from @T_Table w;
-- retrieve enabled widgets for which the user has a role in the current workflow state
select t.Field1, t.Field2, t.Field3
from dbo.T_TABLE t
where t.StatusId = @StatusId
and t.RoleId = @RoleId;
end;
为了在procs之间发送数据集(table),你必须使用Table类型,将proc2的输出存储在table类型的变量中并添加proc3
的只读 table 类型参数
首先,您必须创建一个 table 类型来映射 proc2 的输出:
CREATE TYPE T_RESULT AS TABLE
(
Field1 int
, Field2 varchar(50)
, Field3 int
);
在 dbo.p_Proc
中将 @T_RESULT
更改为:
declare @T_RESULT T_RESULT
然后创建proc3:
CREATE PROCEDURE dbo.proc3
@T_RESULT T_RESULT READONLY
AS
BEGIN
SET NOCOUNT ON
INSERT INTO T3(...)
SELECT ... FROM @T_RESULT
END
不要忘记在过程中的 table 类型参数后添加 READONLY。
我有一个 XML 结构,我在存储过程中使用 OPENXML
解析该结构以检索用于执行查询的参数。此过程是另一个存储过程(过程 2)正在调用的基本过程。过程 2 使用 insert-exec 构造从基本过程中获取数据。只要我们只调用过程 2 或基本过程,这就很好用。
我的第一个问题是我有一个不同的程序(程序 3)现在需要从程序 2 中获取结果(我需要该程序执行的业务规则),但由于消息的原因不能:
An INSERT EXEC statement cannot be nested.
然后我尝试采用基本过程并将其设为 table 值函数,但是当我执行它时,我收到消息:
Only functions and some extended stored procedures can be executed from within a function.
如何解决其中一个或两个问题?
编辑 1 我包括一个代码片段来显示基本过程(过程 1)和根据该过程的结果实现业务需求的过程(过程 2)。如果有第三个过程需要应用业务规则的结果,我们 运行 就会遇到问题。
create procedure dbo.p_Proc
@Xml xml
as
begin
set nocount on;
declare @l_idoc int
, @InfoId int
, @InfoTypeId int
, @Id int
, @Name varchar(50)
, @StatusId int
, @RoleId int
, @XmlBase xml
, @l_path varchar(100);
declare @T_TABLE table(
InfoId int
, InfoTypeId int
);
declare @T_RESULT table
(
Field1 int
, Field2 varchar(50)
, Field3 int
);
EXEC sp_xml_preparedocument @l_idoc OUTPUT, @Xml;
set @l_path = '/xml/Info';
insert into @T_TABLE(InfoId, InfoTypeId)
select InfoId, InfoTypeId
from OPENXML (@l_idoc, @l_path, 1)
with (
InfoId int './@InfoId'
, InfoTypeId int './@InfoTypeId'
);
select @InfoId = InfoId
, @InfoTypeId = InfoTypeId
from @T_TABLE;
-- create the XML to call the base widgets
select @XmlBase =
(
select *
from
(
select t.Id, t.Name, t.StatusId, t.RoleId
from @T_TABLE w
inner join dbo.T_TABLE2 t
on t.InfoId = w.InfoId
and t.InfoTypeId = w.InfoTypeId
) b
for xml raw('Widget'), root('Xml')
);
-- retrieve widgets from base security
insert into @T_RESULT(Field1, Field2, Field3)
exec dbo.p_ProcBase @Xml = @XmlBase;
-- apply business logic here
select w.Field1, w.Field2, w.Field3
from @T_RESULT w;
end;
go
create procedure dbo.p_ProcBase
@Xml xml = null
as
begin
set nocount on;
declare @l_idoc int
, @Id int
, @Name varchar(50)
, @StatusId int
, @RoleId int
, @l_path varchar(100);
declare @T_Table table(
Id int
, Name varchar(50)
, StatusId int
, RoleId int
);
EXEC sp_xml_preparedocument @l_idoc OUTPUT, @Xml;
set @l_path = '/Xml/Widget';
insert into @T_Table(Id, Name, StatusId, RoleId)
select Id, Name, StatusId, RoleId
from OPENXML (@l_idoc, @l_path, 1)
with (
ProjectId int './@Id'
, WidgetTypeName varchar(50) './@Name'
, WorkflowStatusId int './@StatusId'
, UserRoleId bigint './@RoleId'
);
select @Id = w.Id
, @Name = w.Name
, @StatusId = w.StatusId
, @RoleId = w.RoleId
from @T_Table w;
-- retrieve enabled widgets for which the user has a role in the current workflow state
select t.Field1, t.Field2, t.Field3
from dbo.T_TABLE t
where t.StatusId = @StatusId
and t.RoleId = @RoleId;
end;
为了在procs之间发送数据集(table),你必须使用Table类型,将proc2的输出存储在table类型的变量中并添加proc3
的只读 table 类型参数首先,您必须创建一个 table 类型来映射 proc2 的输出:
CREATE TYPE T_RESULT AS TABLE
(
Field1 int
, Field2 varchar(50)
, Field3 int
);
在 dbo.p_Proc
中将 @T_RESULT
更改为:
declare @T_RESULT T_RESULT
然后创建proc3:
CREATE PROCEDURE dbo.proc3
@T_RESULT T_RESULT READONLY
AS
BEGIN
SET NOCOUNT ON
INSERT INTO T3(...)
SELECT ... FROM @T_RESULT
END
不要忘记在过程中的 table 类型参数后添加 READONLY。