使用标与动态SQL在一个存储程序
-
20-08-2019 - |
题
我有一个动态SQL statement我已经创建了一个存储的过程。我需要循环访问的结果,使用了光标。我有一个很难找出正确的语法。这里是我在做什么。
SELECT @SQLStatement = 'SELECT userId FROM users'
DECLARE @UserId
DECLARE users_cursor CURSOR FOR
EXECUTE @SQLStatment --Fails here. Doesn't like this
OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC asp_DoSomethingStoredProc @UserId
END
CLOSE users_cursor
DEALLOCATE users_cursor
什么是正确的方式做到这一点?
解决方案
一个光标将只接受一个SELECT语句,所以如果真的SQL需要是动态的,让您正在执行的语句声明游标部分。对于下面的工作您的服务器将具有使用全局游标是。
Declare @UserID varchar(100)
declare @sqlstatement nvarchar(4000)
--move declare cursor into sql to be executed
set @sqlstatement = 'Declare users_cursor CURSOR FOR SELECT userId FROM users'
exec sp_executesql @sqlstatement
OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId
WHILE @@FETCH_STATUS = 0
BEGIN
Print @UserID
EXEC asp_DoSomethingStoredProc @UserId
FETCH NEXT FROM users_cursor --have to fetch again within loop
INTO @UserId
END
CLOSE users_cursor
DEALLOCATE users_cursor
如果您需要避免使用游标全球,你也可以将您的动态SQL结果到一个临时表中,然后使用该表来填充你的光标。
Declare @UserID varchar(100)
create table #users (UserID varchar(100))
declare @sqlstatement nvarchar(4000)
set @sqlstatement = 'Insert into #users (userID) SELECT userId FROM users'
exec(@sqlstatement)
declare users_cursor cursor for Select UserId from #Users
OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC asp_DoSomethingStoredProc @UserId
FETCH NEXT FROM users_cursor
INTO @UserId
END
CLOSE users_cursor
DEALLOCATE users_cursor
drop table #users
其他提示
此代码是用于与光标的动态列一个很好的例子,因为你不能在@STATEMENT使用“+”:
ALTER PROCEDURE dbo.spTEST
AS
SET NOCOUNT ON
DECLARE @query NVARCHAR(4000) = N'' --DATA FILTER
DECLARE @inputList NVARCHAR(4000) = ''
DECLARE @field sysname = '' --COLUMN NAME
DECLARE @my_cur CURSOR
EXECUTE SP_EXECUTESQL
N'SET @my_cur = CURSOR FAST_FORWARD FOR
SELECT
CASE @field
WHEN ''fn'' then fn
WHEN ''n_family_name'' then n_family_name
END
FROM
dbo.vCard
WHERE
CASE @field
WHEN ''fn'' then fn
WHEN ''n_family_name'' then n_family_name
END
LIKE ''%''+@query+''%'';
OPEN @my_cur;',
N'@field sysname, @query NVARCHAR(4000), @my_cur CURSOR OUTPUT',
@field = @field,
@query = @query,
@my_cur = @my_cur OUTPUT
FETCH NEXT FROM @my_cur INTO @inputList
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @inputList
FETCH NEXT FROM @my_cur INTO @inputList
END
RETURN
与工作无关系数据库(IDMS的人?) 在ODBC connection有资格作为一个的那些时光标和动态SQL似乎是唯一的途径。
select * from a where a=1 and b in (1,2)
需要45分钟的反应,同时重新写入使用的按键组没有在条款将运行在1二:
select * from a where (a=1 and b=1)
union all
select * from a where (a=1 and b=2)
如果在表B列包含1145行,使用了光标创造indidivudal的发言和执行它们,作为动态SQL是远远快于使用中的条款。愚蠢的?
并且是的,没时间在一个关系数据库,光标的应用。我只是不能相信我遇到一个实例,其中一个标循环是几幅度更快。
首先,避免如果在所有可能使用的光标。这里有一些资源铲除它出来的时候,似乎你不能没有:
这是说,虽然,你可能会坚持一个毕竟 - 我不知道从你的问题,足以确保这类原因申请。如果是这样的话,你已经有了一个不同的问题 - 你的光标选择语句必须是一个的实际的SELECT语句,而不是EXECUTE语句。你就完蛋了。
但看到cmsjr答案(这进来,而我写的)有关使用临时表。我想避免的全球的游标甚至超过 “普通” 的....
之后,最近交换来自Oracle到SQL服务器的(雇主的偏好),我通知标的支持SQL服务器的滞后。游标并不总是邪恶的,有时需要,有时要快得多,并有时吸尘器比试图为你一个复杂的查询通过重新安排或增加优化。将"游标是邪恶"的意见更为突出,在SQL Server社区。
所以我想这回答是切换到Oracle或给MS一个线索。
- Oracle执行立即进入一个光标
- 通过循环的一个隐含的光标 (a
for
环隐含地 定义/开启或关闭 光标!)
有是,我想另一实例与你分享结果:d http://www.sommarskog.se/dynamic_sql.html#cursor0
在SQL Server的另一种方法是你所有的动态查询的做成表变量的存储过程,然后使用游标来查询并处理。至于可怕光标辩论:),我所看到的研究显示,在某些情况下,光标实际上可以更快,如果正确设置。我用他们自己时所需的查询是太复杂了,或者只是不从人的角度(对我来说)。)可能
此代码可以对您有用。
例如光标使用在SQL服务器
DECLARE sampleCursor CURSOR FOR
SELECT K.Id FROM TableA K WHERE ....;
OPEN sampleCursor
FETCH NEXT FROM sampleCursor INTO @Id
WHILE @@FETCH_STATUS <> -1
BEGIN
UPDATE TableB
SET
...