【问题标题】:cursor to update a row with values from the previous and current rows游标用前一行和当前行的值更新一行
【发布时间】:2010-11-03 17:25:13
【问题描述】:

查询作者,

我有一张如下表:

myTable t1
col2 col3
 2    1
 3    0
 4    0
 5    0
 6    0

我想用前一行中 col3 的值加上当前行中 col2 的值来更新 col3 上的每个零。所以我的表会如下所示:

myTable t1
col2 col3
 2    1 
 3    4  (1+3)
 4    8  (4+4)
 5    13 (5+8) 
 6    19 (6+13)

我错过了这里的逻辑,也许是短视。我用光标尝试如下:

DECLARE @var3 FLOAT

DECLARE cursor3 CURSOR FOR
SELECT col2, col3 FROM table1
FOR UPDATE OF col3
OPEN cursor3


FETCH FIRST FROM cursor3
WHILE (@@FETCH_STATUS > -1)
BEGIN
 UPDATE @table1
 SET col3 = isnull(@var3, 0) + isnull(col2, 0)
 WHERE CURRENT OF cursor3
 FETCH NEXT FROM cursor3 INTO @var3
END

但这是错误的。 有什么想法吗?

提前致谢。

【问题讨论】:

  • 您是否还有其他列,例如桌子上的 ID 列?游标几乎从来都不是一个好主意...
  • 其他一些列,但没有主键。这是用于生成报告的临时表。我非常相信游标存在这样的问题。
  • 存在此类问题的游标,但这并不意味着它们是最佳/最合适的解决方案。如果您在临时表中添加一个标识列,这将变得容易 10000 倍。

标签: sql sql-server-2005 database-cursor


【解决方案1】:

好的,试试这个。

CREATE TABLE MyTable (Id INT Identity, Col2 int, Col3 int)

INSERT INTO MyTable (Col2, Col3)
VALUES (2,1), (3,0), (4,0),(5,0),(6,0)

SELECT * from MyTable

WHILE (SELECT COUNT(*) FROM MyTable WHERE Col3=0) > 0
BEGIN
    UPDATE TOP (1) MyTable
    SET CoL3 = (Mytable.col2 + (select col3 from mytable t2 where (t2.id = mytable.id-1)))
    WHERE Col3 = 0
END

SELECT * from MyTable

使用WHILE 循环,在大多数情况下应该比游标更快。

【讨论】:

  • 所以它做你想要的?实际数据集有多大,速度是否可以接受?显然你也可以将其更改为使用#Temp@TableVar 类型的表格,我只是做了一个真实的测试。
  • 数据集大约有 100 行,任何解决方案都可以接受。总时间不到2秒,全部包括在内。我使用@tableVar。感谢您的宝贵时间!
  • @JNK 除了使用 While 循环之外的任何其他方法,因为在我的情况下,表中的记录可能约为 200,000 +,然后使用 while 循环肯定会减慢速度。
  • @HemantSisodia 您可以将 WHILE 中的条件替换为 EXISTS,因为它短路应该更快。记录的数量不会太重要。如果您需要非常快,您也可以使用您的 PK 循环遍历这些记录,即第一个循环只处理 1-10,000 之间的 PK 值,然后执行下一个 10k 等。
  • @JNK 感谢您的回复,我得到了您答案的第一部分。您能否解释一下第二部分,即通过任何 Fiddle 部分循环 PK 值?那会很有帮助。
【解决方案2】:

我在表中添加了一个标识列,最后使用了这样的代码:

DECLARE @saldo_Q_previous FLOAT
DECLARE @ID INTEGER

DECLARE cursor3 CURSOR FOR
SELECT ID FROM @myTable
FOR UPDATE OF col2
OPEN cursor3

FETCH NEXT FROM cursor3 INTO @ID
FETCH NEXT FROM cursor3 INTO @ID

WHILE (@@FETCH_STATUS > -1)
BEGIN

    SET @col2_previous = ISNULL((SELECT TOP 1 col2 FROM @myTable WHERE ID < @ID ORDER BY ID DESC), 0)
    SET @vrQ = ISNULL((SELECT TOP 1 vr_Q FROM @myTable WHERE ID < @ID ORDER BY ID DESC), 0)

    UPDATE @myTable
    SET col2 = isnull(@col2_previous, 0) + isnull(vrMov_Q, 0)
    WHERE CURRENT OF cursor3

    FETCH NEXT FROM cursor3 INTO @ID
END

CLOSE cursor3
DEALLOCATE cursor3

它解决了我的问题。谢谢大家。

【讨论】:

    【解决方案3】:

    这是一个使用公用表表达式 (CTE) 更新数据的 UPDATE 语句。

    WITH myTable2 AS
        (
        SELECT col2, col3, ROW_NUMBER() OVER (ORDER BY col2) AS sequence
        FROM myTable
        ),
      newTable AS
        (
        SELECT t1.col2, SUM(t2.col2) - SUM(t2.col3) AS col3
        FROM myTable2 t1
        LEFT OUTER JOIN myTable2 t2 ON t1.sequence >= t2.sequence
        GROUP BY t1.col2
        )
    
    UPDATE myTable
    SET col3 = newTable.col3
    FROM myTable
    JOIN newTable on myTable.col2 = newTable.col2
    ;
    

    【讨论】:

    • 是递归的吗?即,它是否会将第 3 列的更新值用于下一行?
    • @JNK - 它不是递归的。请注意,在 newTable CTE 中,连接条件使用 >=,以便它可以汇总 col2 的所有先前副本。 col3 的总和是减去第一行中的 1 的简单方法。
    【解决方案4】:

    FWIW 使用 CURSOR 的主要、令人信服的理由是,如果不这样做,会对您的 rdbms 造成太大影响。您几乎总是可以使用 WHILE 循环而不是 CURSOR;一次处理一个记录;无论出于何种原因,您可能需要迭代大量记录时,这可能非常有用...... CURSOR 操作比等效的 SET 操作效率更高。

    所以总的来说,它归结为速度和开销与效率......

    CURSORS 几乎是最慢的方法,但开销最少,即使在 MSSQL 2012 中仍然有用...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-22
      • 2021-12-16
      • 2019-07-06
      • 1970-01-01
      • 2016-01-17
      • 1970-01-01
      • 2021-06-02
      相关资源
      最近更新 更多