Bagaimana batasan kunci asing dinonaktifkan sementara menggunakan T-SQL?

824

Apakah menonaktifkan dan mengaktifkan batasan kunci asing didukung di SQL Server? Atau satunya pilihan saya untuk dropkemudian kembalicreate kendala?

sinar
sumber
128
Untuk orang-orang yang bertanya "mengapa" saya ingin melakukan ini: Ini adalah untuk lingkungan pengujian di mana saya ingin dapat menghapus dan memuat data uji dari beberapa tabel tanpa perlu memelihara dan menentukan urutan bagaimana data dimuat. Integritas data tidak begitu penting untuk skenario ini.
Ray
8
Catatan - jika Anda ingin TRUNCATE tabel, Anda harus benar-benar menghilangkan batasan.
OutstandingBill
@Luar BiasaBill Ternyata, ini berfungsi untuk TRUNCATE .
jpaugh
1
Tampaknya aneh bahwa siapa pun akan mempertanyakannya dalam lingkungan produksi . Kasus penggunaan yang sangat umum adalah untuk sisipan massal. Jika Anda memiliki tabel referensi-sendiri, kadang-kadang sangat sulit untuk menyortir sisipan massal sehingga baris induk selalu dimasukkan sebelum anak, jadi Anda menonaktifkan kendala, masukkan massal, aktifkan kendala.
Auspex

Jawaban:

1126

Jika Anda ingin menonaktifkan semua kendala dalam database, jalankan saja kode ini:

-- disable all constraints
EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"

Untuk mengaktifkannya kembali, jalankan: (cetak adalah opsional tentu saja dan itu hanya daftar tabel)

-- enable all constraints
exec sp_MSforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

Saya merasa berguna saat mengisi data dari satu basis data ke basis data lainnya. Ini adalah pendekatan yang jauh lebih baik daripada menjatuhkan batasan. Seperti yang Anda sebutkan itu berguna ketika menjatuhkan semua data dalam database dan mengisi kembali itu (katakanlah di lingkungan pengujian).

Jika Anda menghapus semua data, Anda mungkin menemukan solusi ini sangat membantu.

Terkadang juga berguna untuk menonaktifkan semua pemicu, Anda dapat melihat solusi lengkapnya di sini .

kristof
sumber
9
"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Haruskah hanya ada satu "PERIKSA" di sana?
CrazyPyro
27
@CrazyPyro - tidak, Anda perlu keduanya
kristof
32
@CrazyPyro: keduanya memang diperlukan, alasan untuk itu adalah karena PERIKSA pertama milik DENGAN dan PERIKSA kedua dengan CONSTRAINT (ini adalah jenis kendala). PERIKSA pertama memastikan data Anda diperiksa untuk konsistensi saat mengaktifkan kendala. Jika Anda tidak menginginkannya, Anda dapat menulis DENGAN NOCHECK. Dapat berguna dalam situasi pengujian tertentu ketika Anda tidak peduli dengan data aktual, asalkan ada beberapa sehingga kueri Anda memiliki sesuatu untuk dimainkan.
Valentino Vranken
8
Apakah buruk saya mendapatkan hasil ini pada perintah kedua? "Pernyataan ALTER TABLE bertentangan dengan batasan KUNCI ASING ..."
Pengacara Setan
34
Perlu dicatat bahwa meskipun dengan kendala dinonaktifkan, TRUNCATE TABLE tidak akan berfungsi. Untuk itu, Anda perlu menghilangkan kendala. Jika tidak, gunakan DELETE FROM, tetapi pertimbangkan perbedaannya: mssqltips.com/sqlservertip/1080/…
James McCormack
404

http://www.sqljunkies.com/WebLog/roman/archive/2005/01/30/7037.aspx

-- Disable all table constraints

ALTER TABLE MyTable NOCHECK CONSTRAINT ALL

-- Enable all table constraints

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT ALL

-- Disable single constraint

ALTER TABLE MyTable NOCHECK CONSTRAINT MyConstraint

-- Enable single constraint

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint
ScottStonehouse
sumber
29
Temuan yang bagus, tetapi perhatikan bahwa Anda masih tidak dapat memotong tabel tanpa menghilangkan batasan kunci asing
Steven A. Lowe
1
dan Anda juga perlu menyadari bahwa ketika Anda mengaktifkan kembali kendala dan melakukan pemeriksaan integritas data, data Anda mungkin gagal dan memperbaiki masalah seperti itu bisa menjadi mimpi buruk jika data yang gagal berada di akhir rangkaian panjang kendala terkait.
Jimoc
1
Anda juga perlu pemeriksaan kedua saat menyalakan kembali kendala. Jika tidak, apa adanya, kode Anda hanya akan memeriksa kendala satu kali, bukan menyalakannya.
ps2goat
3
ya, "dengan cek cek" diperlukan untuk 2012. Suntingan ditolak? MS Link
crokusek
1
Pernyataan yang digunakan di sini untuk mengaktifkan kembali kendala tidak ada klausa WITH CHECK. Ini adalah kelemahan yang cukup besar. Lihat jawaban saya untuk detailnya - stackoverflow.com/a/35427150/81595 .
Scott Munro
37

Untuk menonaktifkan batasan Anda memiliki ALTERtabel menggunakan NOCHECK

ALTER TABLE [TABLE_NAME] NOCHECK CONSTRAINT [ALL|CONSTRAINT_NAME]

Untuk memungkinkan Anda harus menggunakan LIHAT ganda :

