【问题标题】:How to replace the Cursor with set based solution in MS SQL Server?如何在 MS SQL Server 中用基于集合的解决方案替换光标?
【发布时间】:2020-05-15 23:44:15
【问题描述】:

我的存储过程中有游标。我已经更新到 while 循环以更快地执行。但是我的 while 循环需要很长时间。请帮我调试我的脚本。请找到我已删除光标并使用 while 循环更新的以下代码。我已经比较了相同的输出。但是与光标相比,这里需要很长时间。

DECLARE @orderArray INT
    ,@LeftTSMKEY NVARCHAR(250)
    ,@ListElid NVARCHAR(250)
    ,@ListType NVARCHAR(250)
    ,@ListType_Prev NVARCHAR(250)
    ,@inc INT

SET @ListType_Prev = ''
SET @inc = 0

DECLARE @cnt INT
DECLARE @t_cnt INT
DECLARE @Modules TABLE (
    ID INT IDENTITY(1, 1)
    ,ListElid VARCHAR(4000)
    ,ListType VARCHAR(30)
    ,orderArray VARCHAR(4000)
    ,LeftTSMKEY VARCHAR(30)
    )

INSERT INTO @Modules (
    ListElid
    ,ListType
    ,orderArray
    ,LeftTSMKEY
    )
SELECT ListElid
    ,ListType
    ,orderArray
    ,LeftTSMKEY
FROM QAT_ListElid
ORDER BY ListType
    ,orderArray

SET @cnt = 1

SELECT @t_cnt = count(*)
FROM @Modules

PRINT @t_cnt
PRINT 'loop started  in ' + cast(getdate() AS NVARCHAR(30))

WHILE @cnt <= @t_cnt
BEGIN
    SELECT @ListElid = ListElid
        ,@ListType = ListType
        ,@orderArray = orderArray
        ,@LeftTSMKEY = LeftTSMKEY
    FROM @Modules
    WHERE ID = @cnt

    IF @ListType <> @ListType_Prev
    BEGIN
        SET @inc = 0
        SET @ListType_Prev = @ListType
    END
    ELSE IF @ListType = @ListType_Prev
    BEGIN
        SET @inc = @inc + 1
    END

    IF @inc <> @orderArray
    BEGIN
        INSERT [QAT_ListElid2] (
            [ListElid]
            ,[ListType]
            ,[orderArray]
            ,[LeftTSMKEY]
            )
        VALUES (
            @ListElid
            ,@ListType
            ,@inc
            ,@LeftTSMKEY + CAST(@inc AS NVARCHAR(10)) + ']'
            )
    END

    SET @cnt = @cnt + 1
END

【问题讨论】:

  • 你为什么首先使用CURSOR (WHILE),这将是我的问题。另外,what CURSOR,你永远不会在上面声明一个。
  • 我已移除光标并使用了 while 循环。还需要很长时间
  • “我已经删除了光标并使用了while循环” 那你为什么要问CURSOR?不管怎样,是的,它会很慢。 SQL 不是一种编程语言,它是一种查询语言。您需要使用基于集合的解决方案。 WHILE(或CURSOR)几乎总是意味着你设计错了。在 T-SQL 中使用这两种方法的原因非常很少,我可以自信地说 99.99%(或更多)的时间都错误地使用了 .您真正需要在这里告诉我们的是您的真正目标是什么,然后我们可以帮助您构建基于集合的解决方案。
  • 只有在传统查询不起作用时才使用游标。游标总是存在性能问题。
  • @Larnu:谢谢你的信息。我需要优化我的脚本。请帮我解决这个问题。光标需要 2:00 小时才能运行我的 sp 。我想换。

标签: sql sql-server optimization while-loop cursor


【解决方案1】:

您没有提供用于验证的数据集和结果集,但几乎像往常一样,很少需要游标。相反,您可以使用查询来获得相同的结果。

我不知道这是否可行,但以下内容可以替代您所写的内容:

DECLARE @Modules TABLE (
    ID INT IDENTITY(1, 1)
    ,ListElid VARCHAR(4000)
    ,ListType VARCHAR(30)
    ,orderArray VARCHAR(4000)
    ,LeftTSMKEY VARCHAR(30)
    ,OrderedList INT
    )

INSERT INTO @Modules (
    ListElid
    ,ListType
    ,orderArray
    ,LeftTSMKEY
    ,OrderedList
    )
SELECT ListElid
    ,ListType
    ,orderArray
    ,LeftTSMKEY
    ,ROW_NUMBER() OVER (partition by ListType order by ListType,orderArray) - 1 as OrderedList
FROM QAT_ListElid

我维护您的临时表,但添加一个字段 OrderedList 以在行级别存储您的 @inc 变量。它是一个简单的计数器,从 0 开始,并在每次 ListType 更改时重新启动。

INSERT [QAT_ListElid2] (
    [ListElid]
    ,[ListType]
    ,[orderArray]
    ,[LeftTSMKEY]
    )
SELECT ListElid
    ,ListType
    ,orderArray
    ,LeftTSMKEY + CAST(OrderedList AS NVARCHAR(10)) + ']'
FROM @Modules
WHERE OrderedList <> orderArray

然后将所有值插入到结果表 QAT_ListElid2 中很简单,但仅当我们计算的值与 orderArray 不同时。

我相信这就是您想要实现的目标。如果不完全正确,您可以根据自己的要求进行调整。重点是证明你可以在没有游标的情况下生活得很好并获得性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多