英文:
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
一些要点:
- 除非你想更新数据,否则应将游标声明为本地静态,以避免污染游标的命名空间,并提高性能。
- 你必须在抓取房间号后在循环内部声明内部游标,否则它将仅与原始房间变量绑定。
- 我更喜欢使用"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 '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
A couple of points:
- 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.
- 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.
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论