2013-07-25 40 views
11

Tôi đang cố xóa dữ liệu của người dùng và tất cả dữ liệu có liên quan của nó nằm trong các bảng khác nhau. Tất cả các bảng đều có Khóa ngoại nhưng không bị xóa tầng.Máy chủ Sql - đệ quy xóa

tôi điều tra một số tùy chọn:

  1. Enable thác xóa trên tất cả FK, xóa và loại bỏ các thác xóa.
  2. Xóa từ dưới cùng UP, lặp lại cho tất cả các lá xóa và lặp lại thao tác này cho đến Gốc.

Có tùy chọn thông minh hơn hoặc các kỹ thuật khác không?

Tôi đang sử dụng Microsoft SQL Server 2012 (SP1)

Trả lời

6

Đó là những sản phẩm tốt nhất và hiệu quả nhất. Đối với các truy vấn sản xuất, tôi sẽ sử dụng 2.

duy nhất cách khác mà tôi có thể nghĩ sẽ (IMO) chỉ phù hợp để loại bỏ nhanh chóng và dơ bẩn của dữ liệu trong một môi trường thử nghiệm (tránh sự cần thiết để phân tích theo đúng thứ tự)

  1. Disable tất cả FKS xóa dữ liệu mong muốn rồi bật lại FK. Điều này không hiệu quả vì chúng cần phải được kích hoạt lại WITH CHECK để tránh rời khỏi các FK trong trạng thái không tin cậy có nghĩa là tất cả dữ liệu được lưu giữ cần phải được xác nhận lại.
  2. Liệt kê tất cả các câu lệnh DELETE trên các bảng bị ảnh hưởng theo thứ tự tùy ý và chạy hàng loạt nhiều lần nếu cần thiết cho đến khi thành công không có lỗi FK.
0

tôi tin rằng nó có thể làm

DELETE FROM tbl1 
INNER JOIN tbl2 
ON tbl1.ID = tbl2.tbl1ID 
WHERE 
tbl1.somthing = x 
1

Cascade xóa rất dễ dàng, hoạt động tốt và đáng tin cậy. Hoạt động trên nhiều cấp độ. Các kế hoạch truy vấn rất thú vị và được tối ưu hóa tốt.

Nếu bạn muốn xóa thủ công, hãy đảm bảo chỉ phát hành một truy vấn trên mỗi bảng vì lý do hiệu quả. Bạn có thể sử dụng các phép nối trong một câu lệnh xóa để tham gia (các) cấp độ gốc để lọc các hàng sẽ bị xóa.

1

Tôi tìm thấy một giải pháp tốt đẹp về nó, dưới: How to generate DELETE statements in PL/SQL, based on the tables FK relations?

.210

tạo ra các chức năng:

IF OBJECT_ID('dbo.udfGetFullQualName') IS NOT NULL 
    DROP FUNCTION dbo.udfGetFullQualName; 

GO 
CREATE FUNCTION dbo.udfGetFullQualName 
(@ObjectId INT) 
RETURNS VARCHAR (300) 
AS 
BEGIN 
    DECLARE @schema_id AS BIGINT; 
    SELECT @schema_id = schema_id 
    FROM sys.tables 
    WHERE object_id = @ObjectId; 
    RETURN '[' + SCHEMA_NAME(@schema_id) + '].[' + OBJECT_NAME(@ObjectId) + ']'; 
END 

GO 
--============ Supporting Function dbo.udfGetOnJoinClause 
IF OBJECT_ID('dbo.udfGetOnJoinClause') IS NOT NULL 
    DROP FUNCTION dbo.udfGetOnJoinClause; 

GO 
CREATE FUNCTION dbo.udfGetOnJoinClause 
(@fkNameId INT) 
RETURNS VARCHAR (1000) 
AS 
BEGIN 
    DECLARE @OnClauseTemplate AS VARCHAR (1000); 
    SET @OnClauseTemplate = '[<@pTable>].[<@pCol>] = [<@cTable>].[<@cCol>] AND '; 
    DECLARE @str AS VARCHAR (1000); 
    SET @str = ''; 
    SELECT @str = @str + REPLACE(REPLACE(REPLACE(REPLACE(@OnClauseTemplate, '<@pTable>', OBJECT_NAME(rkeyid)), '<@pCol>', COL_NAME(rkeyid, rkey)), '<@cTable>', OBJECT_NAME(fkeyid)), '<@cCol>', COL_NAME(fkeyid, fkey)) 
    FROM dbo.sysforeignkeys AS fk 
    WHERE fk.constid = @fkNameId; --OBJECT_ID('FK_ProductArrearsMe_ProductArrears') 
    RETURN LEFT(@str, LEN(@str) - LEN(' AND ')); 
END 

GO 
--=========== CASECADE DELETE STORED PROCEDURE dbo.uspCascadeDelete 
IF OBJECT_ID('dbo.uspCascadeDelete') IS NOT NULL 
    DROP PROCEDURE dbo.uspCascadeDelete; 

GO 
CREATE PROCEDURE dbo.uspCascadeDelete 
@ParentTableId VARCHAR (300), @WhereClause VARCHAR (2000), @ExecuteDelete CHAR (1)='N', --'N' IF YOU NEED DELETE SCRIPT 
@FromClause VARCHAR (8000)='', @Level INT=0 -- TABLE NAME OR OBJECT (TABLE) ID (Production.Location) WHERE CLAUSE (Location.LocationID = 7) 'Y' IF WANT TO DELETE DIRECTLY FROM SP, IF LEVEL 0, THEN KEEP DEFAULT 
AS -- writen by Daniel Crowther 16 Dec 2004 - handles composite primary keys 
SET NOCOUNT ON; 
/* Set up debug */ 
DECLARE @DebugMsg AS VARCHAR (4000), 
@DebugIndent AS VARCHAR (50); 
SET @DebugIndent = REPLICATE('---', @@NESTLEVEL) + '> '; 
IF ISNUMERIC(@ParentTableId) = 0 
    BEGIN -- assume owner is dbo and calculate id 
     IF CHARINDEX('.', @ParentTableId) = 0 
      SET @ParentTableId = OBJECT_ID('[dbo].[' + @ParentTableId + ']'); 
     ELSE 
      SET @ParentTableId = OBJECT_ID(@ParentTableId); 
    END 
