2013-05-01 18 views
9

Ho due tavoli, vendita e prodotto. La vendita ha un vincolo di chiave esterna che fa riferimento al prodotto. La chiave esterna è stata creata WITH NOCHECK e immediatamente disabilitata dopo la creazione. Voglio abilitare e fidarmi del vincolo di chiave esterna. Abilitarlo funziona ma non riesco a farci affidamento.Come rendere affidabili i vincoli di chiave esterna?

domande simili su StackOverflow e vari blog indicano che l'esecuzione ALTER TABLE T WITH CHECK CHECK CONSTRAINT C dovrebbe tradursi in is_disabled=0 e is_not_trusted=0, ma is_not_trusted è sempre 1 per me. Che cosa sto facendo di sbagliato?

ho provato a mettere il codice di esempio su SQL Fiddle ma non piaceva i comandi "DBCC", ecco che è:

-- "_Scratch" is just a sandbox DB that I use for testing stuff. 
USE _Scratch 

CREATE TABLE dbo.Product 
(
    ProductKeyId INT PRIMARY KEY NOT NULL, 
    Description VARCHAR(40) NOT NULL 
) 

CREATE TABLE dbo.Sale 
(
    ProductKeyId INT NOT NULL, 
    SaleTime DATETIME NOT NULL, 
    Value MONEY NOT NULL 
) 

ALTER TABLE dbo.Sale WITH NOCHECK 
    ADD CONSTRAINT FK_Product_ProductKeyId FOREIGN KEY (ProductKeyId) 
    REFERENCES dbo.Product (ProductKeyId) NOT FOR REPLICATION; 

ALTER TABLE dbo.Sale NOCHECK CONSTRAINT FK_Product_ProductKeyId 

INSERT INTO dbo.Product VALUES (1, 'Food') 
INSERT INTO dbo.Sale VALUES (1, GETDATE(), 1.00) 

-- Check the disabled/trusted state 
SELECT name, is_disabled, is_not_trusted 
FROM sys.foreign_keys 
WHERE name = 'FK_Product_ProductKeyId' 

    -- name      is_disabled is_not_trusted 
    -- FK_Product_ProductKeyId 1   1 

-- Check the FK_Product_ProductKeyId constraint 
DBCC CHECKCONSTRAINTS('FK_Product_ProductKeyId') 

    -- DBCC execution completed. 
    -- If DBCC printed error messages, contact your system administrator. 

-- Check all constraints on Sale table 
DBCC CHECKCONSTRAINTS('Sale') 

    -- DBCC execution completed. 
    -- If DBCC printed error messages, contact your system administrator. 

-- Add the constraint and check existing data 
ALTER TABLE Sale WITH CHECK CHECK CONSTRAINT FK_Product_ProductKeyId 

-- Check the disabled/trusted state 
SELECT name, is_disabled, is_not_trusted 
FROM sys.foreign_keys 
WHERE name = 'FK_Product_ProductKeyId' 

    -- name      is_disabled is_not_trusted 
    -- FK_Product_ProductKeyId 0   1 

-- Check the FK_Product_ProductKeyId constraint 
DBCC CHECKCONSTRAINTS('FK_Product_ProductKeyId') 

    -- DBCC execution completed. 
    -- If DBCC printed error messages, contact your system administrator. 

-- Check all constraints on Sale table 
DBCC CHECKCONSTRAINTS('Sale') 

    -- DBCC execution completed. 
    -- If DBCC printed error messages, contact your system administrator. 

risposta

17

Sulla base delle vostre esempi, ho anche provato:

  • Eliminare e ricreare la chiave esterna.
  • Eliminare e ricreare la tabella.

Poi ho notato qualcosa nel comando:

NOT FOR REPLICATION 

Sembra Se un vincolo viene creato con NOT FOR REPLICATION, è sempre non attendibile.

Citando Books Online:

In alcuni casi, è desiderabile che l'attività dell'utente in una topologia di replica un trattamento diverso da attività dell'agente. Ad esempio, se una riga viene inserita da un utente nel server di pubblicazione e che inserisce soddisfa un vincolo di controllo sulla tabella, potrebbe non essere richiesto a applicare lo stesso vincolo quando la riga viene inserita da un agente di replica abbonato. L'opzione NOT FOR REPLICATION consente di specificare che i seguenti oggetti di database vengono trattati in modo diverso quando un agente di replica esegue un'operazione:

vincoli di chiave esterna

Il vincolo di chiave esterna non viene applicata quando un agente di replica esegue un'operazione di inserimento, aggiornamento o cancellazione.

sembra che l'impostazione è IS_NOT_TRUSTED rilevante solo per la replica influenzato da . Suppongo che finché il vincolo viene applicato al server su cui stai lavorando, dovrebbe andare bene. Così sono andato avanti e ha confermato che:

SELECT name, is_disabled, is_not_trusted 
FROM sys.foreign_keys 
WHERE name = 'FK_Product_ProductKeyId' 

name     is_disabled is_not_trusted 
FK_Product_ProductKeyId 0   1 

INSERT INTO dbo.Sale VALUES (2, GETDATE(), 1.00) 

Msg 547, Level 16, State 0, Line 1 
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Product_ProductKeyId". The conflict occurred in database "_Scratch", table "dbo.Product", column 'ProductKeyId'. 
The statement has been terminated. 

Se si vuole ancora vedere IS_NOT_TRUSTED = 0 per la pace della mente, proprio ricreare la chiave esterna, senza NOT FOR REPLICATION.

Nel caso in cui se vi stiate chiedendo, ho verificato lo stesso effetto anche sui vincoli CHECK.

+0

Louie, grazie per aver dedicato del tempo per fare i test, questa è un'ottima risposta, vorrei poterti dare più di un upvote :) Stavo cercando di ottenere i vincoli attendibili perché ho letto da qualche parte quelli non fidati può influire sul rendimento di alcune query. Ho ereditato il database e non lo replichiamo, ma tutto sembra essere "NOT FOR REPLICATION", che potrebbe essere un caso di "generalità speculativa". – WileCau

+0

@WileCau In realtà si ottiene un miglioramento nel tempo di esecuzione quando gli FK sono contrassegnati come non attendibili. Non sto affatto incoraggiando questa pratica. Tuttavia, è molto utile lo spostamento di grandi quantità di dati, ad esempio da staging sql a production sql. controlla qui per maggiori informazioni http://sqlblog.com/blogs/hugo_kornelis/archive/2007/03/29/can-you-trust-your-constraints.aspx – harsimranb

+0

@ Pathachiever11, hai ragione, il motivo per cui i vincoli sono stati disabilitati era perché alcune tabelle erano inizialmente popolate da un altro database coerente noto in modo da rallentare la migrazione dei dati. Dopo la migrazione iniziale, i vincoli avrebbero dovuto essere riabilitati per applicare la coerenza sui nuovi dati, ma sono stati dimenticati. – WileCau

Problemi correlati