Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/79.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 是否可以在不使用游标的情况下对集合执行存储过程?_Sql_Sql Server_Tsql_Stored Procedures - Fatal编程技术网

Sql 是否可以在不使用游标的情况下对集合执行存储过程?

Sql 是否可以在不使用游标的情况下对集合执行存储过程?,sql,sql-server,tsql,stored-procedures,Sql,Sql Server,Tsql,Stored Procedures,我希望在集合中的每一行上执行一个存储过程,而不使用类似以下内容的游标: 选择EXEC dbo.Sproc@Param1=Table1.id 来自表1 我在SQLServer2005中使用T-SQL。我认为使用函数可以做到这一点,但如果可能的话,我希望使用存储过程(公司标准)是的。如果表中有一列可用于标记已处理的列,则可以在存在时使用: DECLARE @Id int WHILE EXISTS(SELECT * FROM Table1 WHERE Processed='N') BEGIN

我希望在集合中的每一行上执行一个存储过程,而不使用类似以下内容的游标:

选择EXEC dbo.Sproc@Param1=Table1.id
来自表1



我在SQLServer2005中使用T-SQL。我认为使用函数可以做到这一点,但如果可能的话,我希望使用存储过程(公司标准)

是的。如果表中有一列可用于标记已处理的列,则可以在存在时使用:

DECLARE @Id int
WHILE EXISTS(SELECT * FROM Table1 WHERE Processed='N')
BEGIN
 SELECT Top 1 @Id = id from Table1 WHERE Procesed='N'
 EXEC dbo.Sproc @Id
 UPDATE Table1 SET Processed = 'Y' WHERE Id = @Id
END
或者,将ID转储到临时表或表变量中,并在完成时删除:

DECLARE @HoldTable table (Id int PRIMARY KEY)
DECLARE @Id int
INSERT INTO @HoldTable SELECT Id FROM Table1
WHILE EXISTS(SELECT * FROM @HoldTable)
BEGIN
 SELECT @Id = id from @HoldTable
 EXEC dbo.Sproc @Id
 DELETE FROM @HoldTable where Id = @Id
END

10次中有9次,您可以不使用光标或while循环来做您想做的事情。然而,如果您必须使用一个,我发现while循环往往更快

此外,如果不想删除或更新该表,可以使用以下方法:

DECLARE @id [type]
SELECT @id = MIN([id]) FROM [table]
WHILE @id IS NOT NULL
BEGIN
    EXEC [sproc] @id
    SELECT @id = MIN([id]) FROM [table] WHERE [id] > @id
END

坦率地说,如果我需要为一组数据执行一个存储过程,而不是为一个记录执行存储过程,那么我将编写基于集合的代码,而不是使用存储过程。如果要针对大型数据集运行,这是唯一有效的方法。您可以从存储过程执行插入的时间从小时到秒甚至毫秒。当您需要处理一组数据时,千万不要使用基于记录的处理,特别是不要因为重复使用代码这样愚蠢的原因。性能胜过代码的重用。

如果可能,我将编写从临时表读取的存储过程的第二个版本


如果这是不可能的,那么您可能会倒霉。

如果您只使用SQL Server 2005或更高版本,不需要考虑向后兼容性,并且可以将代码转换为用户定义的函数(而不是存储过程),那么您可以使用新的“交叉应用”操作符,它使用的语法与您想要的非常类似。我找到了一个简短的介绍(当然,你也可以阅读BOLs和MSDN)

假设您的SP返回一个名为out_int的值,您的示例可以改写为:

SELECT T.id, UDF.out_int
FROM 
    Table1 T
CROSS APPLY
    dbo.fn_My_UDF(T.id) AS UDF
这将从表1中检索每个“id”,并使用它调用fn_My_UDF,其结果将显示在最终结果集中,与原始参数相同

“交叉应用”的变量是“外部应用”。它们相当于“内部联接”和“左联接”,但用于联接表和UDF(并同时调用第二个UDF)


如果你必须(按照尖头发老板的明确命令)使用SPs insead,那好吧,祝你好运!您必须使用游标,或者尝试欺骗一点:将代码更改为UDF,并创建包装器SPs:D。

此循环可以工作,但对于我的SP来说速度很慢。我认为正确的答案是由HLGEM提供的,最好的办法是编写更好的批量查询

DECLARE @id INT
SET @id = 0

DECLARE @max INT
SELECT TOP 1 @max = TableID
FROM dbo.Table
ORDER BY TableID desc