IF @Level = 0 
    BEGIN 
     PRINT @DebugIndent + ' **************************************************************************'; 
     PRINT @DebugIndent + ' *** Cascade delete ALL data from ' + dbo.udfGetFullQualName(@ParentTableId); 
     IF @ExecuteDelete = 'Y' 
      PRINT @DebugIndent + ' *** @ExecuteDelete = Y *** deleting data...'; 
     ELSE 
      PRINT @DebugIndent + ' *** Cut and paste output into another window and execute ***'; 
    END 
DECLARE @CRLF AS CHAR (2); 
SET @CRLF = CHAR(13) + CHAR(10); 
DECLARE @strSQL AS VARCHAR (4000); 
IF @Level = 0 
    SET @strSQL = 'SET NOCOUNT ON' + @CRLF; 
ELSE 
    SET @strSQL = ''; 
SET @strSQL = @strSQL + 'PRINT ''' + @DebugIndent + dbo.udfGetFullQualName(@ParentTableId) + ' Level=' + CAST (@@NESTLEVEL AS VARCHAR) + ''''; 
IF @ExecuteDelete = 'Y' 
    EXECUTE (@strSQL); 
ELSE 
    PRINT @strSQL; 
DECLARE curs_children CURSOR LOCAL FORWARD_ONLY 
    FOR SELECT DISTINCT constid AS fkNameId, -- constraint name 
         fkeyid AS cTableId 
     FROM dbo.sysforeignkeys AS fk 
     WHERE fk.rkeyid <> fk.fkeyid -- WE DO NOT HANDLE self referencing tables!!! 
       AND fk.rkeyid = @ParentTableId; 
OPEN curs_children; 
DECLARE @fkNameId AS INT, 
@cTableId AS INT, 
@cColId AS INT, 
@pTableId AS INT, 
@pColId AS INT; 
FETCH NEXT FROM curs_children INTO @fkNameId, @cTableId; --, @cColId, @pTableId, @pColId 
DECLARE @strFromClause AS VARCHAR (1000); 
DECLARE @nLevel AS INT; 
IF @Level = 0 
    BEGIN 
     SET @FromClause = 'FROM ' + dbo.udfGetFullQualName(@ParentTableId); 
    END 
WHILE @@FETCH_STATUS = 0 
    BEGIN 
     SELECT @strFromClause = @FromClause + @CRLF + '  INNER JOIN ' + dbo.udfGetFullQualName(@cTableId) + @CRLF + '  ON ' + dbo.udfGetOnJoinClause(@fkNameId); 
     SET @nLevel = @Level + 1; 
     EXECUTE dbo.uspCascadeDelete @ParentTableId = @cTableId, @WhereClause = @WhereClause, @ExecuteDelete = @ExecuteDelete, @FromClause = @strFromClause, @Level = @nLevel; 
     SET @strSQL = 'DELETE FROM ' + dbo.udfGetFullQualName(@cTableId) + @CRLF + @strFromClause + @CRLF + 'WHERE ' + @WhereClause + @CRLF; 
     SET @strSQL = @strSQL + 'PRINT ''---' + @DebugIndent + 'DELETE FROM ' + dbo.udfGetFullQualName(@cTableId) + '  Rows Deleted: '' + CAST(@@ROWCOUNT AS VARCHAR)' + @CRLF + @CRLF; 
     IF @ExecuteDelete = 'Y' 
      EXECUTE (@strSQL); 
     ELSE 
      PRINT @strSQL; 
     FETCH NEXT FROM curs_children INTO @fkNameId, @cTableId; 
    --, @cColId, @pTableId, @pColId 
    END 
IF @Level = 0 
    BEGIN 
     SET @strSQL = @CRLF + 'PRINT ''' + @DebugIndent + dbo.udfGetFullQualName(@ParentTableId) + ' Level=' + CAST (@@NESTLEVEL AS VARCHAR) + ' TOP LEVEL PARENT TABLE''' + @CRLF; 
     SET @strSQL = @strSQL + 'DELETE FROM ' + dbo.udfGetFullQualName(@ParentTableId) + ' WHERE ' + @WhereClause + @CRLF; 
     SET @strSQL = @strSQL + 'PRINT ''' + @DebugIndent + 'DELETE FROM ' + dbo.udfGetFullQualName(@ParentTableId) + ' Rows Deleted: '' + CAST(@@ROWCOUNT AS VARCHAR)' + @CRLF; 
     IF @ExecuteDelete = 'Y' 
      EXECUTE (@strSQL); 
     ELSE 
      PRINT @strSQL; 
    END 
CLOSE curs_children; 
DEALLOCATE curs_children; 

sử dụng:

EXEC uspCascadeDelete 
@ParentTableId = '[VAULT].[Package]', 
@WhereClause = 'CreatedBy =''VZBMNSTEST''' 
+0

Hãy cẩn thận khi áp dụng các giải pháp PL/SQL để MSSQL/T-SQL.Tôi không thấy bất kỳ vấn đề với mã của bạn, nhưng Oracle ghét bạn và sẽ làm những việc khác nhau chỉ để được spiteful. –