首页 > 解决方案 > Mad cursor (SQL Server) 使程序冻结和崩溃

问题描述

我开发了一个航班预订程序。我在触发器中有一个(荒谬的)条件,如果它被填充则应该执行。

我的问题是,当我调用存储过程为客户预订航班时,我的程序由于延迟而冻结并崩溃。

我知道这个问题来自我的触发器,你知道是什么卡住了吗?

如果您需要更多详细信息(表、存储过程、代码),请随时告诉我!(对不起法国的评论:p)

ALTER TRIGGER [dbo].[Tr_Check60j]
ON [dbo].[Reservation]
FOR INSERT
AS
BEGIN
    DECLARE @IdVolInsere INT, @DateVol DATETIME, @IdVolExistant INT, @IdClient INT

    SELECT @IdVolInsere = v.VOL_Id 
    FROM Vol v
    JOIN Planning AS p ON p.PLA_Vol_Id = v.VOL_Id
    JOIN inserted AS i ON p.PLA_Id = i.RES_Pla_Id
    WHERE v.VOL_Id = p.PLA_Vol_Id
      AND p.PLA_Id = i.RES_Pla_Id

    SELECT @IdClient = i.RES_Client_Id
    FROM inserted i

    DECLARE @DateVolExistant DATETIME, @DateVolInsere DATETIME;
    DECLARE @IdVilleDepartExistant INT, @IdVilleArriveeExistant INT;
    DECLARE @IdVilleDepartInseree INT, @IdVilleArriveeInseree INT;

        -- Sélectionne l'id des villes du vol inséré
    SELECT @IdVilleDepartInseree = v.VOL_Vil_Depart_Id FROM Vol v WHERE v.VOL_Id = @IdVolInsere
    SELECT @IdVilleArriveeInseree = v.VOL_Vil_Arrivee_Id FROM Vol v WHERE v.VOL_Id = @IdVolInsere

    SELECT @DateVolInsere = p.PLA_Date
    FROM Planning p
    JOIN inserted AS i ON i.RES_Pla_Id = p.PLA_Id
    WHERE i.RES_Pla_Id = p.PLA_Id

    -- Curseur qui compare chaque vol du client existant avec le vol inséré pour vérifier si le vol existant
    -- est un vol retour, si oui, les 2 vols ont-ils plus de 60 jours entre eux? 
    -- Si oui, alors il faut vérifier s'il existe un vol réservé entre ces 2 vols --> S'il y en a un: ERREUR
    DECLARE CR_Check_Vols_Par_Id_Client CURSOR FOR 
        SELECT v.VOL_Id
        FROM Vol v
        JOIN Planning AS p ON v.VOL_Id = p.PLA_Vol_Id
        JOIN Reservation AS r ON p.PLA_Id = r.RES_Pla_Id
        WHERE r.RES_Client_Id = @IdClient

    OPEN CR_Check_Vols_Par_Id_Client

    FETCH CR_Check_Vols_Par_Id_Client INTO @IdVolExistant

    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- Sélectionne l'id des villes aller (mêmes villes) du dernier vol existant à moins de 60j
        SELECT @IdVilleDepartExistant = v.VOL_Vil_Depart_Id FROM Vol v WHERE v.VOL_Id = @IdVolExistant
        SELECT @IdVilleArriveeExistant = v.VOL_Vil_Arrivee_Id FROM Vol v WHERE v.VOL_Id = @IdVolExistant

            -- Vérifie s'il existe un vol aller pour le vol inséré
        IF (@IdVilleDepartInseree = @IdVilleArriveeExistant) AND (@IdVilleArriveeInseree = @IdVilleDepartExistant)
        BEGIN

            SELECT @DateVolExistant = p.PLA_Date
            FROM Planning p
            JOIN Reservation AS r
            ON r.RES_Pla_Id = p.PLA_Id
            JOIN Vol AS v
            ON p.PLA_Vol_Id = @IdVolExistant
            JOIN inserted AS i
            ON r.RES_Id = i.RES_Id
            WHERE r.RES_Client_Id = @IdClient

            -- Vérifie si le vol inséré est à une date de moins de 60 jours du vol retour existant
            IF ( (DATEDIFF(DAY, @DateVolExistant , @DateVolInsere)) < 60  )
            BEGIN

            DECLARE @CheckVolEntre INT
                -- Sélectionne un vol existant entre le vol aller inséré et le vol retour existant
                SELECT @CheckVolEntre = v.VOL_Id
                FROM Vol v
                JOIN Planning AS p
                ON v.VOL_Id = p.PLA_Vol_Id
                JOIN Reservation AS r
                ON p.PLA_Id = r.RES_Pla_Id
                WHERE r.RES_Client_Id = @IdClient AND v.VOL_Id BETWEEN @IdVolExistant AND @IdVolInsere
                -- Vérifie s'il existe un vol existant entre le vol aller inséré et le vol retour existant
                    IF (@CheckVolEntre != NULL)
                    BEGIN
                        RAISERROR('Réservation impossible', 1, 601)
                        ROLLBACK TRANSACTION
                        STOP
                END
            END
        END
    END
    CLOSE CR_Check_Vols_Par_Id_Client
    DEALLOCATE CR_Check_Vols_Par_Id_Client
END

先感谢您!

乔恩

标签: sqlsql-serverdatabase-cursor

解决方案


这只是一个临时的解决方法。正如我的评论中所述,您需要从触发器中删除光标。

