Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 为什么我的光标停在中间? 这里发布的代码是“示例”代码,不是生产代码。我这样做是为了使我解释的问题可读/简洁。_Sql Server_Sql Server 2005_Tsql - Fatal编程技术网

Sql server 为什么我的光标停在中间? 这里发布的代码是“示例”代码,不是生产代码。我这样做是为了使我解释的问题可读/简洁。

Sql server 为什么我的光标停在中间? 这里发布的代码是“示例”代码,不是生产代码。我这样做是为了使我解释的问题可读/简洁。,sql-server,sql-server-2005,tsql,Sql Server,Sql Server 2005,Tsql,使用与下面类似的代码,我们遇到了一个奇怪的bug。每次插入后,WHILE循环停止 表包含100行,当插入在50行之后完成时,光标停止,只触及前50行。当插入在55之后完成时,它在55之后停止,依此类推 -- This code is an hypothetical example written to express -- an problem seen in production DECLARE @v1 int DECLARE @v2 int DECLARE MyCursor CURSOR

使用与下面类似的代码,我们遇到了一个奇怪的bug。每次插入后,WHILE循环停止

表包含100行,当插入在50行之后完成时,光标停止,只触及前50行。当插入在55之后完成时,它在55之后停止,依此类推

-- This code is an hypothetical example written to express
-- an problem seen in production

DECLARE @v1 int
DECLARE @v2 int

DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT Col1, Col2
FROM table

OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @v1, @v2

WHILE(@@FETCH_STATUS=0)
BEGIN

  IF(@v1>10)
  BEGIN
    INSERT INTO table2(col1) VALUES (@v2)
  END

  FETCH NEXT FROM MyCursor INTO @v1, @v2

END

CLOSE MyCursor
DEALLOCATE MyCursor
table2上有一个AFTER INSERT触发器,用于将table2上的突变记录到第三个表中,并恰当地命名为突变。它包含一个游标,用于插入以处理插入(每列以非常特定的方式记录突变,这需要游标)

一点背景知识:这存在于一组小型支持表中。出于审计目的,项目要求记录对源数据所做的每个更改。带有日志记录的表格包含诸如银行账号之类的内容,大量资金将存入其中。最多有几千条记录,它们应该很少被修改。审计功能是用来阻止欺诈的:我们用“谁做的”来记录“发生了什么变化”

实现这一点的明显、快速和合乎逻辑的方法是在每次进行更新时存储整行。这样我们就不需要光标了,它的性能会更好。然而,局势的政治性意味着我束手无策

呸。现在回到问题上来

触发器的简化版本(真实版本对每列进行插入,并且还插入旧值):


为什么代码在循环的中间退出?

< p>这个代码不会从游标中获取任何其他值,也不会增加任何值。事实上,没有理由在这里实现游标

您的整个代码可以重写为:

DECLARE @v1 int
DECLARE @v2 int

SELECT @v1 = Col1, @v2 = Col2
FROM table

IF(@v1>10)
    INSERT INTO table2(col1) VALUES (@v2)


编辑:已对Post进行编辑,以修复我所指的问题。

此代码不会从光标获取任何进一步的值,也不会增加任何值。事实上,没有理由在这里实现游标

您的整个代码可以重写为:

DECLARE @v1 int
DECLARE @v2 int

SELECT @v1 = Col1, @v2 = Col2
FROM table

IF(@v1>10)
    INSERT INTO table2(col1) VALUES (@v2)


编辑:已对帖子进行编辑,以修复我所指的问题。

Ryan,您的问题是@@FETCH\u状态对于连接中的所有游标都是全局的

因此触发器中的光标以@FETCH\u状态-1结束。当控件返回到上面的代码时,最后一个@FETCH\状态为-1,因此光标结束

可以在MSDN上找到的文档中对此进行了解释

您可以使用一个局部变量来存储@@FETCH\u状态,并将该局部变量放入循环中。你会得到这样的结果:

DECLARE @v1 int
DECLARE @v2 int
DECLARE @FetchStatus int

DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT Col1, Col2
FROM table

OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @v1, @v2

SET @FetchStatus = @@FETCH_STATUS

WHILE(@FetchStatus=0)
BEGIN

  IF(@v1>10)
  BEGIN
    INSERT INTO table2(col1) VALUES (@v2)
  END

  FETCH NEXT FROM MyCursor INTO @v1, @v2

  SET @FetchStatus = @@FETCH_STATUS

END

CLOSE MyCursor
DEALLOCATE MyCursor
值得注意的是,这种行为不适用于嵌套游标。我已经做了一个快速的例子,它在SQLServer2008上返回预期的结果(50)


Ryan,您的问题是@@FETCH\u状态对于连接中的所有游标都是全局的

因此触发器中的光标以@FETCH\u状态-1结束。当控件返回到上面的代码时,最后一个@FETCH\状态为-1,因此光标结束

可以在MSDN上找到的文档中对此进行了解释

您可以使用一个局部变量来存储@@FETCH\u状态,并将该局部变量放入循环中。你会得到这样的结果:

DECLARE @v1 int
DECLARE @v2 int
DECLARE @FetchStatus int

DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT Col1, Col2
FROM table

OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @v1, @v2

