Finding Table and Primary Key and Foreign Key

Rédigé par Sozezzo - - Aucun commentaire

SQL Server does not help to easily determine the dependencies between tables.
This research presents the parent table and the child tables and columns used.

 

https://msdn.microsoft.com/en-us/library/ms179610.aspx
http://stackoverflow.com/questions/925738/how-to-find-foreign-key-dependencies-in-sql-server
 

well, however, we have many databases with bad design that didn’t have foreign keys defined but that did have related data.


SELECT        
      ROW_NUMBER() OVER (ORDER BY ForeignKeyTable.name) as rownumber
    , PARENTSchemas.name AS PARENT_SchemaName
    , PARENTObject.name AS PARENT_TableName
    , PARENTColumn.name AS PARENT_ColumnName
    --, PARENTColumn.column_id AS PARENT_ColumnPosition
    , ForeignKeySchema.name AS FOREIGNKEY_SchemaName
    , ForeignKeyTable.name  AS FOREIGNKEY_TableName
    , ForeignKeyColumn.name AS FOREIGNKEY_ColumnName
    --, sys.foreign_key_columns.referenced_object_id AS PARENT_OBJECT_ID
    --, sys.foreign_key_columns.referenced_column_id AS PARENT_COLUMN_ID
    --, sys.foreign_key_columns.constraint_object_id
    --, sys.foreign_key_columns.constraint_column_id
    --, sys.foreign_key_columns.parent_object_id AS FOREIGNKEY_OBJECT_ID
    --, sys.foreign_key_columns.parent_column_id AS FOREIGNKEY_COLUMN_ID
    , sys.foreign_keys.is_disabled
FROM            
    sys.foreign_key_columns INNER JOIN
    sys.foreign_keys ON sys.foreign_key_columns.constraint_object_id = sys.foreign_keys.object_id INNER JOIN
    sys.objects AS PARENTObject ON sys.foreign_key_columns.referenced_object_id = PARENTObject.object_id INNER JOIN
    sys.schemas AS PARENTSchemas ON PARENTObject.schema_id = PARENTSchemas.schema_id INNER JOIN
    sys.columns AS ForeignKeyColumn ON sys.foreign_key_columns.parent_object_id = ForeignKeyColumn.object_id AND
    sys.foreign_key_columns.parent_column_id = ForeignKeyColumn.column_id INNER JOIN
    sys.objects AS ForeignKeyTable ON sys.foreign_key_columns.parent_object_id = ForeignKeyTable.object_id INNER JOIN
    sys.columns AS PARENTColumn ON sys.foreign_key_columns.referenced_object_id = PARENTColumn.object_id AND
    sys.foreign_key_columns.referenced_column_id = PARENTColumn.column_id INNER JOIN
    sys.schemas AS ForeignKeySchema ON ForeignKeyTable.schema_id = ForeignKeySchema.schema_id
WHERE (PARENTObject.type = 'U')

 

This example of use is not a perfect solution. Considerations and compromises that limit this solution:

* Tables can not have self-reference.

* The selection conditions are repeated and are not an example of performance. I thought to use temporary tables but I do not have time and patience.

The SQL code is created to be used to create an initial version.


DROP PROCEDURE sDBACascateDelete
Go
CREATE PROCEDURE sDBACascateDelete
(
   @schemaname nvarchar(100)
 , @tablename nvarchar(100)
 , @where nvarchar(1024)
 , @recursive int = 1

)
AS
BEGIN

SET NOCOUNT ON

DECLARE
   @schemanameNEW nvarchar(100)
 , @tablenameNEW nvarchar(100)
 , @whereNEW nvarchar(1024)
DECLARE @SQL nvarchar(max);
DECLARE @SQL1 nvarchar(max);
DECLARE @SQLTODelete nvarchar(max);
DECLARE @rownumber INT;


