3

Je cherche le meilleur moyen d'ajouter une contrainte à une table qui est effectivement un index unique sur la relation entre l'enregistrement et le reste des enregistrements dans cette table.Validation des instructions UPDATE et INSERT sur une table entière

Imaginez le tableau suivant décrit les patrouilles de divers gardes (du précédent scénario de gardien)

PK PatrolID Integer 
FK GuardID Integer 
    Starts DateTime 
    Ends  DateTime 

Nous commençons par une contrainte précisant que les temps de début et de fin doivent être logiques:

Ends >= Starts 

Cependant, je souhaite ajouter une autre contrainte logique: Un garde spécifique (GuardID) ne peut pas être à deux endroits en même temps, ce qui signifie que pour tout enregistrement, la période spécifiée par Début/Fin ne doit pas chevaucher la période définie pour toute autre patrouille. le même garde .

je peux penser à deux façons d'essayer d'aborder ceci:

Créer un déclencheur INSTEAD OF INSERT. Ce déclencheur utiliserait alors des curseurs pour parcourir la table INSERTED, en vérifiant chaque enregistrement. Si un enregistrement est en conflit avec un enregistrement existant, une erreur est générée. Les deux problèmes que j'ai avec cette approche sont: Je n'aime pas utiliser les curseurs dans une version moderne de SQL Server, et je ne suis pas sûr de savoir comment implémenter la même logique pour les UPDATE. Il peut également exister une complexité des enregistrements INSERTED en conflit les uns avec les autres.

La deuxième approche, apparemment meilleure, consisterait à créer une CONTRAINTE qui appelle une fonction définie par l'utilisateur, en transmettant les identifiants PatrolID, GuardID, Starts et Ends. La fonction effectue ensuite une recherche WHERE EXISTS pour rechercher les enregistrements qui chevauchent les paramètres GuardID/Starts/Ends qui ne sont pas l'enregistrement PatrolID d'origine. Cependant, je ne suis pas sûr des effets secondaires potentiels de cette approche.

La deuxième approche est-elle meilleure? Quelqu'un voit-il des pièges, comme lors de l'insertion/mise à jour de plusieurs lignes à la fois (ici, je suis concerné parce que les lignes dans ce groupe pourraient entrer en conflit, ce qui signifie que l'ordre est "inséré"). Y a-t-il une meilleure façon de le faire (comme un truc de fantaisie d'INDEX?)

Répondre

5

Utilisez déclencheur après pour vérifier que la contrainte de recouvrement n'a pas été violé:

create trigger Patrol_NoOverlap_AIU on Patrol for insert, update as 
    begin 
    if exists (select * 
     from inserted i 
     inner join Patrol p 
      on i.GuardId = p.GuardId 
      and i.PatrolId <> p.PatrolId 
     where (i.Starts between p.starts and p.Ends) 
     or (i.Ends between p.Starts and p.Ends)) 

     rollback transaction 
    end 

REMARQUE: Rolling une transaction dans un déclencheur prendra fin le lot. Contrairement à une violation de contrainte normale, vous ne serez pas en mesure d'attraper l'erreur.

Vous pouvez vouloir une clause where différente en fonction de la façon dont vous définissez la plage de temps et le chevauchement. Par exemple, si vous voulez être en mesure de dire Garde # 1 est à X de 6h00 à 7h00, puis Y 07h00 à 08h00 ce qui précède ne permettrait pas.Vous voulez à la place:

create trigger Patrol_NoOverlap_AIU on Patrol for insert, update as 
    begin 
    if exists (select * 
     from inserted i 
     inner join Patrol p 
      on i.GuardId = p.GuardId 
      and i.PatrolId <> p.PatrolId 
     where (p.Starts <= i.Starts and i.Starts < p.Ends) 
     or (p.Starts <= i.Ends and i.Ends < p.Ends)) 

     rollback transaction 
    end 

Où commence est le moment la garde commence et se termine est le moment infinitésimal après la garde se termine.

3

Le moyen le plus simple serait d'utiliser une procédure stockée pour les insertions. La procédure stockée peut faire l'insertion dans une seule instruction:

insert into YourTable 
(GuardID, Starts, Ends) 
select @GuardID, @Starts, @Ends 
where not exists (
    select * 
    from YourTable 
    where GuardID = @GuardID 
    and Starts <= @Ends 
    and Ends >= @Start 
) 

if @@rowcount <> 1 
    return -1 -- Failure 

D'après mon expérience, les déclencheurs et les contraintes avec les fonctions UDF tendent à devenir très complexes. Ils ont des effets secondaires qui peuvent exiger beaucoup de débogage pour comprendre.

Les procédures stockées fonctionnent simplement, et elles ont l'avantage supplémentaire que vous pouvez refuser les autorisations INSERT aux clients, vous donnant un contrôle précis sur ce qui entre dans votre base de données.

+0

Triggers sont nécessaires pour ce genre de chose que les enregistrements peuvent être insérés des endroits autres que le proc. Si vous n'appliquez pas le niveau de déclenchement, vous ne pouvez pas garantir l'intégrité des données. – HLGEM

+3

Eh bien, c'est pourquoi personne n'obtient des droits INSERT. – Andomar

-1
CREATE TRIGGER [dbo].[emaill] ON [dbo].[email] 
FOR INSERT 

AS 

BEGIN 


    declare @email CHAR(50); 


    SELECT @email=i.email from inserted i; 

    IF @email NOT LIKE '%[email protected]%_.__%' 
    BEGIN 
      print 'Triggered Fired'; 
      Print 'Invalid Emaill....'; 
      ROLLBACK TRANSACTION 
    END 

END 
Questions connexes