2013-07-25 16 views
11

Próbuję usunąć dane użytkownika i wszystkie powiązane z nim dane, które znajdują się w różnych tabelach. Wszystkie tabele mają klucze obce, ale bez usuwania kaskadowego.Serwer Sql - rekursywne usuwanie

badałem niektóre opcje:

  1. Włącz kaskada usuwać na wszystkich FK, usuwać i usunąć kaskady usunąć.
  2. Usuń od dołu UP, zapętlić wszystkie liście, usunąć i powtórzyć tę operację do Root.

Czy są jakieś inteligentne opcje lub inne techniki?

Używam Microsoft SQL Server 2012 (SP1)

Odpowiedz

6

Są to najlepsze i najbardziej wydajnymi. W przypadku zapytań dotyczących produkcji korzystałbym z 2.

tylko inne sposoby mogę myśleć to (IMO) będzie tylko nadaje się do szybkiego i brudne usuwania danych w środowisku testowym (unikając konieczności analizowania poprawną kolejność)

  1. Wyłącz wszystkie FKS usuwać żądane dane, a następnie ponownie włączyć FK. Jest to nieefektywne, ponieważ trzeba je ponownie włączyć WITH CHECK, aby uniknąć pozostawienia FK w niezaufanym stanie, co oznacza, że ​​wszystkie zachowane dane wymagają ponownego sprawdzenia.
  2. Wymień wszystkie stwierdzone tabele w dowolnej kolejności i uruchom partię tyle razy, ile potrzeba, aż do skutku bez błędów FK.
0

wierzę, że to możliwe, aby zrobić

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

Cascade usunięcie jest łatwe, dobrze funkcjonujących i niezawodne. Działa na wielu poziomach. Plany kwerend są interesujące i wydają się dobrze zoptymalizowane.

Jeśli chcesz ręcznie usuwać, upewnij się, że w celu zwiększenia wydajności wydajesz tylko jedno zapytanie na tabelę. Możesz użyć złączeń w instrukcji delete, aby dołączyć do poziomu nadrzędnego, aby przefiltrować wiersze, które mają zostać usunięte.

1

Znalazłem ładne rozwiązanie o tym, under: How to generate DELETE statements in PL/SQL, based on the tables FK relations?

wygenerować funkcję:

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; 

Wykorzystanie:

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

uważać stosując rozwiązania PL/SQL do MSSQL/T-SQL.Nie widzę żadnych problemów z twoim kodem, ale Oracle cię nienawidzi i będzie robił różne rzeczy tylko po to, by być złośliwym. –