5

我想知道您在现有代码中替换 SQL Server 游标的经验,或者您如何解决程序专家使用游标解决的问题,并基于集合进行。

游标用来解决什么问题?你是如何更换光标的?

4

4 回答 4

9

尽量不要循环,处理数据集。

您可以一次插入、更新、删除多行。这里以插入多行的示例为例:

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
于 2009-06-08T21:29:26.787 回答
4

我已经用 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'
于 2009-06-09T17:46:14.333 回答
2

好吧,习惯于程序化编程的应用程序开发人员通常会出于习惯而尝试以程序方式执行所有操作,即使在 SQL 中也是如此。

大多数情况下,具有正确参数的 SELECT 可能会这样做 - 或者您可能正在处理 UPDATE 语句。

真正的重点是:您需要开始思考集合操作并告诉您的 RDBMS 您想要做什么——而不是如何一步一步地去做。

很难给出一个单一的“正确”答案......你几乎必须用一个具体的例子来展示它。

马克

于 2009-06-08T21:14:37.550 回答
0

我编写了一些代码来计算与给定年份相关的财务数据的运行总计。在每个季度中,我必须将当前季度的值添加到运行总计中,同时适当地处理 NULL,以便在当前季度的值为 NULL 时结转上一季度的运行总计。

最初,我使用游标执行此操作,从功能的角度来看,这满足了业务需求。从技术的角度来看,它被证明是一个阻碍,因为随着数据量的增加,代码花费的时间呈指数增长。解决方案是用满足功能要求并消除任何性能问题的相关子查询来替换游标。

希望这可以帮助,

账单

于 2009-06-08T22:00:31.450 回答