为什么嵌套的光标无限次地获取相同的结果

huangapple go评论56阅读模式
英文:

Why does the nested cursor keep pulling the same result infinitely

问题

我有一个过程,其中有一个嵌套的游标,用于返回酒店的房间,还有一个嵌套的游标,用于返回不同时间段内该房间的费用。然而,当我运行我的过程时,我得到了酒店的ID和名称(应该是这样的),房间的名称和描述(也应该是这样的),但然后第一个费用值被无限打印出来。

示例输出如下:

酒店ID:2100 - Sunridge B&B

房间101:单人间
费率:$125.00 有效期从2023年03月16日到2023年11月14日
费率:$125.00 有效期从2023年03月16日到2023年11月14日
费率:$125.00 有效期从2023年03月16日到2023年11月14日
费率:$125.00 有效期从2023年03月16日到2023年11月14日
费率:$125.00 有效期从2023年03月16日到2023年11月14日
费率:$125.00 有效期从2023年03月16日到2023年11月14日
费率:$125.00 有效期从2023年03月16日到2023年11月14日

为什么嵌套游标一直重复相同的值?我尝试使用SELECT DISTINCT,但它仍然返回相同的结果。

英文:

I have a procedure that has a nested cursor to return the rooms of the hotel, with a nested cursor inside that cursor to return the cost for that room over the different periods of time. However, when I run my procedure, I get the hotel id and name (as I should), the room name and description (as I should), but then the first cost value gets infinitely printed out.

CREATE PROCEDURE sp_GetRackRates
@HotelID smallint
AS
BEGIN
    DECLARE @HotelName		varchar(30)
    DECLARE @RoomID			smallint
    DECLARE @RoomNumber		smallint
    DECLARE @RTDescription	varchar(200)
    DECLARE @RackRate		smallmoney
    DECLARE @RackRateBegin	date
    DECLARE @RackRateEnd	date

    SELECT @HotelName = HotelName
    FROM Hotel
    WHERE HotelID = @HotelID

    PRINT 'Hotel ID: ' + CAST(@HotelID AS varchar(max)) + ' - ' + CAST(@HotelName as 
    varchar(max))

    PRINT '	'

    SELECT @RoomID = RoomID, @RoomNumber = RoomNumber, @RTDescription = RTDescription
    FROM (Room
    INNER JOIN RoomType ON Room.RoomTypeID = RoomType.RoomTypeID)
    WHERE Room.HotelID = @HotelID


    SELECT @RackRate = RackRate, @RackRateBegin = RackRateBegin, @RackRateEnd = 
           RackRateEnd
    FROM (RackRate
    INNER JOIN Room ON RackRate.HotelID = Room.HotelID)
    WHERE RackRate.HotelID = @HotelID AND RoomNumber = @RoomNumber


    DECLARE cr_GetRoom CURSOR
    FOR 
    SELECT RoomID, RoomNumber, RTDescription
    FROM (Room
    INNER JOIN RoomType ON Room.RoomTypeID = RoomType.RoomTypeID)
    WHERE Room.HotelID = @HotelID

    DECLARE cr_GetRackRates CURSOR
    FOR
    SELECT RackRate, RackRateBegin, RackRateEnd
    FROM (RackRate
    INNER JOIN Room ON RackRate.HotelID = Room.HotelID)
    WHERE RackRate.HotelID = @HotelID AND RoomNumber = @RoomNumber

    OPEN cr_GetRoom

    FETCH NEXT FROM cr_GetRoom
    INTO @RoomID, @RoomNumber, @RTDescription

    WHILE @@FETCH_STATUS = 0
    BEGIN
	    PRINT 'Room ' + CAST(@RoomNumber AS varchar(max)) + ': ' + CAST(@RTDescription 
               as varchar(max))

	    OPEN cr_GetRackRates
	    FETCH NEXT FROM cr_GetRackRates
	    INTO @RackRate, @RackRateBegin, @RackRateEnd

	    WHILE @@FETCH_STATUS = 0
	    BEGIN
		    PRINT 'Rate: $' + CAST(@RackRate as varchar(max)) + ' valid ' + 
                   cast(@RackRateBegin AS varchar(max)) + ' to ' + CAST(@RackRateEnd AS 
                   varchar(max))
	    END
	    CLOSE cr_GetRackRates

	    PRINT ' '
    END

    CLOSE cr_GetRoom
    DEALLOCATE cr_GetRackRates
    DEALLOCATE cr_GetRoom