ALTER TABLE [TABLE_NAME] WITH CHECK CHECK CONSTRAINT [ALL|CONSTRAINT_NAME]
  • Perhatikan CHECK CHECK ganda saat mengaktifkan.
  • SEMUA berarti untuk semua kendala dalam tabel.

Setelah selesai, jika Anda perlu memeriksa statusnya, gunakan skrip ini untuk mendaftar status kendala. Akan sangat membantu:

    SELECT (CASE 
        WHEN OBJECTPROPERTY(CONSTID, 'CNSTISDISABLED') = 0 THEN 'ENABLED'
        ELSE 'DISABLED'
        END) AS STATUS,
        OBJECT_NAME(CONSTID) AS CONSTRAINT_NAME,
        OBJECT_NAME(FKEYID) AS TABLE_NAME,
        COL_NAME(FKEYID, FKEY) AS COLUMN_NAME,
        OBJECT_NAME(RKEYID) AS REFERENCED_TABLE_NAME,
        COL_NAME(RKEYID, RKEY) AS REFERENCED_COLUMN_NAME
   FROM SYSFOREIGNKEYS
ORDER BY TABLE_NAME, CONSTRAINT_NAME,REFERENCED_TABLE_NAME, KEYNO 
Diego Mendes
sumber
Tidak melihat untuk kunci primer ? Untuk kunci asing SYSFOREIGNKEYS System View sys.sysforeignkeys msdn.microsoft.com/en-us/library/ms177604.aspx
Kiquenet
jika Anda mencoba untuk menonaktifkan kunci primer untuk disisipkan, saya akan merekomendasikan menggunakan (SET IDENTITY_INSERT) jika Anda ingin hanya memeriksa kunci primer, Anda dapat mencoba sys.key_constraints dengan sys.indexes.is_primary_key
Diego Mendes
29

Pilihan terbaik Anda adalah MENGURANGI dan MENCIPTAKAN batasan kunci asing.

Saya tidak menemukan contoh di pos ini yang akan berfungsi untuk saya "apa adanya", yang satu tidak akan berfungsi jika kunci asing merujuk skema yang berbeda, yang lain tidak akan berfungsi jika kunci asing merujuk beberapa kolom. Skrip ini mempertimbangkan baik, beberapa skema dan beberapa kolom per kunci asing.

Berikut ini skrip yang menghasilkan pernyataan "ADD CONSTRAINT", untuk beberapa kolom ia akan memisahkannya dengan koma ( pastikan untuk menyimpan hasil ini sebelum menjalankan pernyataan DROP ):

PRINT N'-- CREATE FOREIGN KEY CONSTRAINTS --';

SET NOCOUNT ON;
SELECT '
PRINT N''Creating '+ const.const_name +'...''
GO
ALTER TABLE ' + const.parent_obj + '
    ADD CONSTRAINT ' + const.const_name + ' FOREIGN KEY (
            ' + const.parent_col_csv + '
            ) REFERENCES ' + const.ref_obj + '(' + const.ref_col_csv + ')
GO'
FROM (
    SELECT QUOTENAME(fk.NAME) AS [const_name]
        ,QUOTENAME(schParent.NAME) + '.' + QUOTENAME(OBJECT_name(fkc.parent_object_id)) AS [parent_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcP.parent_object_id, fcp.parent_column_id))
                FROM sys.foreign_key_columns AS fcP
                WHERE fcp.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [parent_col_csv]
        ,QUOTENAME(schRef.NAME) + '.' + QUOTENAME(OBJECT_NAME(fkc.referenced_object_id)) AS [ref_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcR.referenced_object_id, fcR.referenced_column_id))
                FROM sys.foreign_key_columns AS fcR
                WHERE fcR.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [ref_col_csv]
    FROM sys.foreign_key_columns AS fkc
    INNER JOIN sys.foreign_keys AS fk ON fk.object_id = fkc.constraint_object_id
    INNER JOIN sys.objects AS oParent ON oParent.object_id = fkc.parent_object_id
    INNER JOIN sys.schemas AS schParent ON schParent.schema_id = oParent.schema_id
    INNER JOIN sys.objects AS oRef ON oRef.object_id = fkc.referenced_object_id
    INNER JOIN sys.schemas AS schRef ON schRef.schema_id = oRef.schema_id
    GROUP BY fkc.parent_object_id
        ,fkc.referenced_object_id
        ,fk.NAME
        ,fk.object_id
        ,schParent.NAME
        ,schRef.NAME
    ) AS const
ORDER BY const.const_name

Berikut ini skrip yang menghasilkan pernyataan "DROP CONSTRAINT":

PRINT N'-- DROP FOREIGN KEY CONSTRAINTS --';

SET NOCOUNT ON;

SELECT '
PRINT N''Dropping ' + fk.NAME + '...''
GO
ALTER TABLE [' + sch.NAME + '].[' + OBJECT_NAME(fk.parent_object_id) + ']' + ' DROP  CONSTRAINT ' + '[' + fk.NAME + ']
GO'
FROM sys.foreign_keys AS fk
INNER JOIN sys.schemas AS sch ON sch.schema_id = fk.schema_id
ORDER BY fk.NAME
vic
sumber
5
Bisakah Anda menjelaskan mengapa lebih baik daripada menonaktifkan dan mengaktifkan kembali kendala?
Mahmoodvcs
Naskah yang bagus. Untuk pendekatan yang serupa tetapi alternatif, lihat: mssqltips.com/sqlservertip/3347/…
Matt Browne
11

