Làm thế nào các ràng buộc khóa ngoại có thể tạm thời bị vô hiệu hóa bằng T-SQL?


824

Là vô hiệu hóa và kích hoạt các ràng buộc khóa ngoại được hỗ trợ trong SQL Server? Hoặc là lựa chọn duy nhất của tôi dropvà sau đó lạicreate các ràng buộc?


128
Đối với những người hỏi "tại sao" tôi muốn làm điều này: Nó dành cho môi trường thử nghiệm nơi tôi muốn có thể xóa và tải dữ liệu thử nghiệm từ nhiều bảng mà không cần phải duy trì và chỉ định thứ tự cách tải dữ liệu. Tính toàn vẹn dữ liệu không quan trọng đối với kịch bản này.
Ray

8
Lưu ý - nếu bạn muốn TRUNCATE bảng, bạn sẽ thực sự cần phải bỏ các ràng buộc.
ExcellenceBill

@Out HiểuBill Rõ ràng, điều này hoạt động cho TRUNCATE .
jpaugh

1
Có vẻ kỳ lạ là bất cứ ai cũng sẽ hỏi nó trong một môi trường sản xuất . Một trường hợp sử dụng rất phổ biến là cho chèn số lượng lớn. Nếu bạn có một bảng tự tham chiếu, đôi khi rất khó để sắp xếp một chèn hàng loạt để hàng cha mẹ luôn được chèn trước con, vì vậy bạn vô hiệu hóa ràng buộc, chèn hàng loạt, kích hoạt ràng buộc.
Auspex

Câu trả lời:


1126

Nếu bạn muốn tắt tất cả các ràng buộc trong cơ sở dữ liệu, chỉ cần chạy mã này:

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

Để bật lại, hãy chạy: (tất nhiên bản in là tùy chọn và nó chỉ liệt kê các bảng)

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

Tôi thấy nó hữu ích khi điền dữ liệu từ cơ sở dữ liệu này sang cơ sở dữ liệu khác. Đó là cách tiếp cận tốt hơn nhiều so với các ràng buộc thả. Như bạn đã đề cập, nó rất hữu ích khi loại bỏ tất cả dữ liệu trong cơ sở dữ liệu và phục hồi nó (nói trong môi trường thử nghiệm).

Nếu bạn đang xóa tất cả dữ liệu, bạn có thể thấy giải pháp này hữu ích.

Ngoài ra đôi khi cũng rất hữu ích để vô hiệu hóa tất cả các kích hoạt, bạn có thể xem giải pháp hoàn chỉnh ở đây .


9
"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Chỉ nên có một "KIỂM TRA" ở đó?
CrazyPyro

27
@CrazyPyro - không cần cả hai
kristof

32
@CrazyPyro: cả hai đều thực sự cần thiết, lý do là bởi vì KIỂM TRA đầu tiên thuộc về KIỂM TRA VỚI và KIỂM TRA thứ hai với CONSTRAINT (đó là loại ràng buộc). KIỂM TRA đầu tiên đảm bảo dữ liệu của bạn được kiểm tra tính nhất quán khi kích hoạt ràng buộc. Nếu bạn không muốn điều đó, bạn có thể viết VỚI NOCHECK. Có thể hữu ích trong các tình huống kiểm tra nhất định khi bạn không quan tâm đến dữ liệu thực tế, miễn là có một số để các truy vấn của bạn có nội dung nào đó để chơi.
Valentino Vranken

8
Có tệ không khi tôi nhận được kết quả này trên lệnh thứ hai? "Tuyên bố ALTER TABLE mâu thuẫn với ràng buộc FOREIGN KEY ..."
Devil's Advocate

34
Điều đáng chú ý là ngay cả khi các ràng buộc bị vô hiệu hóa, TRUNCATE TABLE sẽ không hoạt động. Đối với điều đó, bạn sẽ cần phải bỏ các ràng buộc. Mặt
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