END

A Sample output looks as follows:

HotelID: 2100 - Sunridge B&B

Room 101: Single
Rate: $125.00 valid 2023-03-16 to 2023-11-14
Rate: $125.00 valid 2023-03-16 to 2023-11-14
Rate: $125.00 valid 2023-03-16 to 2023-11-14
Rate: $125.00 valid 2023-03-16 to 2023-11-14
Rate: $125.00 valid 2023-03-16 to 2023-11-14
Rate: $125.00 valid 2023-03-16 to 2023-11-14
Rate: $125.00 valid 2023-03-16 to 2023-11-14

Where the Rate: $125.00 continues infinitely, even though there is only one of those values in the database.

Why does the nested cursor keep repeating the same value? I've tried using SELECT DISTINCT but it kept giving the same results.

答案1

得分: 1

我找到了我的问题所在。

在这些代码块中:

FETCH NEXT FROM cr_GetRoom
    INTO @RoomID, @RoomNumber, @RTDescription

    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT 'Room ' + CAST(@RoomNumber AS varchar(max)) + ': ' + CAST(@RTDescription 
               as varchar(max))

        OPEN cr_GetRackRates
        FETCH NEXT FROM cr_GetRackRates
        INTO @RackRate, @RackRateBegin, @RackRateEnd

        WHILE @@FETCH_STATUS = 0
        BEGIN
            PRINT 'Rate: $' + CAST(@RackRate as varchar(max)) + ' valid ' + 
                   cast(@RackRateBegin AS varchar(max)) + ' to ' + CAST(@RackRateEnd AS 
                   varchar(max))
        END
        CLOSE cr_GetRackRates

        PRINT ' '
    END

我没有获取下一个项目,所以它一直停留在相同的项目上,不断地返回它。

应该是:

FETCH NEXT FROM cr_GetRoom
    INTO @RoomID, @RoomNumber, @RTDescription

    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT 'Room ' + CAST(@RoomNumber AS varchar(max)) + ': ' + CAST(@RTDescription 
               as varchar(max))

        OPEN cr_GetRackRates
        FETCH NEXT FROM cr_GetRackRates
        INTO @RackRate, @RackRateBegin, @RackRateEnd

        WHILE @@FETCH_STATUS = 0
        BEGIN
            PRINT 'Rate: $' + CAST(@RackRate as varchar(max)) + ' valid ' + 
                   cast(@RackRateBegin AS varchar(max)) + ' to ' + CAST(@RackRateEnd AS 
                   varchar(max))
            FETCH NEXT FROM cr_GetRackRates
            INTO @RackRate, @RackRateBegin, @RackRateEnd
        END
        CLOSE cr_GetRackRates

        PRINT ' '
        FETCH NEXT FROM cr_GetRoom
        INTO @RoomID, @RoomNumber, @RTDescription
    END
英文:

I figured out what my issue was.

In the blocks

FETCH NEXT FROM cr_GetRoom
    INTO @RoomID, @RoomNumber, @RTDescription

    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT 'Room ' + CAST(@RoomNumber AS varchar(max)) + ': ' + CAST(@RTDescription 
               as varchar(max))

        OPEN cr_GetRackRates
        FETCH NEXT FROM cr_GetRackRates
        INTO @RackRate, @RackRateBegin, @RackRateEnd

        WHILE @@FETCH_STATUS = 0
        BEGIN
            PRINT 'Rate: $' + CAST(@RackRate as varchar(max)) + ' valid ' + 
                   cast(@RackRateBegin AS varchar(max)) + ' to ' + CAST(@RackRateEnd AS 
                   varchar(max))
        END
        CLOSE cr_GetRackRates

        PRINT ' '
    END