Standar SQL-92 memungkinkan untuk pengekang untuk dinyatakan sebagai DAPAT DITERIMA sehingga dapat ditunda (secara implisit atau eksplisit) dalam lingkup transaksi. Sayangnya, SQL Server masih kehilangan fungsionalitas SQL-92 ini.

Bagi saya, mengubah batasan untuk NOCHECK sama dengan mengubah struktur database dengan cepat - menjatuhkan batasan tentu saja - dan sesuatu yang harus dihindari (misalnya pengguna memerlukan peningkatan hak istimewa).

suatu hari nanti
sumber
11
   --Drop and Recreate Foreign Key Constraints

SET NOCOUNT ON

DECLARE @table TABLE(
   RowId INT PRIMARY KEY IDENTITY(1, 1),
   ForeignKeyConstraintName NVARCHAR(200),
   ForeignKeyConstraintTableSchema NVARCHAR(200),
   ForeignKeyConstraintTableName NVARCHAR(200),
   ForeignKeyConstraintColumnName NVARCHAR(200),
   PrimaryKeyConstraintName NVARCHAR(200),
   PrimaryKeyConstraintTableSchema NVARCHAR(200),
   PrimaryKeyConstraintTableName NVARCHAR(200),
   PrimaryKeyConstraintColumnName NVARCHAR(200)    
)

INSERT INTO @table(ForeignKeyConstraintName, ForeignKeyConstraintTableSchema, ForeignKeyConstraintTableName, ForeignKeyConstraintColumnName)
SELECT 
   U.CONSTRAINT_NAME, 
   U.TABLE_SCHEMA, 
   U.TABLE_NAME, 
   U.COLUMN_NAME 
FROM 
   INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
         ON U.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
   C.CONSTRAINT_TYPE = 'FOREIGN KEY'

UPDATE @table SET
   PrimaryKeyConstraintName = UNIQUE_CONSTRAINT_NAME
FROM 
   @table T
      INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
         ON T.ForeignKeyConstraintName = R.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintTableSchema  = TABLE_SCHEMA,
   PrimaryKeyConstraintTableName  = TABLE_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
      ON T.PrimaryKeyConstraintName = C.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintColumnName = COLUMN_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      ON T.PrimaryKeyConstraintName = U.CONSTRAINT_NAME

--SELECT * FROM @table

--DROP CONSTRAINT:
SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   DROP CONSTRAINT ' + ForeignKeyConstraintName + '

   GO'
FROM
   @table

--ADD CONSTRAINT:
SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   ADD CONSTRAINT ' + ForeignKeyConstraintName + ' FOREIGN KEY(' + ForeignKeyConstraintColumnName + ') REFERENCES [' + PrimaryKeyConstraintTableSchema + '].[' + PrimaryKeyConstraintTableName + '](' + PrimaryKeyConstraintColumnName + ')

   GO'
FROM
   @table

GO

Saya setuju dengan Anda, Hamlin. Ketika Anda mentransfer data menggunakan SSIS atau ketika ingin mereplikasi data, tampaknya cukup perlu untuk sementara menonaktifkan atau menghapus batasan kunci asing dan kemudian mengaktifkan kembali atau membuat ulang mereka. Dalam kasus ini, integritas referensial bukan merupakan masalah, karena sudah dipelihara dalam database sumber. Karena itu, Anda dapat yakin tentang masalah ini.

Amir Hussein Samiani
sumber
Skrip ini bagus untuk menghasilkan perintah "ALTER" saya, tetapi bagaimana saya bisa menjalankannya dalam SP?
BlueChippy
1
Saya pikir ini tidak akan berhasil jika salah satu kunci asing multi-kolom
Zar Shardan
Ini juga tidak menghasilkan semua karakter untuk nama tabel / kunci yang sangat panjang.
Joshua Drake
11
SET NOCOUNT ON

DECLARE @table TABLE(
   RowId INT PRIMARY KEY IDENTITY(1, 1),
   ForeignKeyConstraintName NVARCHAR(200),
   ForeignKeyConstraintTableSchema NVARCHAR(200),
   ForeignKeyConstraintTableName NVARCHAR(200),
   ForeignKeyConstraintColumnName NVARCHAR(200),
   PrimaryKeyConstraintName NVARCHAR(200),
   PrimaryKeyConstraintTableSchema NVARCHAR(200),
   PrimaryKeyConstraintTableName NVARCHAR(200),
   PrimaryKeyConstraintColumnName NVARCHAR(200),
   UpdateRule NVARCHAR(100),
   DeleteRule NVARCHAR(100)   
)

INSERT INTO @table(ForeignKeyConstraintName, ForeignKeyConstraintTableSchema, ForeignKeyConstraintTableName, ForeignKeyConstraintColumnName)
SELECT 
   U.CONSTRAINT_NAME, 
   U.TABLE_SCHEMA, 
   U.TABLE_NAME, 
   U.COLUMN_NAME
FROM 
   INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
         ON U.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
   C.CONSTRAINT_TYPE = 'FOREIGN KEY'

UPDATE @table SET
   T.PrimaryKeyConstraintName = R.UNIQUE_CONSTRAINT_NAME,
   T.UpdateRule = R.UPDATE_RULE,
   T.DeleteRule = R.DELETE_RULE
FROM 
   @table T
      INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
         ON T.ForeignKeyConstraintName = R.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintTableSchema  = TABLE_SCHEMA,
   PrimaryKeyConstraintTableName  = TABLE_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
      ON T.PrimaryKeyConstraintName = C.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintColumnName = COLUMN_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      ON T.PrimaryKeyConstraintName = U.CONSTRAINT_NAME

