【问题标题】:Error regarding cursor in SQL关于 SQL 中游标的错误
【发布时间】:2009-04-18 06:20:54
【问题描述】:

以下是我的存储过程。它包含一个子查询

 Select StartTime From DrTimings Where DrID = @DrID 

如果这个子查询返回多个值,我会得到一个错误。子查询返回多行。我想在光标中获取每个 @StartTime 和 @EndTime 。意味着我想“从 Doctor 获取下一个到 @StTime 和 @EndTime”

我可以在游标中使用两个参数吗?

ALTER PROCEDURE [dbo].SP_AFK_GetSlotsByDate
    @DrID int,
    @AppointmentDate Datetime 
AS
BEGIN

Declare @StartTime Datetime
Declare @EndTime Datetime
BEGIN
    SET @StartTime = (Select StartTime From DrTimings Where DrID = @DrID)
    SET @EndTime =  (Select EndTime From DrTimings Where DrID = @DrID)
END

DECLARE Doctor CURSOR FOR

Select StartTime  from  TimeList1 where StartTime>=@StartTime and StartTime<@EndTime

Declare @StTime datetime
Declare @SlotID int
Declare @AppointmentTime datetime

Declare @TempSlots Table (SlotID int , AppointmentTime datetime null) 


Insert into
@TempSlots
(
SlotID ,
AppointmentTime
)
values(
0,
Getdate()
)

open Doctor
    fetch next from Doctor into @StTime
        while @@fetch_status = 0
            Begin

Select  @SlotID= T.SlotId from TimeList1 T
where T.StartTime>=@StartTime and T.StartTime<@EndTime and
T.SlotId not in 
        (Select A.SlotId from AppointmentSheet A where A.AppointmentDate=@AppointmentDate)

Select @AppointmentTime = Convert(varchar,right(T.StartTime,7),131)+' - '+ Convert(varchar,right(T.EndTime,7),131) 
from TimeList1 T
where T.StartTime>=@StartTime and T.StartTime<@EndTime and
T.SlotId not in 
        (Select A.SlotId from AppointmentSheet A where A.AppointmentDate=@AppointmentDate)

        Update @TempSlots
        Set SlotID  = @SlotID,
                AppointmentTime=@AppointmentTime

    fetch next from Doctor into @StTime
           end
    close Doctor
    deallocate Doctor

    Select * From @TempSlots
END 

【问题讨论】:

  • 注意:您还可以将第一个 BEGIN/END 块更改为: Select @StartTime = StartTime, @EndTime = EndTime From DrTimings Where DrID = @DrID 不需要两个单独的选择。

标签: sql sql-server sql-server-2005 tsql cursors


【解决方案1】:

只需这样添加下一个变量:

fetch next from Doctor into @StTime, @EndTime

你的光标的select语句应该包含EndTime列:

select StartTime, EndTime 
from   TimeList1 
where  StartTime>=@StartTime and StartTime<@EndTime

【讨论】:

    【解决方案2】:

    虽然游标是遍历结果集的一种简单方法,但出于性能影响,推荐使用游标。

    如果我知道您的 TimeList1 表的结构以及它与 DrTimings 表的关系,我会推荐一个 使用游标的版本。

    但是,在查看了您的 T-SQL 之后,我决定为您提供一个更新版本,以减少冗余并使用 JOINS 而不是子查询:

    ALTER PROCEDURE [dbo].SP_AFK_GetSlotsByDate
    (
        @DrID int,
        @AppointmentDate DateTime 
    )
    AS
    
    DECLARE 
        @StartTime DateTime,
        @EndTime DateTime,
        @SlotID int,
        @AppointmentTime DateTime;
    
    -- Retrieve the initial values for StartTime and EndTime
    -- These values get overwritten by the cursor (?)
    SELECT 
        @StartTime = StartTime,
        @EndTime = EndTime
    FROM
        DrTimings
    WHERE
        DrID = @DrID;
    
    
    DECLARE @TempSlots TABLE
    (
        SlotID int, 
        AppointmentTime datetime NULL
    ); 
    
    -- Set default values
    INSERT @TempSlots (SlotID,AppointmentTime)
    VALUES (0, GETDATE());
    
    
    DECLARE Doctor CURSOR FOR
    SELECT 
        StartTime,
        EndTime  
    FROM  
        TimeList1 
    where 
        StartTime >= @StartTime AND 
        StartTime < @EndTime;
    
    OPEN Doctor
    FETCH NEXT FROM Doctor INTO @StartTime,@EndTime
    WHILE @@FETCH_STATUS = 0
        BEGIN
            SELECT
                @SlotID = T.SlotId, 
                @AppointmentTime = CONVERT(varchar,RIGHT(T.StartTime,7),131)
                                   + ' - ' + CONVERT(varchar,RIGHT(T.EndTime,7),131) 
            FROM 
                TimeList1 T
                LEFT JOIN AppointmentSheet A ON T.SlotId = A.SlotId
            WHERE 
                T.StartTime >= @StartTime AND 
                T.StartTime < @EndTime AND
                A.AppointmentDate = @AppointmentDate AND
                A.SlotId IS NULL;
    
            -- This table will always be updated to contain the latest values
            -- it will contain only one row
            UPDATE 
                @TempSlots
            SET
                SlotID  = @SlotID,
                AppointmentTime = @AppointmentTime;
    
            FETCH NEXT FROM Doctor INTO @StartTime,@EndTime
        END
    
    CLOSE Doctor
    DEALLOCATE Doctor
    
    -- Return results
    SELECT 
        SlotId,
        AppointmentTime
    FROM 
        @TempSlots;
    

    更新:如果目的是获取 SlotId 和 AppointmentTime 的最新值,则甚至不需要迭代。

    【讨论】:

    • 这是我的 TimeSlot1 结构 SlotID int, StartTime datetime EndTime datetime
    • 完全正确 - 尽可能避免使用游标 - 它们在 C# 或 VB.NET 中很棒,但在 SQL 中却很糟糕。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    • 1970-01-01
    相关资源
    最近更新 更多