2010-05-03 3 views
0

J'ai le déclencheur suivant;SQL Server 2008 déclencheur ne fonctionne pas correctement avec plusieurs insertions

CREATE TRIGGER trFLightAndDestination 
ON checkin_flight 
AFTER INSERT,UPDATE 
AS 
BEGIN 
    IF NOT EXISTS 
    (
        SELECT 1 
      FROM Flight v 
      INNER JOIN Inserted AS i ON i.flightnumber = v.flightnumber 
      INNER JOIN checkin_destination AS ib ON ib.airport = v.airport 
      INNER JOIN checkin_company AS im ON im.company = v.company 
      WHERE i.desk = ib.desk AND i.desk = im.desk 
    ) 
    BEGIN 
     RAISERROR('This combination of of flight and check-in desk is not possible',16,1) 
     ROLLBACK TRAN  
    END 
END 

Ce que je veux que le déclencheur à faire est de vérifier les tables de vol, checkin_destination et checkin_company quand un nouveau record pour checkin_flight est ajouté. Chaque enregistrement de checkin_flight contient un numéro de vol et un numéro de bureau où les passagers doivent s'enregistrer pour cette destination. Les tables checkin_destination et checkin_company contiennent des informations sur les entreprises et les destinations réservées à certains bureaux d'enregistrement. Lors de l'ajout d'un enregistrement à checkin_flight j'ai besoin d'informations de la table de vol pour obtenir la destination et flightcompany avec le numéro de vol inséré. Cette information doit être vérifiée par rapport aux combinaisons d'enregistrement disponibles pour les vols, les destinations et les compagnies.

J'utilise le déclencheur comme indiqué ci-dessus, mais lorsque j'essaie d'insérer une mauvaise combinaison, la gâchette le permet. Qu'est-ce que j'oublie ici?

EDIT 1: J'utilise l'instruction d'insertion multiple suivant

INSERT INTO checkin_flight VALUES (5315,3),(5316,3),(5316,2) 
//5315 is the flightnumber, 3 is the desknumber to checkin for that flight 

EDIT 2: Testés une seule insertion de la ligne qui est impossible, l'erreur est lancée correcte. Donc, c'est l'insertion multiple qui semble donner le problème.

Répondre

1

Le problème est que votre logique permet à tout insert incluant au moins un jeu de valeurs valide. Il échouera uniquement si tous les enregistrements des enregistrements insérés ne sont pas valides, au lieu de si des enregistrements insérés ne sont pas valides.

Changez votre "SI N'EXISTE PAS (...)" en une instruction "SI EXISTE (...)" et changez votre instruction SELECT pour retourner les vols invalides.

par exemple:

IF EXISTS 
(
       SELECT 1 
     FROM Flight v 
     INNER JOIN Inserted AS i ON i.flightnumber = v.flightnumber 
     LEFT JOIN checkin_destination AS ib ON ib.airport = v.airport 
      AND i.desk = ib.desk 
     LEFT JOIN checkin_company AS im ON im.company = v.company 
      AND i.desk = im.desk 
     WHERE (im.desk IS NULL OR ib.desk IS NULL) 
) 
BEGIN 
    RAISERROR('This combination of of flight and check-in desk is not possible',16,1) 
    ROLLBACK TRAN  
END 
+0

Merci chris! Cela a fait l'affaire, devinez que je dois m'entraîner un peu plus dans la pensée logique – Rob

+0

Une petite question, pourquoi une jointure gauche et pas une jointure interne? parce que le bureau peut être vide? – Rob

+1

Je faisais l'hypothèse que le cas négatif était lorsque l'enregistrement correspondant dans checkin_destination ou checkin_company n'existe pas; Vérifier que quelque chose n'existe pas est fait par LEFT JOINing à la table, puis ne recherche aucun enregistrement (d'où la partie "im.desk IS NULL"). C'est juste une supposition, et vous devriez modifier pour le corriger. L'objectif, comme je l'ai dit, est de faire en sorte que votre requête trouve des enregistrements dans INSERTED qui ne sont pas valides. –

1

Je ne suis pas sûr de la logique de votre entreprise, mais vous devez vérifier que la requête fait la bonne chose.

Votre problème est le IF NOT EXISTS, si la condition est vraie pour 1 des 3 lignes dans INSERTED il n'existe pas. Vous devez le convertir pour trouver une ligne de problèmes et utiliser IF EXISTS puis une erreur.

Toutefois, lorsque dans un déclencheur la meilleure façon d'erreur out est:

RAISERROR() 
ROLLBACK TRANSACTION 
RETURN 

Je doute genre de que l'absence d'un RETURN est votre problème, mais il est toujours préférable d'inclure les trois R s en cas d'erreur dans un déclencheur.

+1

mieux utiliser TRY/CATCH bien sûr – gbn

+0

À mon avis rollback explicite est généralement dangereux pour un déclencheur car elle suppose un comportement sous-jacent qui peut être injustifiée. Il y a une raison pour laquelle XACT_ABORT est désactivé par défaut. – Einstein

+1

@Einstein, je ne suis pas d'accord. Si mon déclencheur détecte un problème, je veux toujours le restaurer. Peut-être que c'est juste moi et comment je code, mais je veux toujours le retour au point d'erreur, donc je peux insérer dans une table de journal ce qui se passait. Il est de la responsabilité du code appelant de se rendre compte qu'il y avait une erreur, et il peut ajouter des informations supplémentaires sur le journal des erreurs lorsqu'il se termine également. Cependant, je pourrais voir si vous avez un système de gestion des erreurs différent qu'une annulation dans le déclencheur pourrait vous causer un problème si vous ne l'attendiez pas. –

1

Le tableau inséré peut contenir plusieurs lignes et donc toute logique dans un déclencheur DOIT être en mesure d'appliquer à toutes les lignes. Les déclencheurs d'idée doivent tirer une fois par effet de ligne est un déclencheur de WRT commun malentendu. SQL Server aura tendance à fusionner les appels à un déclencheur pour augmenter les performances lorsqu'ils se produisent dans la même transaction.

Pour corriger, vous pourriez commencer avec un compteur () de inséré et de comparer cela avec un compteur () des conditions correspondantes et d'élever une erreur en cas de non-concordance.

+0

ne devrait pas le select 1 et la combinaison de jointures prendre soin de cela? – Rob

+0

@Einstein dit * SQL Server aura tendance à fusionner les appels à un déclencheur pour augmenter les performances quand ils se produisent dans la même transaction. * Quoi? donc si je fais 'BEGIN TRANSACTION; INSERT YourTable VALUES (...); INSERT YourTable VALUES (...); INSERT YOURTable VALUES (...); COMMIT; 'vous dites que SQL Server appellera seulement le déclencheur une fois? en aucune façon! –

+0

Je ne connais pas la sémantique exacte mais oui c'est vraiment cool :) – Einstein

1

Le problème est que la condition sera vraie si un seul des enregistrements insérés est correct. Vous devez vérifier que tous les documents sont corrects, .: par exemple

if (
    (
    select count(*) from inserted 
) = (
    select count(*) from flight v 
    inner join inserted i ... 
) 
) ... 
+0

Ah maintenant je reçois aussi ce que dit Einstein :) Semble logique en effet! Je vais l'essayer. est 'sinon' la bonne façon de revenir à l'expression? – Rob

+0

Votre compte a effectivement fait l'affaire, mais la solution Chris Shaffers. Merci d'avoir aidé! – Rob

+0

@Rob: À droite, vous devez vérifier la différence de nombre, donc vous utiliserez l'opérateur '<>' au lieu de '='. – Guffa

Questions connexes