--SELECT * FROM @table

SELECT '
BEGIN TRANSACTION
BEGIN TRY'

--DROP CONSTRAINT:
SELECT
   '
 ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
 DROP CONSTRAINT ' + ForeignKeyConstraintName + '
   '
FROM
   @table

SELECT '
END TRY

BEGIN CATCH
   ROLLBACK TRANSACTION
   RAISERROR(''Operation failed.'', 16, 1)
END CATCH

IF(@@TRANCOUNT != 0)
BEGIN
   COMMIT TRANSACTION
   RAISERROR(''Operation completed successfully.'', 10, 1)
END
'

--ADD CONSTRAINT:
SELECT '
BEGIN TRANSACTION
BEGIN TRY'

SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   ADD CONSTRAINT ' + ForeignKeyConstraintName + ' FOREIGN KEY(' + ForeignKeyConstraintColumnName + ') REFERENCES [' + PrimaryKeyConstraintTableSchema + '].[' + PrimaryKeyConstraintTableName + '](' + PrimaryKeyConstraintColumnName + ') ON UPDATE ' + UpdateRule + ' ON DELETE ' + DeleteRule + '
   '
FROM
   @table

SELECT '
END TRY

BEGIN CATCH
   ROLLBACK TRANSACTION
   RAISERROR(''Operation failed.'', 16, 1)
END CATCH

IF(@@TRANCOUNT != 0)
BEGIN
   COMMIT TRANSACTION
   RAISERROR(''Operation completed successfully.'', 10, 1)
END'

GO
Amir Hussein Samiani
sumber
10

WITH CHECK CHECK hampir pasti diperlukan!

Poin ini dimunculkan dalam beberapa jawaban dan komentar tetapi saya merasa cukup penting untuk menyebutnya lagi.

Mengaktifkan kembali kendala menggunakan perintah berikut (tidak WITH CHECK) akan memiliki beberapa kelemahan serius .

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

DENGAN CHECK | DENGAN NOCHECK

Menentukan apakah data dalam tabel adalah atau tidak divalidasi terhadap KUNCI LUAR NEGERI yang baru ditambahkan atau diaktifkan kembali atau PERIKSA kendala. Jika tidak ditentukan, WITH CHECK diasumsikan untuk kendala baru, dan WITH NOCHECK diasumsikan untuk kendala yang diaktifkan kembali.

Jika Anda tidak ingin memverifikasi PERIKSA baru atau kendala KUNCI ASING terhadap data yang ada, gunakan DENGAN NOCHECK. Kami tidak menyarankan melakukan ini, kecuali dalam kasus yang jarang terjadi. Kendala baru akan dievaluasi di semua pembaruan data nanti. Pelanggaran kendala apa pun yang ditekan oleh WITH NOCHECK saat kendala ditambahkan dapat menyebabkan pembaruan di masa mendatang gagal jika pembaruan baris dengan data yang tidak sesuai dengan kendala.

Pengoptimal kueri tidak mempertimbangkan kendala yang didefinisikan DENGAN NOCHECK. Kendala tersebut diabaikan sampai diaktifkan kembali dengan menggunakan tabel ALTER TABLE WITH CHECK CHECK CONSTRAINT ALL.

Catatan: DENGAN NOCHECK adalah standar untuk kendala yang mengaktifkan kembali. Saya harus bertanya-tanya mengapa ...

  1. Tidak ada data yang ada di tabel akan dievaluasi selama pelaksanaan perintah ini - penyelesaian yang sukses tidak ada jaminan bahwa data dalam tabel ini valid sesuai dengan kendala.
  2. Selama pembaruan berikutnya dari catatan yang tidak valid, kendala akan dievaluasi dan akan gagal - menghasilkan kesalahan yang mungkin tidak terkait dengan pembaruan aktual yang dibuat.
  3. Logika aplikasi yang bergantung pada batasan untuk memastikan bahwa data yang valid mungkin gagal.
  4. Pengoptimal kueri tidak akan menggunakan kendala apa pun yang diaktifkan dengan cara ini.

The sys.foreign_keys tampilan sistem menyediakan beberapa visibilitas ke masalah ini. Perhatikan bahwa ia memiliki kolom is_disabledan dan is_not_trusted. is_disabledmenunjukkan apakah operasi manipulasi data di masa depan akan divalidasi terhadap kendala. is_not_trustedmenunjukkan apakah semua data saat ini dalam tabel telah divalidasi terhadap kendala.

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint;

Apakah kendala Anda bisa dipercaya? Temukan...

SELECT * FROM sys.foreign_keys WHERE is_not_trusted = 1;
Scott Munro
sumber
9

Pos pertama :)

Untuk OP, solusi kristof akan bekerja, kecuali ada masalah dengan data besar-besaran dan masalah balon transaksi pada penghapusan besar. Juga, bahkan dengan penyimpanan tlog untuk cadangan, karena menghapus menulis ke tlog, operasi dapat memakan waktu SANGAT lama untuk tabel dengan ratusan juta baris.

Saya menggunakan serangkaian kursor untuk memotong dan memuat ulang salinan besar dari salah satu basis data produksi kami yang besar. Akun yang direkayasa solusi untuk beberapa skema, beberapa kolom kunci asing, dan yang terbaik dari semuanya dapat digunakan untuk digunakan dalam SSIS.