您可能会遇到锁定问题。尝试下面的解决方法,看看是否有帮助。我还解决了一些编码问题。如前一条评论所述,您不能使用!=检查 NOT NULL ,它必须是IS NOT NULLor IS NULL

ALTER TRIGGER [dbo].[Tr_Check60j]
ON [dbo].[Reservation]
FOR INSERT
AS
BEGIN

    DECLARE @IdVolInsere INT, @DateVol DATETIME, @IdVolExistant INT, @IdClient INT

    SELECT 
        @IdVolInsere = v.VOL_Id 
    FROM Vol v WITH ( NOLOCK )
    JOIN Planning AS p WITH ( NOLOCK )
        ON p.PLA_Vol_Id = v.VOL_Id
    JOIN inserted AS i 
        ON p.PLA_Id = i.RES_Pla_Id
    WHERE 
        v.VOL_Id = p.PLA_Vol_Id
        AND p.PLA_Id = i.RES_Pla_Id

    SELECT @IdClient = i.RES_Client_Id FROM inserted i;

    DECLARE @DateVolExistant DATETIME, @DateVolInsere DATETIME;
    DECLARE @IdVilleDepartExistant INT, @IdVilleArriveeExistant INT;
    DECLARE @IdVilleDepartInseree INT, @IdVilleArriveeInseree INT;

        -- Sélectionne l'id des villes du vol inséré
    SELECT 
        @IdVilleDepartInseree = v.VOL_Vil_Depart_Id 
        , @IdVilleArriveeInseree = v.VOL_Vil_Arrivee_Id
    FROM Vol v WITH ( NOLOCK )
    WHERE 
        v.VOL_Id = @IdVolInsere;

    SELECT 
        @DateVolInsere = p.PLA_Date
    FROM Planning p WITH ( NOLOCK )
    JOIN inserted AS i 
        ON i.RES_Pla_Id = p.PLA_Id
    WHERE 
        i.RES_Pla_Id = p.PLA_Id

    -- Curseur qui compare chaque vol du client existant avec le vol inséré pour vérifier si le vol existant
    -- est un vol retour, si oui, les 2 vols ont-ils plus de 60 jours entre eux? 
    -- Si oui, alors il faut vérifier s'il existe un vol réservé entre ces 2 vols --> S'il y en a un: ERREUR
    DECLARE CR_Check_Vols_Par_Id_Client CURSOR FOR 
    SELECT 
        v.VOL_Id 
    FROM Vol v WITH ( NOLOCK )
    JOIN Planning AS p WITH ( NOLOCK ) 
        ON v.VOL_Id = p.PLA_Vol_Id
    JOIN Reservation AS r WITH ( NOLOCK ) 
        ON p.PLA_Id = r.RES_Pla_Id
    WHERE 
        r.RES_Client_Id = @IdClient

    OPEN CR_Check_Vols_Par_Id_Client

    FETCH CR_Check_Vols_Par_Id_Client INTO @IdVolExistant

    WHILE @@FETCH_STATUS = 0
    BEGIN

        -- Sélectionne l'id des villes aller (mêmes villes) du dernier vol existant à moins de 60j
        SELECT 
            @IdVilleDepartExistant = v.VOL_Vil_Depart_Id 
            , @IdVilleArriveeExistant = v.VOL_Vil_Arrivee_Id
        FROM Vol v WITH ( NOLOCK ) 
        WHERE 
            v.VOL_Id = @IdVolExistant;

            -- Vérifie s'il existe un vol aller pour le vol inséré
        IF (@IdVilleDepartInseree = @IdVilleArriveeExistant) AND (@IdVilleArriveeInseree = @IdVilleDepartExistant)
        BEGIN

            SELECT 
                @DateVolExistant = p.PLA_Date
            FROM Planning p WITH ( NOLOCK )
            JOIN Reservation AS r WITH ( NOLOCK )
                ON r.RES_Pla_Id = p.PLA_Id
            JOIN Vol AS v WITH ( NOLOCK )
                ON p.PLA_Vol_Id = @IdVolExistant
            JOIN inserted AS i
                ON r.RES_Id = i.RES_Id
            WHERE 
                r.RES_Client_Id = @IdClient;

            -- Vérifie si le vol inséré est à une date de moins de 60 jours du vol retour existant
            IF ( (DATEDIFF(DAY, @DateVolExistant , @DateVolInsere)) < 60  )
            BEGIN

            DECLARE @CheckVolEntre INT
                -- Sélectionne un vol existant entre le vol aller inséré et le vol retour existant
                SELECT 
                    @CheckVolEntre = v.VOL_Id
                FROM Vol v WITH ( NOLOCK )
                JOIN Planning AS p WITH ( NOLOCK )
                    ON v.VOL_Id = p.PLA_Vol_Id
                JOIN Reservation AS r WITH ( NOLOCK )
                    ON p.PLA_Id = r.RES_Pla_Id
                WHERE 
                    r.RES_Client_Id = @IdClient 
                    AND v.VOL_Id BETWEEN @IdVolExistant AND @IdVolInsere;

                -- Vérifie s'il existe un vol existant entre le vol aller inséré et le vol retour existant
                IF (@CheckVolEntre IS NOT NULL)
                BEGIN
                    RAISERROR('Réservation impossible', 1, 601)
                    ROLLBACK TRANSACTION
                    STOP
                END

            END

        END

    END

    CLOSE CR_Check_Vols_Par_Id_Client
    DEALLOCATE CR_Check_Vols_Par_Id_Client

END

推荐阅读