I was not fetching the next item, so it was just sitting at the same item constantly returning it.

It should be:

FETCH NEXT FROM cr_GetRoom
    INTO @RoomID, @RoomNumber, @RTDescription

    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT 'Room ' + CAST(@RoomNumber AS varchar(max)) + ': ' + CAST(@RTDescription 
               as varchar(max))

        OPEN cr_GetRackRates
        FETCH NEXT FROM cr_GetRackRates
        INTO @RackRate, @RackRateBegin, @RackRateEnd

        WHILE @@FETCH_STATUS = 0
        BEGIN
            PRINT 'Rate: $' + CAST(@RackRate as varchar(max)) + ' valid ' + 
                   cast(@RackRateBegin AS varchar(max)) + ' to ' + CAST(@RackRateEnd AS 
                   varchar(max))
            FETCH NEXT FROM cr_GetRackRates
            INTO @RackRate, @RackRateBegin, @RackRateEnd
        END
        CLOSE cr_GetRackRates

        PRINT ' '
        FETCH NEXT FROM cr_GetRoom
        INTO @RoomID, @RoomNumber, @RTDescription
    END

答案2

得分: 0

改进后的版本:

CREATE PROCEDURE spGetRackRates
@HotelID smallint
AS
BEGIN
	DECLARE @HotelName      varchar(30)
	DECLARE @RoomID         smallint
	DECLARE @RoomNumber     smallint
	DECLARE @RTDescription  varchar(200)
	DECLARE @RackRate       smallmoney
	DECLARE @RackRateBegin  date
	DECLARE @RackRateEnd    date
	
	SELECT @HotelName = HotelName
	FROM Hotel
	WHERE HotelID = @HotelID
	
	PRINT 'Hotel ID: ' + CAST(@HotelID AS varchar(max)) + ' - ' + CAST(@HotelName as 
	varchar(max))
	
	PRINT ' '
	
	DECLARE cr_GetRoom CURSOR READ_ONLY FORWARD_ONLY LOCAL STATIC FOR 
		SELECT	RoomID, RoomNumber, RTDescription
		FROM	Room
		INNER JOIN RoomType ON Room.RoomTypeID = RoomType.RoomTypeID
		WHERE	Room.HotelID = @HotelID

	OPEN cr_GetRoom
		
	WHILE 1 = 1
	BEGIN
		FETCH NEXT FROM cr_GetRoom
		INTO @RoomID, @RoomNumber, @RTDescription
		
		IF @@FETCH_STATUS <> 0
			BREAK
			
		DECLARE cr_GetRackRates CURSOR READ_ONLY FORWARD_ONLY LOCAL STATIC FOR
			SELECT	RackRate, RackRateBegin, RackRateEnd
			FROM	RackRate
			INNER JOIN Room ON RackRate.HotelID = Room.HotelID
			WHERE	RackRate.HotelID = @HotelID AND RoomNumber = @RoomNumber
		
		OPEN cr_GetRackRates
		
		WHILE 1 = 1
		BEGIN
			FETCH NEXT FROM cr_GetRackRates
			INTO @RackRate, @RackRateBegin, @RackRateEnd

			IF @@FETCH_STATUS <> 0
				BREAK
				
			PRINT 'Room ' + CAST(@RoomNumber AS varchar(max)) + ': ' + CAST(@RTDescription as varchar(max))
			PRINT 'Rate: $' + CAST(@RackRate as varchar(max)) + ' valid ' + 
		           cast(@RackRateBegin AS varchar(max)) + ' to ' + CAST(@RackRateEnd AS 
		           varchar(max))
		END
		PRINT ' '
		CLOSE cr_GetRackRates
		DEALLOCATE cr_GetRackRates
	END
	CLOSE cr_GetRoom
	DEALLOCATE cr_GetRoom