Ini melibatkan pembuatan tiga tabel pementasan (tabel nyata) untuk menampung DROP, CREATE, dan PERIKSA skrip FK, pembuatan dan penyisipan skrip tersebut ke dalam tabel, dan kemudian pengulangan di atas tabel dan menjalankannya. Skrip terlampir adalah empat bagian: 1.) pembuatan dan penyimpanan skrip dalam tiga tabel (nyata) staging, 2.) pelaksanaan skrip drop FK melalui kursor satu per satu, 3.) Menggunakan sp_MSselalu dapat digunakan untuk memotong semua skrip tabel dalam database selain dari tiga tabel pementasan kami dan 4.) pelaksanaan pembuatan FK dan periksa skrip FK di akhir paket ETL SSIS Anda.

Jalankan bagian pembuatan skrip dalam tugas Jalankan SQL di SSIS. Jalankan bagian "eksekusi Drop FK Scripts" di tugas kedua Jalankan SQL. Letakkan skrip pemotongan dalam tugas ketiga Jalankan SQL, kemudian lakukan proses ETL lain yang perlu Anda lakukan sebelum melampirkan skrip CREATE dan PERIKSA dalam tugas akhir Jalankan SQL (atau dua jika diinginkan) di akhir alur kontrol Anda.

Penyimpanan skrip di tabel nyata telah terbukti sangat berharga ketika aplikasi ulang kunci asing gagal karena Anda dapat memilih * dari sync_CreateFK, salin / tempel ke jendela kueri Anda, jalankan satu per satu, dan perbaiki masalah data begitu Anda temukan yang gagal / masih gagal mendaftar ulang.

Jangan menjalankan kembali skrip jika gagal tanpa memastikan bahwa Anda menerapkan kembali semua kunci asing / pemeriksaan sebelum melakukannya, atau Anda kemungkinan besar akan kehilangan beberapa kreasi dan memeriksa skrip fk karena tabel pementasan kami dihapus dan diciptakan kembali sebelum pembuatan skrip untuk dieksekusi.

----------------------------------------------------------------------------
1)
/*
Author:         Denmach
DateCreated:    2014-04-23
Purpose:        Generates SQL statements to DROP, ADD, and CHECK existing constraints for a 
                database.  Stores scripts in tables on target database for execution.  Executes
                those stored scripts via independent cursors. 
DateModified:
ModifiedBy
Comments:       This will eliminate deletes and the T-log ballooning associated with it.
*/

DECLARE @schema_name SYSNAME; 
DECLARE @table_name SYSNAME; 
DECLARE @constraint_name SYSNAME; 
DECLARE @constraint_object_id INT; 
DECLARE @referenced_object_name SYSNAME; 
DECLARE @is_disabled BIT; 
DECLARE @is_not_for_replication BIT; 
DECLARE @is_not_trusted BIT; 
DECLARE @delete_referential_action TINYINT; 
DECLARE @update_referential_action TINYINT; 
DECLARE @tsql NVARCHAR(4000); 
DECLARE @tsql2 NVARCHAR(4000); 
DECLARE @fkCol SYSNAME; 
DECLARE @pkCol SYSNAME; 
DECLARE @col1 BIT; 
DECLARE @action CHAR(6);  
DECLARE @referenced_schema_name SYSNAME;



--------------------------------Generate scripts to drop all foreign keys in a database --------------------------------

IF OBJECT_ID('dbo.sync_dropFK') IS NOT NULL
DROP TABLE sync_dropFK

CREATE TABLE sync_dropFK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )

DECLARE FKcursor CURSOR FOR

    SELECT 
        OBJECT_SCHEMA_NAME(parent_object_id)
        , OBJECT_NAME(parent_object_id)
        , name
    FROM 
        sys.foreign_keys WITH (NOLOCK)
    ORDER BY 
        1,2;

OPEN FKcursor;

FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name

WHILE @@FETCH_STATUS = 0

BEGIN
        SET @tsql = 'ALTER TABLE '
                + QUOTENAME(@schema_name) 
                + '.' 
                + QUOTENAME(@table_name)
                + ' DROP CONSTRAINT ' 
                + QUOTENAME(@constraint_name) 
                + ';';
    --PRINT @tsql;
    INSERT sync_dropFK  (
                        Script
                        )
                        VALUES (
                                @tsql
                                )   

    FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name
    ;

END;

CLOSE FKcursor;

DEALLOCATE FKcursor;


---------------Generate scripts to create all existing foreign keys in a database --------------------------------
----------------------------------------------------------------------------------------------------------
IF OBJECT_ID('dbo.sync_createFK') IS NOT NULL
DROP TABLE sync_createFK

CREATE TABLE sync_createFK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )

IF OBJECT_ID('dbo.sync_createCHECK') IS NOT NULL
DROP TABLE sync_createCHECK

CREATE TABLE sync_createCHECK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )   

DECLARE FKcursor CURSOR FOR

     SELECT 
        OBJECT_SCHEMA_NAME(parent_object_id)
        , OBJECT_NAME(parent_object_id)
        , name
        , OBJECT_NAME(referenced_object_id)
        , OBJECT_ID
        , is_disabled
        , is_not_for_replication
        , is_not_trusted
        , delete_referential_action
        , update_referential_action
        , OBJECT_SCHEMA_NAME(referenced_object_id)

    FROM 
        sys.foreign_keys WITH (NOLOCK)

    ORDER BY 
        1,2;

OPEN FKcursor;

FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name
    , @referenced_object_name
    , @constraint_object_id
    , @is_disabled
    , @is_not_for_replication
    , @is_not_trusted
    , @delete_referential_action
    , @update_referential_action
    , @referenced_schema_name;

