@@ FETCH_STATUS位于光标之外

时间:2014-10-17 09:58:06

标签: sql-server tsql cursor

我有一个处理XML数据的查询,我对游标返回的数据使用while(@@FETCH_STATUS = 0)循环。

当我使用Management Studio运行查询时,@@FETCH_STATUS等于-1,并且省略了我的循环中的代码。如果我使用调试器运行查询并按继续,它运行正常,@@FETCH_STATUS等于0.当我再次运行查询时,在调试@@FETCH_STATUS中运行它后等于0并更改为-1 。

总结一下:

  1. 我使用SSMS运行 - @@FETCH_STATUS = -1
  2. 我使用调试器运行 - @@FETCH_STATUS = 0(我想要这个值)
  3. 在使用调试器@@FETCH_STATUS运行后,我使用SSMS运行一次仍然等于0但后来更改为-1。
  4. 我使用OPEN cursorCLOSE cursorDEALLOCATE cursor。为什么这样做?

    编辑:您要求的代码:

    IF (OBJECT_ID('dbo.XmlOrderResponses') IS NOT NULL)
        DROP TABLE XmlOrderResponses;
    
    CREATE TABLE XmlOrderResponses (
        OrderResponseType INT
        ,OrderResponseNumber NVARCHAR(40)
        ,OrderResponseDate DATETIME
        ,DocumentFunctionCode NVARCHAR(40)
        ,Remarks INT
        );
    
    DECLARE CUR CURSOR
    FOR
    SELECT Subdirectory
    FROM XMLFiles;
    
    OPEN CUR
    
    WHILE (@@FETCH_STATUS = 0)
    BEGIN
        DECLARE @DocHandle AS INT;
        DECLARE @TMP AS NVARCHAR(512);
    
        FETCH NEXT
        FROM Cur
        INTO @TMP
    
        DECLARE @XmlDocument AS NVARCHAR(MAX);
    
        SET @XmlDocument = (
                SELECT CAST(XMLSource AS NVARCHAR(max))
                FROM XMLFiles
                WHERE subdirectory = @TMP
                );
    
        EXEC sys.sp_xml_preparedocument @DocHandle OUTPUT
            ,@XmlDocument;
    
        INSERT INTO XmlOrderResponses (
            OrderResponseType
            ,OrderResponseNumber
            ,OrderResponseDate
            ,DocumentFunctionCode
            ,Remarks
        )
    SELECT *
    FROM OPENXML(@DocHandle, '/Document-OrderResponse/*', 11) WITH (
            OrderResponseType INT
            ,OrderResponseNumber NVARCHAR(40)
            ,OrderResponseDate DATETIME
            ,DocumentFunctionCode NVARCHAR(40)
            ,Remarks INT
            );
    
        EXEC sys.sp_xml_removedocument @DocHandle;
    END
    
    CLOSE CUR;
    
    DEALLOCATE CUR;
    
    --I know I shouldn't be doing that but I can't get rid of NULL records the other way.
    DELETE
    FROM XmlOrderResponses
    WHERE OrderResponseType IS NULL
        AND OrderResponseNumber IS NULL
        AND OrderResponseDate IS NULL
        AND DocumentFunctionCode IS NULL
        AND Remarks IS NULL;
    
    SELECT *
    FROM XmlOrderResponses
    
    SELECT @@FETCH_STATUS
    

1 个答案:

答案 0 :(得分:2)

问题是,第一次引用@@FETCH_STATUS时,您还没有使用光标进行提取,因此它指的是最后使用的光标。想象一下这个简单的例子:

DECLARE C1 CURSOR
FOR
    SELECT TOP 3 ID
    FROM (VALUES ('1'), ('2'), ('3')) t (ID);

OPEN C1;

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @c1 CHAR(1);
    FETCH NEXT FROM C1 INTO @c1;
    PRINT @c1;
END

CLOSE C1;
DEALLOCATE C1;

DECLARE C2 CURSOR 
FOR 
    SELECT TOP 3 ID
    FROM (VALUES ('1'), ('2'), ('3')) t (ID);

OPEN C2;

-- HERE @@FETCH_STATUS REFERS TO THE LAST FETCH FOR CURSOR `C1` NOT `C2`
SELECT @@FETCH_STATUS;

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @c2 CHAR(1);
    FETCH NEXT FROM C2 INTO @c2;
    PRINT @c2;
END;

CLOSE C2;
DEALLOCATE C2;

在注释行中,即使您已关闭并取消分配C1@@FETCH_STATUS仍然引用此光标(因为之后没有执行其他FETCH),所以你永远不要进入C2

的循环

你应该在循环之前执行Fetch,然后在每个循环结束时执行,而不是在开始时执行。

DECLARE @TMP AS NVARCHAR(512);
OPEN CUR
-- DO FETCH FIRST
FETCH NEXT FROM Cur INTO @TMP

WHILE (@@FETCH_STATUS = 0)
BEGIN
    DECLARE @DocHandle AS INT;

    -- DO ALL YOUR WORK WITH @TMP


    --PERFORM THE FETCH AGAIN AT THE END OF THE LOOP
    FETCH NEXT FROM Cur INTO @TMP
END

在每个循环开始时执行FETCH的另一个问题是,最后一项将被处理两次。再一个简单的例子(假设你输入@@ FETCH_STATUS = 0的循环)

DECLARE C1 CURSOR 
FOR
    SELECT ID = '1';

OPEN C1;
DECLARE @c CHAR(1);
WHILE (@@FETCH_STATUS = 0)
BEGIN
    DECLARE @c1 CHAR(1);
    FETCH NEXT FROM C1 INTO @c1;
    PRINT @c1;
END

这将打印

1
1

因为,当@@FETCH_STATUS为-1时,FETCH只会返回当前位置的项目。