29
một tìm kiếm tốt, nhưng lưu ý rằng bạn vẫn không thể cắt bớt bảng mà không loại bỏ các ràng buộc khóa ngoại
Steven A. Lowe

1
và bạn cũng cần lưu ý rằng khi bạn bật lại các ràng buộc và kiểm tra tính toàn vẹn dữ liệu, dữ liệu của bạn có thể bị lỗi và khắc phục sự cố như thế có thể là một cơn ác mộng nếu dữ liệu bị lỗi nằm ở cuối chuỗi dài ràng buộc liên kết.
Jimoc

1
Bạn cũng cần kiểm tra lần thứ hai khi bật lại các ràng buộc. Mặt khác, như vậy, mã của bạn sẽ chỉ kiểm tra ràng buộc một lần, không bật nó.
ps2goat

3
có, "với kiểm tra séc" cần thiết cho năm 2012. Chỉnh sửa đã bị từ chối? Liên kết MS
crokusek

1
Các câu lệnh được sử dụng ở đây để kích hoạt lại các ràng buộc bị thiếu các mệnh đề VỚI KIỂM TRA. Đây là một lỗ hổng khá lớn. Xem câu trả lời của tôi để biết chi tiết - stackoverflow.com/a353427150/81595 .
Scott Munro

37

Để vô hiệu hóa các ràng buộc, bạn có ALTERbảng bằng NOCHECK

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

Để cho phép bạn phải sử dụng KIỂM TRA kép :

ALTER TABLE [TABLE_NAME] WITH CHECK CHECK CONSTRAINT [ALL|CONSTRAINT_NAME]
  • Hãy chú ý đến KIỂM TRA KIỂM TRA kép khi bật.
  • TẤT CẢ có nghĩa là cho tất cả các ràng buộc trong bảng.

Sau khi hoàn thành, nếu bạn cần kiểm tra trạng thái, hãy sử dụng tập lệnh này để liệt kê trạng thái ràng buộc. Sẽ rất hữu ích:

    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 

Không xem cho Khóa chính ? Đối với khóa ngoại SYSFOREIGNKEYS Hệ thống Xem sys.sysforeignkeys msdn.microsoft.com/en-us/l
Library / ms177604.aspx

nếu bạn đang cố gắng vô hiệu hóa khóa chính để chèn, tôi sẽ khuyên bạn nên sử dụng (SET IDENTITY_INSERT) nếu bạn chỉ muốn kiểm tra khóa chính, bạn có thể thử sys.key_constraint với sys.indexes.is_primary_key
Diego Mendes

29

Tùy chọn tốt nhất của bạn là DROP và TẠO các ràng buộc khóa ngoài.

Tôi đã không tìm thấy các ví dụ trong bài đăng này sẽ hoạt động với tôi "như hiện tại", một cái sẽ không hoạt động nếu các khóa ngoại tham chiếu các lược đồ khác nhau, cái kia sẽ không hoạt động nếu khóa ngoại tham chiếu nhiều cột. Kịch bản này xem xét cả hai, nhiều lược đồ và nhiều cột cho mỗi khóa ngoại.