WHILE @@FETCH_STATUS = 0

BEGIN

        BEGIN
            SET @tsql = 'ALTER TABLE '
                        + QUOTENAME(@schema_name) 
                        + '.' 
                        + QUOTENAME(@table_name)
                        +   CASE 
                                @is_not_trusted
                                WHEN 0 THEN ' WITH CHECK '
                                ELSE ' WITH NOCHECK '
                            END
                        + ' ADD CONSTRAINT ' 
                        + QUOTENAME(@constraint_name)
                        + ' FOREIGN KEY (';

        SET @tsql2 = '';

        DECLARE ColumnCursor CURSOR FOR 

            SELECT 
                COL_NAME(fk.parent_object_id
                , fkc.parent_column_id)
                , COL_NAME(fk.referenced_object_id
                , fkc.referenced_column_id)

            FROM 
                sys.foreign_keys fk WITH (NOLOCK)
                INNER JOIN sys.foreign_key_columns fkc WITH (NOLOCK) ON fk.[object_id] = fkc.constraint_object_id

            WHERE 
                fkc.constraint_object_id = @constraint_object_id

            ORDER BY 
                fkc.constraint_column_id;

        OPEN ColumnCursor;

        SET @col1 = 1;

        FETCH NEXT FROM ColumnCursor INTO @fkCol, @pkCol;

        WHILE @@FETCH_STATUS = 0

        BEGIN
            IF (@col1 = 1)
                SET @col1 = 0;
            ELSE
            BEGIN
                SET @tsql = @tsql + ',';
                SET @tsql2 = @tsql2 + ',';
            END;

            SET @tsql = @tsql + QUOTENAME(@fkCol);
            SET @tsql2 = @tsql2 + QUOTENAME(@pkCol);
            --PRINT '@tsql = ' + @tsql 
            --PRINT '@tsql2 = ' + @tsql2
            FETCH NEXT FROM ColumnCursor INTO @fkCol, @pkCol;
            --PRINT 'FK Column ' + @fkCol
            --PRINT 'PK Column ' + @pkCol 
        END;

        CLOSE ColumnCursor;
        DEALLOCATE ColumnCursor;

        SET @tsql = @tsql + ' ) REFERENCES ' 
                    + QUOTENAME(@referenced_schema_name) 
                    + '.' 
                    + QUOTENAME(@referenced_object_name)
                    + ' (' 
                    + @tsql2 + ')';

        SET @tsql = @tsql
                    + ' ON UPDATE ' 
                    + 
                        CASE @update_referential_action
                            WHEN 0 THEN 'NO ACTION '
                            WHEN 1 THEN 'CASCADE '
                            WHEN 2 THEN 'SET NULL '
                                ELSE 'SET DEFAULT '
                        END

                    + ' ON DELETE ' 
                    + 
                        CASE @delete_referential_action
                            WHEN 0 THEN 'NO ACTION '
                            WHEN 1 THEN 'CASCADE '
                            WHEN 2 THEN 'SET NULL '
                                ELSE 'SET DEFAULT '
                        END

                    + 
                    CASE @is_not_for_replication
                        WHEN 1 THEN ' NOT FOR REPLICATION '
                            ELSE ''
                    END
                    + ';';

        END;

    --  PRINT @tsql
        INSERT sync_createFK    
                        (
                        Script
                        )
                        VALUES (
                                @tsql
                                )

-------------------Generate CHECK CONSTRAINT scripts for a database ------------------------------
----------------------------------------------------------------------------------------------------------

        BEGIN

        SET @tsql = 'ALTER TABLE '
                    + QUOTENAME(@schema_name) 
                    + '.' 
                    + QUOTENAME(@table_name)
                    + 
                        CASE @is_disabled
                            WHEN 0 THEN ' CHECK '
                                ELSE ' NOCHECK '
                        END
                    + 'CONSTRAINT ' 
                    + QUOTENAME(@constraint_name)
                    + ';';
        --PRINT @tsql;
        INSERT sync_createCHECK 
                        (
                        Script
                        )
                        VALUES (
                                @tsql
                                )   
        END;

    FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name
    , @referenced_object_name
    , @constraint_object_id
    , @is_disabled
    , @is_not_for_replication
    , @is_not_trusted
    , @delete_referential_action
    , @update_referential_action
    , @referenced_schema_name;

END;

CLOSE FKcursor;

DEALLOCATE FKcursor;

--SELECT * FROM sync_DropFK
--SELECT * FROM sync_CreateFK
--SELECT * FROM sync_CreateCHECK
---------------------------------------------------------------------------
2.)
-----------------------------------------------------------------------------------------------------------------
----------------------------execute Drop FK Scripts --------------------------------------------------

DECLARE @scriptD NVARCHAR(4000)

DECLARE DropFKCursor CURSOR FOR
    SELECT Script 
    FROM sync_dropFK WITH (NOLOCK)

OPEN DropFKCursor

FETCH NEXT FROM DropFKCursor
INTO @scriptD

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptD
EXEC (@scriptD)
FETCH NEXT FROM DropFKCursor
INTO @scriptD
END
CLOSE DropFKCursor
DEALLOCATE DropFKCursor
--------------------------------------------------------------------------------
3.) 

------------------------------------------------------------------------------------------------------------------
----------------------------Truncate all tables in the database other than our staging tables --------------------
------------------------------------------------------------------------------------------------------------------