-- loop until BREAK
-- this is how you can perform a DO-WHILE loop in TSQL
WHILE (1 = 1) 
BEGIN
    SELECT      
        TOP 1 @id = TableID
        FROM dbo.Table
        WHERE TableID > @id 

    IF @id IS NOT NULL
    BEGIN        
        -- call you SP here
        PRINT @id        
    END

    -- break out of the loop once the max id has been reached
    IF @id = @max BREAK 
END
请使用动态SQL了解一下这一点。下面是根据您的查询得到的结果

DECLARE @TSQL NVARCHAR(MAX) = ''
SELECT @TSQL = @TSQL + N'EXEC dbo.Sproc ' + id + '; 
' -- To insert a line break
FROM Table1
EXEC sp_executesql @TSQL
如果您
PRINT@TSQL
,则等于以下内容。请注意

如果您不需要存储过程,那么它会更简单。我曾经需要根据视图中的每一行(Code是列名,[Value]是值)更新一个表。因为视图是动态的,每次都返回不同的行,因此会返回不同的列-值对。我在不使用任何存储过程的情况下重新编写了查询

DECLARE @TSQL NVARCHAR(MAX), @Id VARCHAR(10) = 50

SELECT @TSQL = COALESCE(@TSQL + '''
,','') + Code + ' = ''' + [Value] 
FROM view
WHERE Id = @Id

SET @TSQL = N'UPDATE tableName
SET ' + @TSQL + ''' 
WHERE Id = ' + @Id
PRINT @TSQL
--EXEC SP_EXECUTESQL @TSQL
PRINT@TSQL

UPDATE tableName
SET Discussed = 'Yes'
,Other = 'Blue'
,IntakeRating = 'H: < $25k'
,IntakeValueEstimate = '25'
,OpportunityDecision = 'Intake'
,Fee = '33 1/3'
,Fee2 = '40'
,IsManager = 'Yes' 
WHERE Id = 50
更新表名
集合讨论='是'
,其他=‘蓝色’
,凹角='H:<$25000'
,IntakValueEstimate='25'
,OpportunityDecision=‘摄入’
,费用='33 1/3'
,Fee2='40'
,IsManager='是'
其中Id=50

有些项目绝对需要使用游标,但与GOTOs一样,对它们的真正需求非常小。我同意最好在任何有意义的地方进行基于集合的处理,但您的“性能胜过代码重用”有点极端。因为我们讨论的是分钟和毫秒或小时和秒(或分钟)之间的差异,我不认为这是极端的。不考虑代码重用的数据库性能的程序员(特别是在使用光标时)通常有非常糟糕的数据库。在斯特劳曼案中,谁能争辩?但如果差异是毫秒与秒,或者毫秒与两倍于秒的毫秒,这要看情况而定。这不是斯特劳曼的例子,而是真实的生活体验。我已经修复了具有这种改进的游标,特别是在大型数据集插入上。将代码从存储过程中拉出来并放入内联集代码的问题是,您可能需要为每一行运行多个语句,并且需要从多个不同的触发器调用这些语句。这就是封装的全部要点,它是为了减少代码量,并在需要对其进行更改时将其集中起来。这完全正确吗?您可以在不指定GROUPBY子句的情况下按id订购吗?它不能正常工作。读取最大id后,它不会停止。-1不工作。正如Richard Collette所说,select语法被破坏了。如果你修改了语法,while就会陷入无限循环,正如Tony_Henrich所指出的那样)。我添加了一个具有工作循环的答案,但在我的经验中速度很慢。将exec置于循环顶部,然后无需检查两次条件(显然需要在循环之前选择一次id)。这只适用于特定场景(自动递增整数id)。交叉应用不适用于存储过程,因为它们不返回数据集,所以只报告输出。函数返回任何内容,这就是为什么SQL只能在使用交叉应用时使用它们。投票被否决。@Fernando68我在回答的开头指出了使用交叉应用作为解决方案的3个必要条件,其中第3个是“可以将代码转换为UDF而不是SP”如何将交叉应用与存储过程一起使用,因为UDF的定义中不能有exec命令。编辑了第二个回答。您选择的是@Id而不是@HoldTable
UPDATE tableName
SET Discussed = 'Yes'
,Other = 'Blue'
,IntakeRating = 'H: < $25k'
,IntakeValueEstimate = '25'
,OpportunityDecision = 'Intake'
,Fee = '33 1/3'
,Fee2 = '40'
,IsManager = 'Yes' 
WHERE Id = 50