3

Ich bin auf der Suche nach dem besten Weg zum Hinzufügen einer Einschränkung für eine Tabelle, die effektiv ein eindeutiger Index für die Beziehung zwischen dem Datensatz und den Rest der Datensätze in dieser Tisch.Überprüfung UPDATE und INSERT-Anweisungen für eine ganze Tabelle

Stellen Sie sich die folgende Tabelle zeigt die Patrouillen der verschiedenen Wachen (aus dem früheren Wächter-Szenario) beschreiben

PK PatrolID Integer 
FK GuardID Integer 
    Starts DateTime 
    Ends  DateTime 

Wir waren mit einer Einschränkung beginnen die Angabe, dass die Start- und Endzeiten logisch sein muss:

Ends >= Starts 

Ich möchte jedoch eine weitere logische Einschränkung hinzufügen: Eine bestimmte Wache (GuardID) darf nicht gleichzeitig an zwei Orten sein, was bedeutet, dass für jeden Datensatz der durch Start/Ende festgelegte Zeitraum nicht mit dem für jede andere Patrouille definierten Zeitraum überlappen sollte die gleiche Wache .

Ich denke an zwei Möglichkeiten, kann versuchen, diesen Ansatz:

einen STATT INSERT-Trigger erstellen. Dieser Trigger würde dann Cursor verwenden, um durch die INSERTED-Tabelle zu gehen und jeden Datensatz zu prüfen. Wenn ein Datensatz mit einem vorhandenen Datensatz in Konflikt steht, wird ein Fehler ausgelöst. Die zwei Probleme, die ich mit diesem Ansatz habe, sind: Ich mag nicht Cursor in einer modernen Version von SQL Server verwenden, und ich bin mir nicht sicher, wie man die gleiche Logik für UPDATEs zu implementieren. Es kann auch die Komplexität von Datensätzen innerhalb von INSERTED geben, die miteinander in Konflikt stehen.

Der zweite, scheinbar bessere Ansatz wäre, einen CONSTRAINT zu erstellen, der eine benutzerdefinierte Funktion aufruft, die die PatrolID, GuardID, Starts und Ends übergibt. Die Funktion würde dann eine WHERE EXISTS-Abfrage durchführen, die nach Datensätzen sucht, die sich mit den GuardID/Start/Ends-Parametern überschneiden, die nicht dem ursprünglichen PatrolID-Datensatz entsprechen. Ich bin mir jedoch nicht sicher, welche möglichen Nebenwirkungen dieser Ansatz haben könnte.

Ist der zweite Ansatz besser? Sieht jemand irgendwelche Fallstricke, wie zum Beispiel beim Einfügen/Aktualisieren mehrerer Zeilen gleichzeitig (hier bin ich besorgt, weil Zeilen innerhalb dieser Gruppe einen Konflikt verursachen könnten, was bedeutet, dass die Reihenfolge, in der sie "eingefügt" werden, einen Unterschied macht). Gibt es eine bessere Möglichkeit, dies zu tun (z. B. einen ausgefallenen INDEX-Trick?)

Antwort

5

ein nach Trigger verwenden um zu überprüfen, dass die Überlappung Einschränkung verletzt wurde nicht:

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 

HINWEIS: innerhalb eines Triggers eine Transaktion Rollback wird die Partie beenden. Im Gegensatz zu einer normalen Contraint-Verletzung können Sie den Fehler nicht abfangen.

Möglicherweise möchten Sie eine andere where-Klausel, je nachdem, wie Sie den Zeitbereich und die Überlappung definieren. Wenn Sie zum Beispiel sagen können, dass die Wache 1 von 6:00 Uhr bis 7:00 Uhr auf X steht, dann würde Y 7:00 Uhr bis 8:00 Uhr das oben Gesagte nicht erlauben.Sie würden wollen, stattdessen:

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 

Wo beginnt die Zeit ist, die Bewachung beginnt und endet, ist das unendlich Moment nach dem Ende Bewachung.

3

Der einfachste Weg wäre, eine gespeicherte Prozedur für die Einsätze zu verwenden. Die gespeicherte Prozedur kann die Einfügung in einer einzigen Anweisung ausführen:

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 

Meiner Erfahrung nach werden Trigger und Einschränkungen mit UDFs sehr komplex. Sie haben Nebenwirkungen, die viel Debuggen erfordern, um herauszufinden.

Gespeicherte Prozeduren funktionieren nur, und sie haben den zusätzlichen Vorteil, dass Sie INSERT-Berechtigungen für Clients verweigern können, was Ihnen eine feinkörnige Kontrolle darüber gibt, was in Ihre Datenbank eintritt.

+0

Trigger sind für diese Art der Sache notwendig, da Aufzeichnungen von anderen Orten als der proc eingesetzt werden können. Wenn Sie die Trigger-Ebene nicht erzwingen, können Sie die Datenintegrität nicht garantieren. – HLGEM

+3

Nun, deshalb bekommt niemand INSERT-Rechte. – 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 
Verwandte Themen