END

一些要点:

  1. 除非你想更新数据,否则应将游标声明为本地静态,以避免污染游标的命名空间,并提高性能。
  2. 你必须在抓取房间号后在循环内部声明内部游标,否则它将仅与原始房间变量绑定。
  3. 我更喜欢使用"WHERE 1 = 1"循环,并在抓取变量后检查fetch_status。这是因为很多时候人们忘记在循环结束时重新抓取变量,这会搞乱事情。
英文:

You seem to mix cursors with regular variable fetching and not doing actual loops, and declaring the cursors in wrong position so i'm gonna assume your fixed code doesn't work, so please forgive me if my assumption is not correct.

The improved(?) version:

CREATE PROCEDURE spGetRackRates
@HotelID smallint
AS
BEGIN
	DECLARE @HotelName      varchar(30)
	DECLARE @RoomID         smallint
	DECLARE @RoomNumber     smallint
	DECLARE @RTDescription  varchar(200)
	DECLARE @RackRate       smallmoney
	DECLARE @RackRateBegin  date
	DECLARE @RackRateEnd    date
	
	SELECT @HotelName = HotelName
	FROM Hotel
	WHERE HotelID = @HotelID
	
	PRINT &#39;Hotel ID: &#39; + CAST(@HotelID AS varchar(max)) + &#39; - &#39; + CAST(@HotelName as 
	varchar(max))
	
	PRINT &#39; &#39;

	DECLARE cr_GetRoom CURSOR READ_ONLY FORWARD_ONLY LOCAL STATIC FOR 
		SELECT	RoomID, RoomNumber, RTDescription
		FROM	Room
		INNER JOIN RoomType ON Room.RoomTypeID = RoomType.RoomTypeID
		WHERE	Room.HotelID = @HotelID

	OPEN cr_GetRoom
		
	WHILE 1 = 1
	BEGIN
		FETCH NEXT FROM cr_GetRoom
		INTO @RoomID, @RoomNumber, @RTDescription
		
		IF @@FETCH_STATUS &lt;&gt; 0
			BREAK
			
		DECLARE cr_GetRackRates CURSOR READ_ONLY FORWARD_ONLY LOCAL STATIC FOR
			SELECT	RackRate, RackRateBegin, RackRateEnd
			FROM	RackRate
			INNER JOIN Room ON RackRate.HotelID = Room.HotelID
			WHERE	RackRate.HotelID = @HotelID AND RoomNumber = @RoomNumber
		
		OPEN cr_GetRackRates
		
		WHILE 1 = 1
		BEGIN
			FETCH NEXT FROM cr_GetRackRates
			INTO @RackRate, @RackRateBegin, @RackRateEnd

			IF @@FETCH_STATUS &lt;&gt; 0
				BREAK
				
			PRINT &#39;Room &#39; + CAST(@RoomNumber AS varchar(max)) + &#39;: &#39; + CAST(@RTDescription as varchar(max))
			PRINT &#39;Rate: $&#39; + CAST(@RackRate as varchar(max)) + &#39; valid &#39; + 
		           cast(@RackRateBegin AS varchar(max)) + &#39; to &#39; + CAST(@RackRateEnd AS 
		           varchar(max))
		END
		PRINT &#39; &#39;
		CLOSE cr_GetRackRates
		DEALLOCATE cr_GetRackRates
	END
	CLOSE cr_GetRoom
	DEALLOCATE cr_GetRoom
	
END

A couple of points:

  1. Unless you want to update data, you should declare your cursors as local static, which avoids pollution the namespace of cursors, and also improve your performance.
  2. You have to declare the inner cursor "inside" the loop after your fetch the room number, otherwise it just becomes static bound to the original room variable.
  3. I prefer to use WHERE 1 = 1 loops and check for fetch_status after the fetching of variables. This is because a lot of times one forgets to fetch the variables again in the end of the loop which messes things up.

huangapple
  • 本文由 发表于 2023年2月19日 14:51:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75498460.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定