DECLARE @TMP TABLE (
    [rownumber] [bigint] NULL,
    [PARENT_SchemaName] [sysname] NOT NULL,
    [PARENT_TableName] [sysname] NOT NULL,
    [PARENT_ColumnName] [sysname] NULL,
    [PARENT_ColumnPosition] [int] NOT NULL,
    [FOREIGNKEY_SchemaName] [sysname] NOT NULL,
    [FOREIGNKEY_TableName] [sysname] NOT NULL,
    [FOREIGNKEY_ColumnName] [sysname] NULL,
    [PARENT_OBJECT_ID] [int] NOT NULL,
    [PARENT_COLUMN_ID] [int] NOT NULL,
    [constraint_object_id] [int] NOT NULL,
    [constraint_column_id] [int] NOT NULL,
    [FOREIGNKEY_OBJECT_ID] [int] NOT NULL,
    [FOREIGNKEY_COLUMN_ID] [int] NOT NULL,
    [is_disabled] [bit] NOT NULL
);

SET @SQL = char(13)+char(10);
SET @SQL = '';
SET @SQL = 'DELETE FROM ['+@schemaname+'].['+@tablename+'] WHERE '+@where+@SQL + ';';
SET @SQLTODelete = ' FROM ['+@schemaname+'].['+@tablename+'] WHERE '+@where;  

INSERT INTO @TMP
SELECT        
      ROW_NUMBER() OVER (ORDER BY ForeignKeyTable.name) as rownumber
    , PARENTSchemas.name AS PARENT_SchemaName
    , PARENTObject.name AS PARENT_TableName
    , PARENTColumn.name AS PARENT_ColumnName
    , PARENTColumn.column_id AS PARENT_ColumnPosition
    , ForeignKeySchema.name AS FOREIGNKEY_SchemaName
    , ForeignKeyTable.name  AS FOREIGNKEY_TableName
    , ForeignKeyColumn.name AS FOREIGNKEY_ColumnName
    , sys.foreign_key_columns.referenced_object_id AS PARENT_OBJECT_ID
    , sys.foreign_key_columns.referenced_column_id AS PARENT_COLUMN_ID
    , sys.foreign_key_columns.constraint_object_id
    , sys.foreign_key_columns.constraint_column_id
    , sys.foreign_key_columns.parent_object_id AS FOREIGNKEY_OBJECT_ID
    , sys.foreign_key_columns.parent_column_id AS FOREIGNKEY_COLUMN_ID
    , sys.foreign_keys.is_disabled
FROM            
    sys.foreign_key_columns INNER JOIN
    sys.foreign_keys ON sys.foreign_key_columns.constraint_object_id = sys.foreign_keys.object_id INNER JOIN
    sys.objects AS PARENTObject ON sys.foreign_key_columns.referenced_object_id = PARENTObject.object_id INNER JOIN
    sys.schemas AS PARENTSchemas ON PARENTObject.schema_id = PARENTSchemas.schema_id INNER JOIN
    sys.columns AS ForeignKeyColumn ON sys.foreign_key_columns.parent_object_id = ForeignKeyColumn.object_id AND
    sys.foreign_key_columns.parent_column_id = ForeignKeyColumn.column_id INNER JOIN
    sys.objects AS ForeignKeyTable ON sys.foreign_key_columns.parent_object_id = ForeignKeyTable.object_id INNER JOIN
    sys.columns AS PARENTColumn ON sys.foreign_key_columns.referenced_object_id = PARENTColumn.object_id AND
    sys.foreign_key_columns.referenced_column_id = PARENTColumn.column_id INNER JOIN
    sys.schemas AS ForeignKeySchema ON ForeignKeyTable.schema_id = ForeignKeySchema.schema_id
WHERE (PARENTObject.type = 'U')
AND PARENTSchemas.name = @schemaname
AND PARENTObject.name  = @tablename

-- select * from @TMP

DELETE FROM @TMP WHERE FOREIGNKEY_SchemaName = @tablename AND FOREIGNKEY_TableName =  @schemaname;
--SELECT * FROM #TMP;

DECLARE @n INT = 26;

