【问题标题】:How Can I avoid using Cursor for implementing this pseudo code - SQL Server如何避免使用游标来实现此伪代码 - SQL Server
【发布时间】:2011-03-18 06:20:14
【问题描述】:
CREATE PROCEDURE p_processDataFor @accountId
BEGIN

    for each item in 
      (select * from Accounts where accountId   = @accountId and isProcessed = 0)
    BEGIN
        CASE current row
            WHEN  has x Condition THEN
                exec p_x <Pass all data of current row>
            WHEN  has y Condition THEN
                exec p_y <Pass all data of current row>
            WHEN  has z Condition THEN
                exec p_z <Pass all data of current row>
            END
    END

END

【问题讨论】:

  • 您可能必须内联p_xp_yp_z 的内容。如果没有看到它们的内容,很难知道在这种情况下是否可能。了解p_x 等是否在您系统的其他地方使用也会很有用。
  • p_x 等包含基于少数条件将数据插入其他表的代码。假设 p_x 等不会在其他地方使用。
  • 一行是否会匹配多个 x、y 和/或 z 条件,并且是您实现的超出预期的行为(如果 x 条件匹配,则永远不会考虑 y 或 z) .此外,这个游标从不将 isProcessed 设置为另一个值这一事实是否是一个遗漏,还是在其他地方处理过(或者是无关紧要的)?
  • 如果每个p_x 只是一个单独的插入,那么绝对可以将它们内联并消除光标。
  • p_x 在不同的表中有多个插入,并且必须在后续表中再次插入 SCOPE_IDENTITY()。它们具有逻辑参照完整性(但未在表上创建外键)。是的,你是对的,它设置了 isProcessed。我完全错过了。

标签: sql-server tsql sql-server-2008 cursor


【解决方案1】:

由于您正在调用 EXEC,因此您通常无法避免循环,这不能作为基于 SET 的操作来完成;必须一件一件地完成。

如果你只是想避免 CURSOR,你可以使用 WHILE 循环来实现它。

否则,另一种选择是使用 SELECT + FOR XML 语句将 EXEC 语句作为单个 NVARCHAR(MAX) 语句构建到变量中,然后只执行该动态 SQL。

【讨论】:

  • +1 你能告诉我如何使用SELECT + FOR XML 获取单个 nvarchar(max) 值吗?
【解决方案2】:

好的,这个例子只对条件 X 进行插入,但希望向您展示您可以继续的方式:

create table T1 (
    ID int IDENTITY(1,1) not null,
    Val1 varchar(10) not null,
    constraint PK_T1 PRIMARY KEY (ID)
)
go
create table T2 (
    ID int not null,
    Val2 varchar(10) not null,
    constraint PK_T2 PRIMARY KEY (ID)
)
go
create table Val (
    ID int IDENTITY(1,1) not null,
    Val1 varchar(10) not null,
    Val2 varchar(10) not null,
    Processed bit not null,
    CondX bit not null
)
go

Val 是我的表,其中包含要处理的行(在您的示例中为 Accounts)。 T1 和 T2 是您的 p_x 过程当前插入/更新的两个表。

insert into Val(Val1,Val2,Processed,CondX)
select 'abc','def',0,1 union all
select 'ghi','jkl',0,0 union all
select 'mno','pqr',0,1
go

只是一些示例数据 - 我有 3 行,其中 2 行匹配“条件 x”:

declare @Inter table (ValID int,T1ID int,Val2 varchar(10))

;merge into T1 using (select * from Val where CondX=1) Val on 1=0
when not matched then insert (Val1) values (Val.Val1)
output inserted.ID,Val.ID,Val.Val2 into @Inter (T1ID,ValID,Val2);

insert into T2(ID,Val2)
select T1ID,Val2 from @Inter

update Val set Processed = 1 where ID in (select ValID from @Inter)
go

对于您的实际工作,您需要上述 3 份副本 - x、y 和 z 各一份。如果它在同一个存储过程中,则需要为 @Inter 表使用不同的名称。 merge statement 被轻微滥用,因为您不能使用从插入语句引用其他表的OUTPUT clause。但我们使用它是为了从 T1 捕获生成的 IDENTITY 值,以及将要插入到其他表中的相应数据。

所以现在我们将使用表变量@Inter 进一步插入 T2,并最终更新 Val 以指示行已被处理。如果有一个表链需要插入和获取标识值,则需要引入更多的合并语句和表变量。

select * from Val
select * from T1
select * from T2

我们得到了结果:

ID          Val1       Val2       Processed CondX
----------- ---------- ---------- --------- -----
1           abc        def        1         1
2           ghi        jkl        0         0
3           mno        pqr        1         1

(3 row(s) affected)

ID          Val1
----------- ----------
1           abc
2           mno

(2 row(s) affected)

ID          Val2
----------- ----------
1           def
2           pqr

(2 row(s) affected)

所以我们已经针对条件 X 执行了所有工作,始终保持代码集为基础。

【讨论】:

  • 我会在星期一试试这个,然后告诉你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 2019-09-24
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 2013-09-25
相关资源
最近更新 更多