Domanda

Sto cercando il modo migliore per aggiungere un vincolo a una tabella che sia effettivamente un indice univoco sulla relazione tra il record e il resto dei record in quella tabella.

Immagina la seguente tabella che descrive le pattuglie di varie guardie (dal precedente scenario del guardiano)

PK  PatrolID Integer
FK  GuardID  Integer
    Starts   DateTime
    Ends     DateTime

Iniziamo con un vincolo che specifica che gli orari di inizio e fine devono essere logici:

Ends >= Starts

Tuttavia, voglio aggiungere un altro vincolo logico: una protezione specifica (GuardID) non può trovarsi contemporaneamente in due posizioni, il che significa che per qualsiasi record il periodo specificato da Inizio / Fine non deve sovrapporsi al periodo definito per nessun altro pattugliamento della stessa guardia.

Posso pensare a due modi per provare ad avvicinarmi a questo:

Crea un trigger INSTEAD OF INSERT. Questo trigger utilizza quindi i cursori per passare attraverso la tabella INSERTED, controllando ogni record. Se un record fosse in conflitto con un record esistente, verrebbe generato un errore. I due problemi che ho con questo approccio sono: non mi piace usare i cursori in una versione moderna di SQL Server e non sono sicuro di come implementare la stessa logica per gli UPDATE. Potrebbe anche esserci la complessità dei record all'interno di INSERTED in conflitto tra loro.

Il secondo approccio, apparentemente migliore, sarebbe quello di creare un VINCITORE che chiama una funzione definita dall'utente, passando PatrolID, GuardID, Starts and Ends. La funzione eseguirà quindi una query WHERE EXISTS verificando eventuali record che si sovrappongono ai parametri GuardID / Starts / Ends che non sono il record PatrolID originale. Tuttavia, non sono sicuro di quali potenziali effetti collaterali potrebbe avere questo approccio.

Il secondo approccio è migliore? Qualcuno vede delle insidie, come quando si inseriscono / aggiornano più righe contemporaneamente (qui sono preoccupato perché le righe all'interno di quel gruppo potrebbero essere in conflitto, il che significa che l'ordine in cui sono "inserite" fa la differenza). C'è un modo migliore per farlo (come qualche trucco di fantasia INDEX?)

È stato utile?

Soluzione

Utilizzare un trigger after per verificare che il vincolo di sovrapposizione non sia stato violato:

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

NOTA: il rollback di una transazione all'interno di un trigger terminerà il batch. A differenza di una normale violazione del contraffatto, non sarai in grado di rilevare l'errore.

Potresti volere una clausola where diversa a seconda di come definisci l'intervallo di tempo e la sovrapposizione. Ad esempio, se vuoi essere in grado di dire che la Guardia n. 1 è su X dalle 6:00 alle 7:00, allora Y dalle 7:00 alle 8:00, quanto sopra non lo permetterebbe. Vorresti invece:

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

Dove inizia è il momento in cui inizia la guardia e Termina è il momento infinitesimale dopo la fine della guardia.

Altri suggerimenti

Il modo più semplice sarebbe usare una procedura memorizzata per gli inserti. La procedura memorizzata può eseguire l'inserimento in un'unica istruzione:

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

Nella mia esperienza, i trigger e i vincoli con l'UDF tendono a diventare molto complessi. Hanno effetti collaterali che possono richiedere molto debug per capire.

Le stored procedure funzionano e hanno l'ulteriore vantaggio di negare le autorizzazioni INSERT ai client, offrendo un controllo approfondito su ciò che entra nel database.

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 '%_@%_.__%' 
    BEGIN
            print 'Triggered Fired';
            Print 'Invalid Emaill....';
            ROLLBACK TRANSACTION
    END 

END
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top