EXEC sp_MSforeachtable 'IF OBJECT_ID(''?'') NOT IN 
(
ISNULL(OBJECT_ID(''dbo.sync_createCHECK''),0),
ISNULL(OBJECT_ID(''dbo.sync_createFK''),0),
ISNULL(OBJECT_ID(''dbo.sync_dropFK''),0)
)
BEGIN TRY
 TRUNCATE TABLE ?
END TRY
BEGIN CATCH
 PRINT ''Truncation failed on''+ ? +''
END CATCH;' 
GO
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------
----------------------------execute Create FK Scripts and CHECK CONSTRAINT Scripts---------------
----------------------------tack me at the end of the ETL in a SQL task-------------------------
-------------------------------------------------------------------------------------------------
DECLARE @scriptC NVARCHAR(4000)

DECLARE CreateFKCursor CURSOR FOR
    SELECT Script 
    FROM sync_createFK WITH (NOLOCK)

OPEN CreateFKCursor

FETCH NEXT FROM CreateFKCursor
INTO @scriptC

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptC
EXEC (@scriptC)
FETCH NEXT FROM CreateFKCursor
INTO @scriptC
END
CLOSE CreateFKCursor
DEALLOCATE CreateFKCursor
-------------------------------------------------------------------------------------------------
DECLARE @scriptCh NVARCHAR(4000)

DECLARE CreateCHECKCursor CURSOR FOR
    SELECT Script 
    FROM sync_createCHECK WITH (NOLOCK)

OPEN CreateCHECKCursor

FETCH NEXT FROM CreateCHECKCursor
INTO @scriptCh

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptCh
EXEC (@scriptCh)
FETCH NEXT FROM CreateCHECKCursor
INTO @scriptCh
END
CLOSE CreateCHECKCursor
DEALLOCATE CreateCHECKCursor
Denmach
sumber
7

Temukan batasannya

SELECT * 
FROM sys.foreign_keys
WHERE referenced_object_id = object_id('TABLE_NAME')

Jalankan SQL yang dihasilkan oleh SQL ini

SELECT 
    'ALTER TABLE ' +  OBJECT_SCHEMA_NAME(parent_object_id) +
    '.[' + OBJECT_NAME(parent_object_id) + 
    '] DROP CONSTRAINT ' + name
FROM sys.foreign_keys
WHERE referenced_object_id = object_id('TABLE_NAME')

Jalan aman.

Catatan: Menambahkan solusi untuk menjatuhkan batasan sehingga tabel dapat dijatuhkan atau dimodifikasi tanpa kesalahan kendala.

Aditya
sumber
3

Klik kanan desain tabel dan pergi ke Hubungan dan pilih kunci asing di panel sisi kiri dan di panel sisi kanan, atur Terapkan batasan kunci asing ke 'Ya' (untuk mengaktifkan batasan kunci asing) atau 'Tidak' (untuk nonaktifkan). masukkan deskripsi gambar di sini

AmirHossein Manian
sumber
3

Jawaban bertanda '905' terlihat bagus tetapi tidak berfungsi.

Mengikuti berhasil bagi saya. Kunci Utama, Kunci Unik, atau batasan Default TIDAK DAPAT dinonaktifkan. Bahkan, jika 'sp_helpconstraint' 'menunjukkan' n / a 'di status_enabled - Berarti itu TIDAK dapat diaktifkan / dinonaktifkan.

- Untuk menghasilkan skrip ke DISABLE

select 'ALTER TABLE ' + object_name(id) + ' NOCHECK CONSTRAINT [' + object_name(constid) + ']'
from sys.sysconstraints 
where status & 0x4813 = 0x813 order by object_name(id)

- Untuk menghasilkan skrip ke AKTIFKAN

select 'ALTER TABLE ' + object_name(id) + ' CHECK CONSTRAINT [' + object_name(constid) + ']'
from sys.sysconstraints 
where status & 0x4813 = 0x813 order by object_name(id)
V. Agarwal
sumber
3

Anda sebenarnya harus dapat menonaktifkan batasan kunci asing dengan cara yang sama Anda menonaktifkan sementara kendala lain:

Alter table MyTable nocheck constraint FK_ForeignKeyConstraintName

Pastikan Anda menonaktifkan batasan pada tabel pertama yang tercantum dalam nama kendala. Misalnya, jika batasan kunci asing saya adalah FK_LocationsEmployeesLocationIdEmployeeId, saya ingin menggunakan yang berikut:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

meskipun melanggar batasan ini akan menghasilkan kesalahan yang tidak selalu menyatakan tabel itu sebagai sumber konflik.

lwilliams
sumber
1

Satu skrip untuk mengatur semuanya: ini menggabungkan perintah truncate dan delete dengan sp_MSforeachtable sehingga Anda dapat menghindari menjatuhkan dan menciptakan kembali kendala - cukup tentukan tabel yang perlu dihapus daripada terpotong dan untuk tujuan saya, saya telah menyertakan filter skema tambahan untuk selamanya mengukur (diuji pada 2008r2)

declare @schema nvarchar(max) = 'and Schema_Id=Schema_id(''Value'')'
declare @deletiontables nvarchar(max) = '(''TableA'',''TableB'')'
declare @truncateclause nvarchar(max) = @schema + ' and o.Name not in ' +  + @deletiontables;
declare @deleteclause nvarchar(max) = @schema + ' and o.Name in ' + @deletiontables;        

exec sp_MSforeachtable 'alter table ? nocheck constraint all', @whereand=@schema
exec sp_MSforeachtable 'truncate table ?', @whereand=@truncateclause
exec sp_MSforeachtable 'delete from ?', @whereand=@deleteclause
exec sp_MSforeachtable 'alter table ? with check check constraint all', @whereand=@schema
Alex Hinton
sumber
1