SET @FetchStatus = @@FETCH_STATUS

WHILE(@FetchStatus=0)
BEGIN

  IF(@v1>10)
  BEGIN
    INSERT INTO table2(col1) VALUES (@v2)
  END

  FETCH NEXT FROM MyCursor INTO @v1, @v2

  SET @FetchStatus = @@FETCH_STATUS

END

CLOSE MyCursor
DEALLOCATE MyCursor
值得注意的是,这种行为不适用于嵌套游标。我已经做了一个快速的例子,它在SQLServer2008上返回预期的结果(50)


正如ck所提到的,您不会获取任何进一步的值。因此,@FETCH\u状态从AFTER INSERT触发器中包含的游标中获取其值

您应该将代码更改为

DECLARE @v1 int
DECLARE @v2 int
DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT Col1, Col2
FROM table

OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @v1, @v2

WHILE(@@FETCH_STATUS=0)
BEGIN
  IF(@v1>10)
  BEGIN
    INSERT INTO table2(col1) VALUES (@v2)
  END
  FETCH NEXT FROM MyCursor INTO @v1, @v2
END

正如ck所提到的,您不会获取任何进一步的值。因此,@FETCH\u状态从AFTER INSERT触发器中包含的游标中获取其值

您应该将代码更改为

DECLARE @v1 int
DECLARE @v2 int
DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT Col1, Col2
FROM table

OPEN MyCursor

FETCH NEXT FROM MyCursor INTO @v1, @v2

WHILE(@@FETCH_STATUS=0)
BEGIN
  IF(@v1>10)
  BEGIN
    INSERT INTO table2(col1) VALUES (@v2)
  END
  FETCH NEXT FROM MyCursor INTO @v1, @v2
END

你的问题是你根本不应该使用光标!这是上面给出的示例的代码

INSERT INTO table2(col1)
SELECT Col1 FROM table
where col1>10
您也不应该在触发器中使用光标,这会降低性能。如果有人在insert中添加100000行,这可能需要几分钟(甚至几小时),而不是几毫秒或几秒钟。我们在这里替换了一个(在我开始这份工作之前),并将该表的导入时间从40分钟减少到45秒


应该检查使用游标的任何生产代码,用正确的基于集合的代码替换它。根据我的经验,90%以上的游标都可以以基于集合的方式编写。

您的问题是根本不应该使用游标!这是上面给出的示例的代码

INSERT INTO table2(col1)
SELECT Col1 FROM table
where col1>10
您也不应该在触发器中使用光标,这会降低性能。如果有人在insert中添加100000行,这可能需要几分钟(甚至几小时),而不是几毫秒或几秒钟。我们在这里替换了一个(在我开始这份工作之前),并将该表的导入时间从40分钟减少到45秒


应该检查使用游标的任何生产代码,用正确的基于集合的代码替换它。根据我的经验,90%以上的游标都可以用基于集合的方式编写。

这只是对触发器的一个简单误解。。。你根本不需要光标

if UPDATE(Col1)
begin

    insert into mutaties
    (
        tablename, 
        columnname, 
        newvalue
    )
    select
    'table2',
    coalesce(d.Col1,''),
    coalesce(i.Col1,''),
    getdate()
    from inserted i
        join deleted d on i.ID=d.ID
            and coalesce(d.Col1,-666)<>coalesce(i.Col1,-666)

end

这是对触发器的简单误解。。。你根本不需要光标

if UPDATE(Col1)
begin

    insert into mutaties
    (
        tablename, 
        columnname, 
        newvalue
    )
    select
    'table2',
    coalesce(d.Col1,''),
    coalesce(i.Col1,''),
    getdate()
    from inserted i
        join deleted d on i.ID=d.ID
            and coalesce(d.Col1,-666)<>coalesce(i.Col1,-666)

end

您不必使用光标将每一列作为单独的行插入

以下是一个例子:

INSERT LOG.DataChanges
SELECT
   SchemaName = 'Schemaname',
   TableName = 'TableName',
   ColumnName = CASE ColumnID WHEN 1 THEN 'Column1' WHEN 2 THEN 'Column2' WHEN 3 THEN 'Column3' WHEN 4 THEN 'Column4' END
   ID = Key1,
   ID2 = Key2,
   ID3 = Key3,
   DataBefore = CASE ColumnID WHEN 1 THEN I.Column1 WHEN 2 THEN I.Column2 WHEN 3 THEN I.Column3 WHEN 4 THEN I.Column4 END,
   DataAfter = CASE ColumnID WHEN 1 THEN D.Column1 WHEN 2 THEN D.Column2 WHEN 3 THEN D.Column3 WHEN 4 THEN D.Column4 END,
   DateChange = GETDATE(),
   USER = WhateverFunctionYouAreUsingForThis
FROM
   Inserted I
   FULL JOIN Deleted D ON I.Key1 = D.Key1 AND I.Key2 = D.Key2
   CROSS JOIN (
      SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
   ) X (ColumnID)
在X表中,您可以使用第二列编写额外的行为代码,该列专门描述如何处理该列(假设您希望某些列一直发布,但其他列仅在值更改时发布)。重要的是,这是一个将行拆分为每列的交叉连接技术的示例,但是还有很多事情可以做