DECLARE  @PARENT_tableName AS NVARCHAR(MAX);
WHILE (@recursive=1 AND (SELECT count(*) FROM @TMP)>0)
BEGIN
    
    ----SELECT TOP 1 @PARENT_tableName = PARENT_tableName, @sql1 = 'DELETE FROM ' + PARENT_tableName FROM #TMP WHERE FOREIGNKEY_TableName = @tablename AND FOREIGNKEY_SchemaName = @schemaname;
    ----SELECT TOP 1 * FROM #TMP WHERE FOREIGNKEY_TableName = @tablename AND FOREIGNKEY_SchemaName = @schemaname;
    --SELECT @sql1 = 'DELETE FROM ['+FOREIGNKEY_SchemaName+'].['+FOREIGNKEY_TableName+'] WHERE ' + FOREIGNKEY_ColumnName + ' in (SELECT '+PARENT_ColumnName+' FROM ['+PARENT_SchemaName+'].['+PARENT_TableName+'])'
    --FROM #TMP WHERE PARENT_TableName = @tablename AND PARENT_SchemaName = @schemaname;
    --PRINT @sql1;

    SELECT TOP 1
            @rownumber  = rownumber
          , @schemanameNEW = FOREIGNKEY_SchemaName
          , @tablenameNEW  = FOREIGNKEY_TableName
          , @whereNEW      = FOREIGNKEY_ColumnName + ' in (SELECT '+PARENT_ColumnName + @SQLTODelete + ')'
    FROM @TMP
    --SELECT count(*) as n FROM @TMP;
    DELETE FROM @TMP WHERE rownumber=@rownumber;
    --SELECT count(*) as n FROM @TMP;

    --PRINT @rownumber;
    --print @schemaname
    --print @tablename
    --print @where
    IF (@tablename = @tablenameNEW)
        exec sDBACascateDelete @schemanameNEW, @tablenameNEW, @whereNEW, 0
    else
        exec sDBACascateDelete @schemanameNEW, @tablenameNEW, @whereNEW, 1

    -- DELETE FROM #TMP WHERE  FOREIGNKEY_TableName = @tablename AND FOREIGNKEY_SchemaName = @schemaname;
    IF (@n=0) break;
    SET @n = @n - 1;
END

PRINT @sql

END
GO

 

Ideal use :

exec sDBACascateDelete @schemaname='dbo', @tablename='Region', @where='EmployeesID < @EmployeesID'
 

/** RESULT **/

DELETE FROM [dbo].[EmployeeTerritories] WHERE TerritoryID in (SELECT TerritoryID FROM [dbo].[Territories] WHERE RegionID in (SELECT RegionID FROM [dbo].[Region] WHERE EmployeesID < @EmployeesID));
DELETE FROM [dbo].[Territories] WHERE RegionID in (SELECT RegionID FROM [dbo].[Region] WHERE EmployeesID < @EmployeesID);
DELETE FROM [dbo].[Region] WHERE EmployeesID < @EmployeesID;

 

This example the table dbo.[Employees] has a self-reference.
In this case the code should be rewritten because it certainly will not work.

exec sDBACascateDelete @schemaname='dbo', @tablename='Employees', @where='EmployeesID < @EmployeesID'

/* RESULT */
DELETE FROM [dbo].[Employees] WHERE ReportsTo in (SELECT EmployeeID FROM [dbo].[Employees] WHERE EmployeesID < @EmployeesID);
DELETE FROM [dbo].[EmployeeTerritories] WHERE EmployeeID in (SELECT EmployeeID FROM [dbo].[Employees] WHERE EmployeesID < @EmployeesID);
DELETE FROM [dbo].[Order Details] WHERE OrderID in (SELECT OrderID FROM [dbo].[Orders] WHERE EmployeeID in (SELECT EmployeeID FROM [dbo].[Employees] WHERE EmployeesID < @EmployeesID));
DELETE FROM [dbo].[Orders] WHERE EmployeeID in (SELECT EmployeeID FROM [dbo].[Employees] WHERE EmployeesID < @EmployeesID);
DELETE FROM [dbo].[Employees] WHERE EmployeesID < @EmployeesID;

** I use NorthWind database **

 

Les commentaires sont fermés.