Anda dapat menonaktifkan sementara kendala pada tabel Anda, melakukan pekerjaan, lalu membangunnya kembali.

Berikut ini cara mudah untuk melakukannya ...

Nonaktifkan semua indeks, termasuk kunci utama, yang akan menonaktifkan semua kunci asing, lalu aktifkan kembali hanya kunci utama sehingga Anda dapat bekerja dengannya ...

DECLARE @sql AS NVARCHAR(max)=''
select @sql = @sql +
    'ALTER INDEX ALL ON [' + t.[name] + '] DISABLE;'+CHAR(13)
from  
    sys.tables t
where type='u'

select @sql = @sql +
    'ALTER INDEX ' + i.[name] + ' ON [' + t.[name] + '] REBUILD;'+CHAR(13)
from  
    sys.key_constraints i
join
    sys.tables t on i.parent_object_id=t.object_id
where
    i.type='PK'


exec dbo.sp_executesql @sql;
go

[Lakukan sesuatu, seperti memuat data]

Kemudian aktifkan kembali dan bangun kembali indeks ...

DECLARE @sql AS NVARCHAR(max)=''
select @sql = @sql +
    'ALTER INDEX ALL ON [' + t.[name] + '] REBUILD;'+CHAR(13)
from  
    sys.tables t
where type='u'

exec dbo.sp_executesql @sql;
go
Carter Medlin
sumber
Ini tampak menjanjikan, tetapi @sqlselalu terpotong. :(
Will Strohl
1

Saya memiliki versi yang lebih bermanfaat jika Anda tertarik. Saya mengangkat sedikit kode dari sini situs web di mana tautannya tidak lagi aktif. Saya memodifikasinya untuk memungkinkan array tabel ke dalam prosedur tersimpan dan mengisi drop, truncate, tambahkan pernyataan sebelum menjalankan semuanya. Ini memberi Anda kendali untuk memutuskan tabel mana yang perlu dipotong.

/****** Object:  UserDefinedTableType [util].[typ_objects_for_managing]    Script Date: 03/04/2016 16:42:55 ******/
CREATE TYPE [util].[typ_objects_for_managing] AS TABLE(
    [schema] [sysname] NOT NULL,
    [object] [sysname] NOT NULL
)
GO

create procedure [util].[truncate_table_with_constraints]
@objects_for_managing util.typ_objects_for_managing readonly

--@schema sysname
--,@table sysname

as 
--select
--    @table = 'TABLE',
--    @schema = 'SCHEMA'

declare @exec_table as table (ordinal int identity (1,1), statement nvarchar(4000), primary key (ordinal));

--print '/*Drop Foreign Key Statements for ['+@schema+'].['+@table+']*/'

insert into @exec_table (statement)
select
          'ALTER TABLE ['+SCHEMA_NAME(o.schema_id)+'].['+ o.name+'] DROP CONSTRAINT ['+fk.name+']'
from sys.foreign_keys fk
inner join sys.objects o
          on fk.parent_object_id = o.object_id
where 
exists ( 
select * from @objects_for_managing chk 
where 
chk.[schema] = SCHEMA_NAME(o.schema_id)  
and 
chk.[object] = o.name
) 
;
          --o.name = @table and
          --SCHEMA_NAME(o.schema_id)  = @schema

insert into @exec_table (statement) 
select
'TRUNCATE TABLE ' + src.[schema] + '.' + src.[object] 
from @objects_for_managing src
; 

--print '/*Create Foreign Key Statements for ['+@schema+'].['+@table+']*/'
insert into @exec_table (statement)
select 'ALTER TABLE ['+SCHEMA_NAME(o.schema_id)+'].['+o.name+'] ADD CONSTRAINT ['+fk.name+'] FOREIGN KEY (['+c.name+']) 
REFERENCES ['+SCHEMA_NAME(refob.schema_id)+'].['+refob.name+'](['+refcol.name+'])'
from sys.foreign_key_columns fkc
inner join sys.foreign_keys fk
          on fkc.constraint_object_id = fk.object_id
inner join sys.objects o
          on fk.parent_object_id = o.object_id
inner join sys.columns c
          on      fkc.parent_column_id = c.column_id and
                   o.object_id = c.object_id
inner join sys.objects refob
          on fkc.referenced_object_id = refob.object_id
inner join sys.columns refcol
          on fkc.referenced_column_id = refcol.column_id and
                   fkc.referenced_object_id = refcol.object_id
where 
exists ( 
select * from @objects_for_managing chk 
where 
chk.[schema] = SCHEMA_NAME(o.schema_id)  
and 
chk.[object] = o.name
) 
;

          --o.name = @table and
          --SCHEMA_NAME(o.schema_id)  = @schema



declare @looper int , @total_records int, @sql_exec nvarchar(4000)

select @looper = 1, @total_records = count(*) from @exec_table; 

while @looper <= @total_records 
begin

select @sql_exec = (select statement from @exec_table where ordinal =@looper)
exec sp_executesql @sql_exec 
print @sql_exec 
set @looper = @looper + 1
end
Zak Willis
sumber
Tautan mati dalam jawaban Anda. Poin ke artikel kosong.
Neolisk
Hai, mungkin ada deadlink tetapi seluruh kode ditentukan dalam karya. apa yang salah dengan ini?
Zak Willis
Tidak ada yang salah, tetapi Anda mungkin harus mengedit jawaban Anda dan menghapus tautan yang mati.
Neolisk