【问题标题】:TSQL - perform code for each row of an selectTSQL - 为选择的每一行执行代码
【发布时间】:2016-01-20 01:24:13
【问题描述】:

是否有可能在不使用游标的情况下以某种方式为选择的每一行执行一些代码?

就我而言: 我有一个临时表来存储复杂脚本的一些数据。最后我想把这个表的一些信息(受某些条件限制)输出到输出中。

目前我正在使用带有选择的游标来限制表格的行。在这个光标中,我正在使用

 print '...'

生成输出。

必须有更简单的方法来做这些事情......

编辑:

create table #tmpAttributes(AttributeId uniqueidentifier, Value float, ValueString nvarchar(max), ActionId uniqueidentifier)

insert into #tmpAttributes (AttributeId, Value, ValueString, ActionId)
    select ID,..... -- in this select i'm doing some value conversions, if conversion is not possible i'm using -1

insert into ActionAttribute (ActionDefinitionID, Discriminator, ID, ReferredActionID, ValueDate, ValueListID, ValueMoney, ValueString, ValueUserID)
    select @defId, 'ActionAttributeMoneyEntity', NEWID(), ActionId, null, null, Value, null, null from #tmpAttributes

-- afterwards there is this cursor where I'm printint all rows where Value = -1

【问题讨论】:

  • 信息不足。你到底想达到什么目的?如果您向我们提供完整的场景,您可能会发现有一种完全基于集合的方式来满足您的需求。
  • 为什么不在已经创建的“临时表”上多放一个标识列,然后为每一行循环。
  • 如果您只想在“最后”打印这些信息,那为什么还需要光标呢?
  • 您是否有理由需要PRINT 行,而不是仅仅在Value = -1 所在的所有行上执行SELECT
  • 我想为用户生成一个输出,向他展示转换失败的位置。但你是对的......这也可能是一个选择 -.-

标签: sql sql-server tsql


【解决方案1】:

为结果集中的每一行执行打印语句几乎需要一个游标或类似的方法

declare @id int, @stuff varchar(20)
declare @tmp table
(
  id int not null
, stuff varchar(20)

  primary key(id)
)

insert @tmp
select id, stuff from mastertable
where condition1 > condition2

select top 1 @id=id, @stuff=stuff from @tmp

while (@@rowcount > 0)
begin
  print @stuff
  delete from @tmp where id=@id
  select top 1 @id=id, @stuff=stuff from @tmp
end

您仍在循环遍历每一行,但避免了光标。由于您使用的是表变量而不是游标,因此可以避免表锁定,但这不一定是更好的方法。您可以通过多种可能的方式逐行处理,例如添加“已处理列”或将所有选定行编号为 1..n 并根据行号进行迭代

如果您可以执行基于集合的操作,您只能避免逐行处理。您的问题中没有足够的信息来查看这在 TSQL 中是否可以避免

现在,编写 CLR proc 可能更灵活,因为您拥有更丰富的编程模型,并且在 CLR proc 中循环遍历结果集的每一行的开销很小。从 CLR proc 为数据库从 TSQL 中的每一行调用的每一行进行数据库调用

编辑 - 我看到有人已经添加了一种可能的方法来将您的打印语句转换为面向集合的操作。即

declare @msg varchar(max)
select @msg = ''

select msg = @msg + stuff 
from mastertable where condition1 > condition2

print @msg

这没关系,实际上是我所说的执行集合操作时所指的最优。如果可能,总是首选基于集合的操作。这可能不是很明显,但是如果涉及到很多行,在这个例子中字符串连接也会变得很慢。


我说过使用临时变量可以避免表锁定。这并不完全正确,因为 sql server 确实将临时变量写入 tempdb 中的表。我真正的意思是避免锁定生产表,并且由于保证您是该表的唯一用户,因此您不会竞争并发访问。

我也没有尝试对此进行优化。例如,内部循环可以跟踪 id 并且 where 条件变为 where id>@id(您还需要在 id 上定义主键)。由于在每次循环迭代期间临时表都不会更新,因此我希望它会更快。

【讨论】:

  • 仍然是一个循环,但没有使用“重”光标。谢谢!
【解决方案2】:

我认为您需要提供更多详细信息,但您可能正在寻找类似的内容:

declare @msg varchar(max)='';

select @msg = @msg + 'Output line: ' + ColumnA + ' -- ' + 'ColumnB' + char(13)+char(10)
from #temp
where ...
;

print @msg;

【讨论】:

  • 所以对于每一行,变量的值都会被扩展......好主意!
  • 确保添加 'OPTION (MAXDOP 1)' 以防止并行,这会产生意想不到的结果。
猜你喜欢
  • 1970-01-01
  • 2013-09-25
  • 2018-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
相关资源
最近更新 更多