Đây là tập lệnh tạo ra các câu lệnh "ADD CONSTRAINT", đối với nhiều cột, nó sẽ phân tách chúng bằng dấu phẩy ( hãy chắc chắn lưu kết quả này trước khi thực hiện các câu lệnh 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

Đây là tập lệnh tạo ra các câu lệnh "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

5
Bạn có thể giải thích tại sao nó tốt hơn việc vô hiệu hóa và kích hoạt lại các ràng buộc không?
Mahmoodvcs

Kịch bản hay. Để biết cách tiếp cận tương tự nhưng thay thế, hãy xem: mssqltips.com/sqlservertip/3347/ Khăn
Matt Browne

11

Tiêu chuẩn SQL-92 cho phép một hằng số được khai báo là DEAXRABLE để nó có thể được hoãn lại (ngầm hoặc rõ ràng) trong phạm vi giao dịch. Đáng buồn thay, SQL Server vẫn thiếu chức năng SQL-92 này.

Đối với tôi, việc thay đổi một ràng buộc thành NOCHECK gần giống với việc thay đổi cấu trúc cơ sở dữ liệu một cách nhanh chóng - chắc chắn là có một số điều cần tránh (ví dụ: người dùng yêu cầu các đặc quyền gia tăng).


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

Tôi đồng ý với bạn, Hamlin. Khi bạn đang truyền dữ liệu bằng SSIS hoặc khi muốn sao chép dữ liệu, có vẻ như khá cần thiết để tạm thời vô hiệu hóa hoặc loại bỏ các ràng buộc khóa ngoại và sau đó kích hoạt lại hoặc tạo lại chúng. Trong những trường hợp này, tính toàn vẹn tham chiếu không phải là một vấn đề, bởi vì nó đã được duy trì trong cơ sở dữ liệu nguồn. Do đó, bạn có thể yên tâm về vấn đề này.


Kịch bản này rất tốt để tạo các lệnh "ALTER" của tôi, nhưng làm cách nào tôi có thể thực hiện các lệnh này để thực thi / chạy trong SP?
BlueChippy

1
Tôi nghĩ rằng điều này sẽ không hoạt động nếu bất kỳ khóa ngoại nào là đa cột
Zar Shardan

Điều này cũng không tạo ra tất cả các ký tự cho các tên bảng / khóa quá dài.
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

10

WITH CHECK CHECK gần như chắc chắn là bắt buộc!

Điểm này đã được nêu ra trong một số câu trả lời và ý kiến ​​nhưng tôi cảm thấy rằng nó đủ quan trọng để gọi nó một lần nữa.

Kích hoạt lại một ràng buộc bằng lệnh sau (không WITH CHECK) sẽ có một số nhược điểm nghiêm trọng .

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

VỚI KIỂM TRA | VỚI NOCHECK

Chỉ định xem dữ liệu trong bảng có hoặc không được xác thực đối với ràng buộc FOREIGN KEY hoặc CHECK mới được thêm hoặc bật lại. Nếu không được chỉ định, VỚI KIỂM TRA được giả sử cho các ràng buộc mới và VỚI NOCHECK được giả sử cho các ràng buộc được bật lại.

Nếu bạn không muốn xác minh các ràng buộc CHECK hoặc FOREIGN KEY mới đối với dữ liệu hiện có, hãy sử dụng VỚI NOCHECK. Chúng tôi không khuyên bạn nên làm điều này, trừ trường hợp hiếm. Các ràng buộc mới sẽ được đánh giá trong tất cả các cập nhật dữ liệu sau này. Bất kỳ vi phạm ràng buộc nào bị loại bỏ bởi VỚI NOCHECK khi ràng buộc được thêm vào có thể khiến các cập nhật trong tương lai không thành công nếu chúng cập nhật các hàng với dữ liệu không tuân thủ ràng buộc.

Trình tối ưu hóa truy vấn không xem xét các ràng buộc được xác định VỚI NOCHECK. Các ràng buộc như vậy được bỏ qua cho đến khi chúng được kích hoạt lại bằng cách sử dụng bảng ALTER TABLE VỚI KIỂM TRA KIỂM TRA TẤT CẢ.

Lưu ý: VỚI NOCHECK là mặc định để bật lại các ràng buộc. Tôi phải tự hỏi tại sao ...

  1. Không có dữ liệu hiện có trong bảng sẽ được đánh giá trong quá trình thực hiện lệnh này - hoàn thành thành công không đảm bảo rằng dữ liệu trong bảng là hợp lệ theo ràng buộc.
  2. Trong lần cập nhật tiếp theo của các bản ghi không hợp lệ, ràng buộc sẽ được đánh giá và sẽ thất bại - dẫn đến các lỗi có thể không liên quan đến bản cập nhật thực tế được thực hiện.
  3. Logic ứng dụng dựa trên các ràng buộc để đảm bảo dữ liệu hợp lệ có thể bị lỗi.
  4. Trình tối ưu hóa truy vấn sẽ không sử dụng bất kỳ ràng buộc nào được kích hoạt theo cách này.

Các sys.foreign_keys xem hệ thống cung cấp một số tầm nhìn về vấn đề này. Lưu ý rằng nó có cả một is_disabledvà một is_not_trustedcột. is_disabledcho biết liệu các hoạt động thao tác dữ liệu trong tương lai sẽ được xác nhận chống lại các ràng buộc. is_not_trustedcho biết liệu tất cả dữ liệu hiện tại trong bảng đã được xác thực theo ràng buộc hay chưa.

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint;

Là những hạn chế của bạn để được tin tưởng? Tìm ra...

SELECT * FROM sys.foreign_keys WHERE is_not_trusted = 1;

9

Bài viết đầu tiên :)

