SQL Server 替换游标有哪些不同的方法?

0aydgbwb  于 2023-01-16  发布在  其他
关注(0)|答案(5)|浏览(223)

我想知道您在替换现有代码中的SQL Server游标方面的经验,或者您是如何处理一个过程化人员将使用游标来解决的问题,并基于集合来解决它的。
光标是用来解决什么问题的?你是如何替换光标的?

jq6vz3qz

jq6vz3qz1#

尽量不要循环,处理数据集。
你可以插入,更新,删除多行在同一时间.这里在一个例子插入多行:

INSERT INTO YourTable
        (col1, col2, col3, col4)
    SELECT
        cola, colb+Colz, colc, @X
        FROM ....
            LEFT OUTER JOIN ...
        WHERE...

查看循环时,看看它在里面做了什么。如果只是插入/删除/更新,重写为使用单个命令。如果有IF,看看它们是否是插入/删除/更新的CASE语句或WHERE条件。如果是,删除循环并使用set命令。
我用基于集合的命令替换了循环,将执行时间从几分钟减少到几秒钟;我用许多嵌套循环和过程调用替换了过程,并保留了循环(只使用插入/删除/更新是不可能的),但是我删除了游标,并且看到了更少的锁定/阻塞和巨大的性能提升。这里有两个比游标循环更好的循环方法。
如果你必须循环,在一个集合上做如下操作:

--this looks up each row for every iteration
DECLARE @msg VARCHAR(250)
DECLARE @hostname sysname

--first select of currsor free loop
SELECT @hostname= min(RTRIM(hostname))
    FROM  master.dbo.sysprocesses (NOLOCK)
    WHERE  hostname <> ''

WHILE @hostname is not null
BEGIN
    set @msg='exec master.dbo.xp_cmdshell "net send ' 
        + RTRIM(@hostname) + ' '
        + 'testing  "'
    print @msg
    --EXEC (@msg)

    --next select of cursor free loop
    SELECT @hostname= min(RTRIM(hostname))
        FROM master.dbo.sysprocesses (NOLOCK)
        WHERE  hostname <> ''
        and hostname > @hostname
END

如果你有一个合理的项目集(不是100,000)循环,你可以这样做:

--this will capture each Key to loop over
DECLARE @msg VARCHAR(250)
DECLARE @From   int
DECLARE @To     int
CREATE TABLE #Rows
(
     RowID     int not null primary key identity(1,1)
    ,hostname  varchar(100)
)

INSERT INTO #Rows
SELECT DISTINCT hostname
    FROM  master.dbo.sysprocesses (NOLOCK)
    WHERE  hostname <> ''
SELECT @From=0,@To=@@ROWCOUNT

WHILE @From<@To
BEGIN
    SET @From=@From+1

    SELECT @msg='exec master.dbo.xp_cmdshell "net send ' 
        + RTRIM(hostname) + ' '
        + 'testing  "'
        FROM #Rows WHERE RowID=@From
    print @msg
    --EXEC (@msg)
END
t8e9dugd

t8e9dugd2#

我用WHILE循环替换了一些游标。

DECLARE @SomeTable TABLE
(
     ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
     SomeNumber int,
     SomeText varchar
)

DECLARE @theCount int
DECLARE @theMax int

DECLARE @theNumber int
DECLARE @theText varchar

INSERT INTO @SomeTable (SomeNumber, SomeText)
SELECT Number, Text
FROM PrimaryTable

SET @theCount = 1
SELECT @theMax = COUNT(ID) FROM @SomeTable

WHILE (@theCount <= @theMax)
BEGIN

     SET @theNumber = 0
     SET @theText = ''

     SELECT @theNumber = IsNull(Number, 0), @theText = IsNull(Text, 'nothing')
     FROM @SomeTable
     WHERE ID = @theCount

     -- Do something.
     PRINT 'This is ' + @theText + ' from record ' + CAST(@theNumber AS varchar) + '.'

     SET @theCount = @theCount + 1

END

PRINT 'Done'
u3r8eeie

u3r8eeie3#

通常,习惯于过程式编程的应用程序开发人员出于习惯,会尝试过程化地做所有事情,即使是在SQL中。
大多数情况下,带有正确参数的SELECT语句就可以做到--或者您正在处理的是UPDATE语句。
关键是:你需要开始考虑集合操作,告诉你的RDBMS你想做什么--而不是如何一步一步地做。
很难给予一个单一的、“正确的”答案......你几乎必须用一个具体的例子来说明它。
马克

3zwtqj6y

3zwtqj6y4#

我编写了一些代码来计算与给定年份相关的财务数据的累计,在每个季度,我必须将当前季度的值添加到累计中,同时适当地处理NULL,以便当前季度的值为NULL时,上一季度的累计可以结转。
最初,我使用游标来完成此操作,从功能Angular 来看,这满足了业务需求。从技术Angular 来看,这变成了一个障碍,因为随着数据量的增加,代码花费的时间呈指数增长。解决方案是用满足功能需求并消除任何性能问题的相关子查询来替换游标。
希望这能帮上忙,
比尔

nzk0hqpo

nzk0hqpo5#

当(看看我在那里做了什么)问题被问了又回答,我想添加我的响应,以防它会帮助其他人。像许多其他人一样,我转换到while语句,但我想利用我的数据集中现有的主键,而不是计数到结果集的大小。我想出了这个模式,一直运作良好。我对其他人和改进持开放态度。谢谢!

declare @PrimaryKey int
/* get first record for looping */
select @PrimaryKey = min(PrimaryKey) from Table
/* it will be null when all records have iterated */
while (@PrimaryKey is not null)
  begin        
    /* do stuff */
    
    /* iterate to next key */
    select @PrimaryKey = min(PrimaryKey) from Table where PrimaryKey > @PrimaryKey
  end

相关问题