Mad cursor (SQL Server) 使程序卡死和崩溃
Mad cursor (SQL Server) make the program freeze and crash
我开发了一个航班预订程序。我在触发器中有一个(荒谬的)条件,如果它被填充则应该执行。
我的问题是,当我调用我的存储过程为客户预订航班时,我的程序由于延迟而冻结和崩溃。
我知道这个问题出在我的触发器上,你知道卡住的是什么吗?
如果您需要更多详细信息(表、存储过程、代码),请随时告诉我! (对法语评论感到抱歉: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
提前致谢!
乔恩
这只是一种临时解决方法。如我的评论所述,您需要从触发器中移除光标。
您可能 运行 遇到锁定问题。尝试下面的解决方法,看看是否有帮助。我还解决了一些编码问题。如前一条评论所述,您不能使用 != 检查 NOT NULL,它必须是 IS NOT NULL
或 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
首先,即使您的触发器没有超时,它也几乎可以肯定没有按照您的意愿进行。其中大部分归结为公然忽略某些语句中可能存在的多个 return 值。
我也有点担心目前的情况,因为如果我下周出差很短,我似乎无法做周末航班之类的事情!您可能想查看 下一班 航班,但我的知识不足以保证这一点。
也就是说,我 认为(基于对您的数据和意图的一些假设,但您需要检查我的工作)您可以使触发器看起来像这样:
ALTER TRIGGER [dbo].[Tr_Check60j]
ON [dbo].[Reservation]
FOR INSERT -- You could probably do INSTEAD OF and perform the insert here
AS
BEGIN
SELECT 'Existing Flight'
FROM Inserted
JOIN Planning Inserted_planning
ON Inserted_planning.pla_id = Inserted.res_pla_id
JOIN Vol Inserted_vol
ON Inserted_vol.vol_id = Inserted_planning.pla_vol_id
WHERE EXISTS (
SELECT 'Existing Flight'
FROM Vol Return_vol
JOIN Planning Return_planning
ON Return_planning.pla_vol_Id = return_vol.vol_id
AND Return_planning.pla_date >= DATEADD(day, -60, CAST(Inserted_planning.pla_date AS DATE))
AND Return_planning.pla_id != Inserted_planning.pla_id
JOIN Return_reservation
ON Return_reservation.res_pla_id = Return_planning.pla_Id
AND Return_reservation.res_client_id = Inserted.res_client_id
AND Return_reservation.res_id != Inserted.res_id
JOIN Planning Planning_Other
ON Planning_Other.pla_date >= Return_planning.pla_date
AND Planning_Other.pla_date < Inserted_planning.pla_date
AND Planning_other.pla_id != Inserted_planning.pla_id
AND Planning_other.pla_id != Return_planning.pla_id
JOIN Reservation Reservation_Other
ON Reservation_Other.res_pla_id = Planning_Other.pla_id
AND Reservation_Other.res_client_id = Inserted.res_client_id
AND Reservation_other.res_id != Inserted.res_id
AND Reservation_other.res_id != Return_reservation.res_id
WHERE Return_vol.vol_vil_depart_id = Inserted_vol.vol_vil_arrivee_id
AND Return_vol.vil_arrivee_id = Inserted_vol.vol_vil_depart_id)
IF (@@ROWCOUNT > 0)
BEGIN
RAISERROR('Réservation impossible', 1, 601)
ROLLBACK TRANSACTION
STOP
END
END
好的,这是 analysis/change 的推理。让我们按部分分解。
首先,可以合并和简化您的前置查询:
-- For clarity, put DECLAREs on their own lines
DECLARE @IdVolInsere INT;
DECLARE @IdClient INT;
DECLARE @DateVolInsere DATETIME;
DECLARE @IdVilleDepartInseree INT;
DECLARE @IdVilleArriveeInseree INT;
-- ....I'm against prefixing columns with table names, especially _shortened_ table names.
-- Except maybe for id columns.
SELECT @IdVolInsere = Vol.vol_id,
@IdClient = Inserted.res_client_Id,
@IdVilleDepartInseree = Vol.vol_vil_depart_id,
@IdVilleArriveeInseree = Vol.vol_vil_arrivee_id,
@DateVolInsere = Planning.pla_Date
FROM Vol -- Single-character aliases make it hard to track tables.
JOIN Planning
ON Planning.pla_vol_Id = vol.vol_Id
JOIN Inserted
ON Inserted.res_pla_id = Planning.pla_id
...既然已经清理完毕,让我们(清理并)看一下游标和其他语句:
DECLARE CR_Check_Vols_Par_Id_Client CURSOR FOR
SELECT Vol.vol_Id
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
...好的,检查是否有任何 Vol.vol_id
与我们的客户端 ID 相匹配。除了,这是一个直接的 AFTER
触发器,我们将获取刚刚插入的行!我们可能真的不想要那样!
好吧,我们暂时忽略它,并假设我们的最终状态不需要担心它。不过,让我们只删除游标声明,这会将它变成一个集合:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
...接下来的几行:
-- 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)
...告诉我我们想根据原始查询中的数据限制我们的集合。这会将集合剪切为仅这些匹配的行。让我们这样做:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
WHERE Vol.vol_vil_depart_id = @IdVilleArriveeInseree
AND Vol.vil_arrivee_id = @IdVilleDepartInseree
...下一个查询获取数据的简单条件:
IF ( (DATEDIFF(DAY, @DateVolExistant , @DateVolInsere)) < 60 )
...但是,我们已经有了这些数据,可以将其添加到我们的查询中:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
AND Planning.pla_date < DATEADD(day, 60, CAST(@DateVolInsere AS DATE))
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
WHERE Vol.vol_vil_depart_id = @IdVilleArriveeInseree
AND Vol.vil_arrivee_id = @IdVilleDepartInseree
...不幸的是,下一节的主要条件很可能是废话:
v.VOL_Id BETWEEN @IdVolExistant AND @IdVolInsere
... 那是因为它假设了关于键的非键关系。如果这些是自动生成的代理键,那绝对不是真的。自然密钥最可能的密钥方案不太可能在所有情况下都有用。使用属性数据要好得多,对于这种查询通常是日期。我的猜测是我们可以为此使用 Planning.pla_date
:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
AND Planning.pla_date >= DATEADD(day, -60, CAST(@DateVolInsere AS DATE))
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
JOIN Planning Planning_Other
ON Planning_Other.pla_date >= Planning.pla_date
AND Planning_Other.pla_date < @DateVolInsere
AND Planning_Other.pla_id != Planning.pla_id
JOIN Reservation Reservation_Other
ON Reservation_Other.res_pla_id = Planning_Other.pla_id
AND Reservation_Other.res.client_id = @IdClient
AND Reservation_Other.res_id != Reservation.res_id
WHERE Vol.vol_vil_depart_id = @IdVilleArriveeInseree
AND Vol.vil_arrivee_id = @IdVilleDepartInseree
... 而在这一点上,我们观察到我们实际上并不关心任何数据,只关心这样一行是否存在。值得庆幸的是,我们可以将其变成一个常规的 EXISTS
子句。
我开发了一个航班预订程序。我在触发器中有一个(荒谬的)条件,如果它被填充则应该执行。
我的问题是,当我调用我的存储过程为客户预订航班时,我的程序由于延迟而冻结和崩溃。
我知道这个问题出在我的触发器上,你知道卡住的是什么吗?
如果您需要更多详细信息(表、存储过程、代码),请随时告诉我! (对法语评论感到抱歉: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
提前致谢!
乔恩
这只是一种临时解决方法。如我的评论所述,您需要从触发器中移除光标。
您可能 运行 遇到锁定问题。尝试下面的解决方法,看看是否有帮助。我还解决了一些编码问题。如前一条评论所述,您不能使用 != 检查 NOT NULL,它必须是 IS NOT NULL
或 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
首先,即使您的触发器没有超时,它也几乎可以肯定没有按照您的意愿进行。其中大部分归结为公然忽略某些语句中可能存在的多个 return 值。
我也有点担心目前的情况,因为如果我下周出差很短,我似乎无法做周末航班之类的事情!您可能想查看 下一班 航班,但我的知识不足以保证这一点。
也就是说,我 认为(基于对您的数据和意图的一些假设,但您需要检查我的工作)您可以使触发器看起来像这样:
ALTER TRIGGER [dbo].[Tr_Check60j]
ON [dbo].[Reservation]
FOR INSERT -- You could probably do INSTEAD OF and perform the insert here
AS
BEGIN
SELECT 'Existing Flight'
FROM Inserted
JOIN Planning Inserted_planning
ON Inserted_planning.pla_id = Inserted.res_pla_id
JOIN Vol Inserted_vol
ON Inserted_vol.vol_id = Inserted_planning.pla_vol_id
WHERE EXISTS (
SELECT 'Existing Flight'
FROM Vol Return_vol
JOIN Planning Return_planning
ON Return_planning.pla_vol_Id = return_vol.vol_id
AND Return_planning.pla_date >= DATEADD(day, -60, CAST(Inserted_planning.pla_date AS DATE))
AND Return_planning.pla_id != Inserted_planning.pla_id
JOIN Return_reservation
ON Return_reservation.res_pla_id = Return_planning.pla_Id
AND Return_reservation.res_client_id = Inserted.res_client_id
AND Return_reservation.res_id != Inserted.res_id
JOIN Planning Planning_Other
ON Planning_Other.pla_date >= Return_planning.pla_date
AND Planning_Other.pla_date < Inserted_planning.pla_date
AND Planning_other.pla_id != Inserted_planning.pla_id
AND Planning_other.pla_id != Return_planning.pla_id
JOIN Reservation Reservation_Other
ON Reservation_Other.res_pla_id = Planning_Other.pla_id
AND Reservation_Other.res_client_id = Inserted.res_client_id
AND Reservation_other.res_id != Inserted.res_id
AND Reservation_other.res_id != Return_reservation.res_id
WHERE Return_vol.vol_vil_depart_id = Inserted_vol.vol_vil_arrivee_id
AND Return_vol.vil_arrivee_id = Inserted_vol.vol_vil_depart_id)
IF (@@ROWCOUNT > 0)
BEGIN
RAISERROR('Réservation impossible', 1, 601)
ROLLBACK TRANSACTION
STOP
END
END
好的,这是 analysis/change 的推理。让我们按部分分解。
首先,可以合并和简化您的前置查询:
-- For clarity, put DECLAREs on their own lines
DECLARE @IdVolInsere INT;
DECLARE @IdClient INT;
DECLARE @DateVolInsere DATETIME;
DECLARE @IdVilleDepartInseree INT;
DECLARE @IdVilleArriveeInseree INT;
-- ....I'm against prefixing columns with table names, especially _shortened_ table names.
-- Except maybe for id columns.
SELECT @IdVolInsere = Vol.vol_id,
@IdClient = Inserted.res_client_Id,
@IdVilleDepartInseree = Vol.vol_vil_depart_id,
@IdVilleArriveeInseree = Vol.vol_vil_arrivee_id,
@DateVolInsere = Planning.pla_Date
FROM Vol -- Single-character aliases make it hard to track tables.
JOIN Planning
ON Planning.pla_vol_Id = vol.vol_Id
JOIN Inserted
ON Inserted.res_pla_id = Planning.pla_id
...既然已经清理完毕,让我们(清理并)看一下游标和其他语句:
DECLARE CR_Check_Vols_Par_Id_Client CURSOR FOR
SELECT Vol.vol_Id
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
...好的,检查是否有任何 Vol.vol_id
与我们的客户端 ID 相匹配。除了,这是一个直接的 AFTER
触发器,我们将获取刚刚插入的行!我们可能真的不想要那样!
好吧,我们暂时忽略它,并假设我们的最终状态不需要担心它。不过,让我们只删除游标声明,这会将它变成一个集合:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
...接下来的几行:
-- 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)
...告诉我我们想根据原始查询中的数据限制我们的集合。这会将集合剪切为仅这些匹配的行。让我们这样做:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
WHERE Vol.vol_vil_depart_id = @IdVilleArriveeInseree
AND Vol.vil_arrivee_id = @IdVilleDepartInseree
...下一个查询获取数据的简单条件:
IF ( (DATEDIFF(DAY, @DateVolExistant , @DateVolInsere)) < 60 )
...但是,我们已经有了这些数据,可以将其添加到我们的查询中:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
AND Planning.pla_date < DATEADD(day, 60, CAST(@DateVolInsere AS DATE))
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
WHERE Vol.vol_vil_depart_id = @IdVilleArriveeInseree
AND Vol.vil_arrivee_id = @IdVilleDepartInseree
...不幸的是,下一节的主要条件很可能是废话:
v.VOL_Id BETWEEN @IdVolExistant AND @IdVolInsere
... 那是因为它假设了关于键的非键关系。如果这些是自动生成的代理键,那绝对不是真的。自然密钥最可能的密钥方案不太可能在所有情况下都有用。使用属性数据要好得多,对于这种查询通常是日期。我的猜测是我们可以为此使用 Planning.pla_date
:
SELECT -- dunno what we need yet
FROM Vol
JOIN Planning
ON Planning.pla_vol_Id = Vol.vol_id
AND Planning.pla_date >= DATEADD(day, -60, CAST(@DateVolInsere AS DATE))
JOIN Reservation
ON Reservation.res_pla_id = Planning.pla_Id
AND Reservation.res_client_id = @IdClient
JOIN Planning Planning_Other
ON Planning_Other.pla_date >= Planning.pla_date
AND Planning_Other.pla_date < @DateVolInsere
AND Planning_Other.pla_id != Planning.pla_id
JOIN Reservation Reservation_Other
ON Reservation_Other.res_pla_id = Planning_Other.pla_id
AND Reservation_Other.res.client_id = @IdClient
AND Reservation_Other.res_id != Reservation.res_id
WHERE Vol.vol_vil_depart_id = @IdVilleArriveeInseree
AND Vol.vil_arrivee_id = @IdVilleDepartInseree
... 而在这一点上,我们观察到我们实际上并不关心任何数据,只关心这样一行是否存在。值得庆幸的是,我们可以将其变成一个常规的 EXISTS
子句。