Đối với OP, giải pháp của kristof sẽ hoạt động, trừ khi có vấn đề với dữ liệu lớn và các vấn đề về nhật ký giao dịch trên các bản xóa lớn. Ngoài ra, ngay cả khi lưu trữ tlog dự phòng, vì xóa ghi vào tlog, thao tác có thể mất nhiều thời gian RẤT cho các bảng có hàng trăm triệu hàng.

Tôi sử dụng một loạt các con trỏ để cắt và tải lại các bản sao lớn của một trong những cơ sở dữ liệu sản xuất khổng lồ của chúng tôi thường xuyên. Giải pháp được thiết kế cho nhiều lược đồ, nhiều cột khóa ngoại và tốt nhất có thể được sử dụng trong SSIS.

Nó liên quan đến việc tạo ba bảng phân tầng (các bảng thực) để chứa các tập lệnh DROP, CREATE và CHECK FK, tạo và chèn các tập lệnh đó vào các bảng, sau đó lặp qua các bảng và thực hiện chúng. Kịch bản đính kèm gồm bốn phần: 1.) tạo và lưu trữ các tập lệnh trong ba bảng phân đoạn (thực), 2.) thực thi các tập lệnh FK thả qua một con trỏ từng cái một, 3.) Sử dụng sp_MSforeachtable để cắt tất cả các bảng trong cơ sở dữ liệu khác với ba bảng phân tầng của chúng tôi và 4.) thực thi FK tạo và kiểm tra các tập lệnh FK ở cuối gói ETL SSIS của bạn.

Chạy phần tạo tập lệnh trong tác vụ SQL Thực thi trong SSIS. Chạy phần "thực thi Drop FK ScScript" trong tác vụ SQL thực thi thứ hai. Đặt tập lệnh cắt ngắn trong tác vụ SQL Thực thi thứ ba, sau đó thực hiện bất kỳ quy trình ETL nào khác mà bạn cần thực hiện trước khi đính kèm tập lệnh CREATE và CHECK trong tác vụ SQL thực thi cuối cùng (hoặc hai nếu muốn) ở cuối luồng điều khiển của bạn.

Việc lưu trữ các tập lệnh trong các bảng thực đã được chứng minh là vô giá khi việc áp dụng lại các khóa ngoại không thành công vì bạn có thể chọn * từ sync_CreateFK, sao chép / dán vào cửa sổ truy vấn của mình, chạy chúng một lần và khắc phục các sự cố dữ liệu khi bạn tìm những cái không thành công / vẫn không áp dụng lại.

Không chạy lại tập lệnh một lần nữa nếu nó không thành công mà không đảm bảo rằng bạn áp dụng lại tất cả các khóa / kiểm tra ngoại lệ trước khi thực hiện, hoặc rất có thể bạn sẽ mất một số sáng tạo và kiểm tra tập lệnh fk khi các bảng phân tầng của chúng tôi bị hủy và được tạo lại trước khi tạo các kịch bản để thực thi.

----------------------------------------------------------------------------
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

7

Tìm các ràng buộc

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

Thực thi SQL được tạo bởi SQL này

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')

Cách thức an toàn.

Lưu ý: Đã thêm giải pháp cho việc bỏ ràng buộc để có thể bỏ hoặc sửa đổi bảng mà không có bất kỳ lỗi ràng buộc nào.


3

Nhấp chuột phải vào thiết kế bảng và đi đến Mối quan hệ và chọn khóa ngoại ở ngăn bên trái và trong ngăn bên phải, đặt ràng buộc ràng buộc khóa ngoài thành 'Có' (để bật ràng buộc khóa ngoài) hoặc 'Không' (để vô hiệu hóa nó). nhập mô tả hình ảnh ở đây


3

Câu trả lời được đánh dấu '905' có vẻ tốt nhưng không hoạt động.

Sau đây làm việc cho tôi. Bất kỳ khóa chính, khóa duy nhất hoặc ràng buộc mặc định nào đều KHÔNG thể bị vô hiệu hóa. Trên thực tế, nếu 'sp_helpconstraint' 'hiển thị' n / a 'trong status_enables - Có nghĩa là nó KHÔNG THỂ thể được bật / tắt.

- Để tạo tập lệnh thành DISABLE

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

- Để tạo tập lệnh thành ENABLE

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

3

Bạn thực sự có thể vô hiệu hóa các ràng buộc khóa ngoại giống như cách bạn tạm thời vô hiệu hóa các ràng buộc khác:

Alter table MyTable nocheck constraint FK_ForeignKeyConstraintName

Chỉ cần đảm bảo rằng bạn đang vô hiệu hóa ràng buộc trên bảng đầu tiên được liệt kê trong tên ràng buộc. Ví dụ: nếu ràng buộc khóa ngoại của tôi là FK_LocationsEmprocodLocationIdEmployeeId, tôi sẽ muốn sử dụng như sau:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

mặc dù vi phạm ràng buộc này sẽ tạo ra một lỗi không nhất thiết phải nêu bảng đó là nguồn gốc của xung đột.


1

Một tập lệnh để thống trị tất cả: tập lệnh này kết hợp cắt ngắn và xóa các lệnh với sp_MSforeachtable để bạn có thể tránh bỏ và tạo lại các ràng buộc - chỉ cần xác định các bảng cần xóa thay vì cắt ngắn và vì mục đích của tôi, tôi đã bao gồm một bộ lọc lược đồ bổ sung cho tốt biện pháp (thử nghiệm năm 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

1

Bạn có thể tạm thời vô hiệu hóa các ràng buộc trên các bảng của mình, thực hiện công việc, sau đó xây dựng lại chúng.

Đây là một cách dễ dàng để làm điều đó ...

Vô hiệu hóa tất cả các chỉ mục, bao gồm các khóa chính, sẽ vô hiệu hóa tất cả các khóa ngoại, sau đó kích hoạt lại chỉ các khóa chính để bạn có thể làm việc với chúng ...

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

[Làm gì đó, như tải dữ liệu]

Sau đó kích hoạt lại và xây dựng lại các chỉ mục ...

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

Điều này có vẻ đầy hứa hẹn, nhưng @sqlluôn bị cắt ngắn. :(
Will Strohl

1

Tôi có một phiên bản hữu ích hơn nếu bạn quan tâm. Tôi đã nâng một chút mã từ đây một trang web nơi liên kết không còn hoạt động. Tôi đã sửa đổi nó để cho phép một mảng các bảng vào thủ tục được lưu trữ và nó điền vào phần thả, cắt, thêm các câu lệnh trước khi thực hiện tất cả các bảng. Điều này cho phép bạn kiểm soát để quyết định bảng nào cần cắt bớt.

/****** 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

Liên kết chết trong câu trả lời của bạn. Chỉ vào một bài viết trống.
Neolisk

Xin chào, có thể có một deadlink nhưng toàn bộ mã được chỉ định trong phần. Điều gì là sai với điều này?
Zak Willis

Không có gì sai, nhưng có lẽ bạn nên chỉnh sửa câu trả lời của mình và xóa liên kết chết.
Neolisk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.