| 
                
Please start any new threads on our new 
    site at https://forums.sqlteam.com.  We've got lots of great SQL Server
    experts to answer whatever question you can come up with. 
    
        | 
                
                    | 
                            
                                | Author | Topic |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                        7020 Posts | 
                                            
                                            |  Posted - 2006-10-03 : 22:21:59 
 |  
                                            | The script below can be used to determine the reference levels of all tables in a database in order to be able to create a script to load tables in the correct order to prevent Foreign Key violations.This script returns 3 result sets.  The first shows the tables in order by level and table name.  The second shows tables and tables that reference it in order by table and referencing table.  The third shows tables and tables it references in order by table and referenced table.Tables at level 0 have no related tables, except self-references.  Tables at level 1 reference no other table, but are referenced by other tables.  Tables at levels 2 and above are tables which reference lower level tables and may be referenced by higher levels.  Tables with a level of NULL may indicate a circular reference (example: TableA references TableB and TableB references TableA).Tables at levels 0 and 1 can be loaded first without FK violations, and then the tables at higher levels can be loaded in order by level from lower to higher to prevent FK violations.  All tables at the same level can be loaded at the same time without FK violations.Tested on SQL 2000 only.  Please post any errors found.Edit 2006/10/10:Fixed bug with tables that have multiple references, and moved tables that have only self-references to level 1 from level 0. -- Start of Script - Find_Table_Reference_Levels.sql/*Find Table Reference LevelsThis script finds table references and ranks them by level in orderto be able to load tables with FK references in the correct order.Tables can then be loaded one level at a time from lower to higher.This script also shows all the relationships for each tableby tables it references and by tables that reference it.Level 0 is tables which have no FK relationships.Level 1 is tables which reference no other tables, exceptthemselves, and are only referenced by higher level tablesor themselves.Levels 2 and above are tables which reference lower levelsand may be referenced by higher levels or themselves.*/declare @r table (PK_TABLE nvarchar(200),FK_TABLE nvarchar(200),primary key clustered (PK_TABLE,FK_TABLE))declare @rs table (PK_TABLE nvarchar(200),FK_TABLE nvarchar(200),primary key clustered (PK_TABLE,FK_TABLE))declare @t table (REF_LEVEL int,TABLE_NAME nvarchar(200) not null primary key clustered )declare @table table (TABLE_NAME nvarchar(200) not null primary key clustered )set nocount offprint 'Load tables for database '+db_name()insert into @tableselect	TABLE_NAME = a.TABLE_SCHEMA+'.'+a.TABLE_NAMEfrom	INFORMATION_SCHEMA.TABLES awhere	a.TABLE_TYPE = 'BASE TABLE'	and	a.TABLE_SCHEMA+'.'+a.TABLE_NAME <> 'dbo.dtproperties'order by	1print 'Load PK/FK references'insert into @rselect	distinct	PK_TABLE = 	b.TABLE_SCHEMA+'.'+b.TABLE_NAME,	FK_TABLE = 	c.TABLE_SCHEMA+'.'+c.TABLE_NAMEfrom	INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS a	join	INFORMATION_SCHEMA.TABLE_CONSTRAINTS b	on	a.CONSTRAINT_SCHEMA = b.CONSTRAINT_SCHEMA and	a.UNIQUE_CONSTRAINT_NAME = b.CONSTRAINT_NAME	join	INFORMATION_SCHEMA.TABLE_CONSTRAINTS c	on	a.CONSTRAINT_SCHEMA = c.CONSTRAINT_SCHEMA and	a.CONSTRAINT_NAME = c.CONSTRAINT_NAMEorder by	1,2print 'Make copy of PK/FK references'insert into @rsselect 	*from	@rorder by	1,2print 'Load un-referenced tables as level 0'insert into @tselect	REF_LEVEL = 0,	a.TABLE_NAMEfrom	@table awhere	a.TABLE_NAME not in	(	select PK_TABLE from @r union all 	select FK_TABLE from @r	)order by	1-- select * from @rprint 'Remove self references'delete from @rwhere	PK_TABLE = FK_TABLEdeclare @level intset @level = 0while @level < 100	begin	set @level = @level + 1	print 'Delete lower level references'	delete from @r	where		PK_TABLE in 		( select TABLE_NAME from @t )		or		FK_TABLE in 		( select TABLE_NAME from @t )	print 'Load level '+convert(varchar(20),@level)+' tables'	insert into @t	select		REF_LEVEL =@level,		a.TABLE_NAME	from		@table a	where		a.TABLE_NAME not in		( select FK_TABLE from @r )		and		a.TABLE_NAME not in		( select TABLE_NAME from @t )	order by		1	if not exists (select * from  @r )		begin		print 'Done loading table levels'		print ''		break		end	endprint 'Count of Tables by level'print ''select	REF_LEVEL,	TABLE_COUNT = count(*)from 	@tgroup by	REF_LEVELorder by	REF_LEVELprint 'Tables in order by level and table name'print 'Note: Null REF_LEVEL nay indicate possible circular reference'print ''select	b.REF_LEVEL,	TABLE_NAME = convert(varchar(40),a.TABLE_NAME)from 	@table a	left join	@t b	on a.TABLE_NAME = b.TABLE_NAMEorder by	b.REF_LEVEL,	a.TABLE_NAMEprint 'Tables and Referencing Tables'print ''select	b.REF_LEVEL,	TABLE_NAME = convert(varchar(40),a.TABLE_NAME),	REFERENCING_TABLE =convert(varchar(40),c.FK_TABLE)from 	@table a	left join	@t b	on a.TABLE_NAME = b.TABLE_NAME	left join	@rs c	on a.TABLE_NAME = c.PK_TABLEorder by	a.TABLE_NAME,	c.FK_TABLEprint 'Tables and Tables Referenced'print ''select	b.REF_LEVEL,	TABLE_NAME = convert(varchar(40),a.TABLE_NAME),	TABLE_REFERENCED =convert(varchar(40),c.PK_TABLE)from 	@table a	left join	@t b	on a.TABLE_NAME = b.TABLE_NAME	left join	@rs c	on a.TABLE_NAME = c.FK_TABLEorder by	a.TABLE_NAME,	c.PK_TABLE-- End of ScriptResults from Northwind database: Load tables for database Northwind(13 row(s) affected)Load PK/FK references(13 row(s) affected)Make copy of PK/FK references(13 row(s) affected)Load un-referenced tables as level 0(0 row(s) affected)Remove self references(1 row(s) affected)Delete lower level references(0 row(s) affected)Load level 1 tables(7 row(s) affected)Delete lower level references(9 row(s) affected)Load level 2 tables(4 row(s) affected)Delete lower level references(3 row(s) affected)Load level 3 tables(2 row(s) affected)Done loading table levels Count of Tables by level REF_LEVEL   TABLE_COUNT ----------- ----------- 1           72           43           2(3 row(s) affected)Tables in order by level and table nameNote: Null REF_LEVEL nay indicate possible circular reference REF_LEVEL   TABLE_NAME                               ----------- ---------------------------------------- 1           dbo.Categories1           dbo.CustomerDemographics1           dbo.Customers1           dbo.Employees1           dbo.Region1           dbo.Shippers1           dbo.Suppliers2           dbo.CustomerCustomerDemo2           dbo.Orders2           dbo.Products2           dbo.Territories3           dbo.EmployeeTerritories3           dbo.Order Details(13 row(s) affected)Tables and Referencing Tables REF_LEVEL   TABLE_NAME                               REFERENCING_TABLE                        ----------- ---------------------------------------- ---------------------------------------- 1           dbo.Categories                           dbo.Products2           dbo.CustomerCustomerDemo                 NULL1           dbo.CustomerDemographics                 dbo.CustomerCustomerDemo1           dbo.Customers                            dbo.CustomerCustomerDemo1           dbo.Customers                            dbo.Orders1           dbo.Employees                            dbo.Employees1           dbo.Employees                            dbo.EmployeeTerritories1           dbo.Employees                            dbo.Orders3           dbo.EmployeeTerritories                  NULL3           dbo.Order Details                        NULL2           dbo.Orders                               dbo.Order Details2           dbo.Products                             dbo.Order Details1           dbo.Region                               dbo.Territories1           dbo.Shippers                             dbo.Orders1           dbo.Suppliers                            dbo.Products2           dbo.Territories                          dbo.EmployeeTerritories(16 row(s) affected)Tables and Tables Referenced REF_LEVEL   TABLE_NAME                               TABLE_REFERENCED                         ----------- ---------------------------------------- ---------------------------------------- 1           dbo.Categories                           NULL2           dbo.CustomerCustomerDemo                 dbo.CustomerDemographics2           dbo.CustomerCustomerDemo                 dbo.Customers1           dbo.CustomerDemographics                 NULL1           dbo.Customers                            NULL1           dbo.Employees                            dbo.Employees3           dbo.EmployeeTerritories                  dbo.Employees3           dbo.EmployeeTerritories                  dbo.Territories3           dbo.Order Details                        dbo.Orders3           dbo.Order Details                        dbo.Products2           dbo.Orders                               dbo.Customers2           dbo.Orders                               dbo.Employees2           dbo.Orders                               dbo.Shippers2           dbo.Products                             dbo.Categories2           dbo.Products                             dbo.Suppliers1           dbo.Region                               NULL1           dbo.Shippers                             NULL1           dbo.Suppliers                            NULL2           dbo.Territories                          dbo.Region(19 row(s) affected)CODO ERGO SUM |  |  
                                    | robvolkMost Valuable Yak
 
 
                                    15732 Posts | 
                                        
                                          |  Posted - 2006-10-03 : 23:42:22 
 |  
                                          | Thank you Thank you THANK YOU!!!  I've been wanting something like this for a while, could've SWORN I wrote it a while back but lost it and couldn't figure it out since.BTW, did you create this just for this post?  http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72946Or did you have it kicking around already? |  
                                          |  |  |  
                                    | KristenTest
 
 
                                    22859 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 03:52:12 
 |  
                                          | Excellent script MVJ, and much better than the fragile on we currently use internally.Maybe a reverse sort of Output 1 for people doing a DELETE first?And/or output dummy commands for TRUNCATE / DELETE?Very pedantic point: but the number of levels can be reduced, given that the tables are alphabetical within their group, if child tables are included one level higher if their parent tables are alphabetically earlier.  We do this because we have tables like Order, OrderItem, Customer, CustomerAddress etc. which we prefer to group adjacent (even though one level apart in RI terms) because we find it easier to consider them "at the same time" when we are making decisions about "Do I want to transfer this table" - of course for more than two levels this is little use, unless you get very creative with table names - Customer, CustomerAddress, CustomerAddressDelivery would work I suppose!Kristen |  
                                          |  |  |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                    7020 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 06:00:59 
 |  
                                          | quote:I developed it recently, not for that post.Like you, I was sure I had one, but couldn't seem to find it.  I just finished a new DB model and needed it, so I decided now was a good time to write it.  Figured it would take about 15 minutes tops, but I was a little off on that estimate.CODO ERGO SUMOriginally posted by robvolk
 Thank you Thank you THANK YOU!!!  I've been wanting something like this for a while, could've SWORN I wrote it a while back but lost it and couldn't figure it out since.BTW, did you create this just for this post?  http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72946Or did you have it kicking around already?
 
 |  
                                          |  |  |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                    7020 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 06:08:59 
 |  
                                          | quote:I already thought about a Truncate/Delete script.  It's close to the reverse, but not exactly.  Maybe when I get another 15 minutes.CODO ERGO SUMOriginally posted by Kristen
 Excellent script MVJ, and much better than the fragile on we currently use internally.Maybe a reverse sort of Output 1 for people doing a DELETE first?And/or output dummy commands for TRUNCATE / DELETE?Very pedantic point: but the number of levels can be reduced, given that the tables are alphabetical within their group, if child tables are included one level higher if their parent tables are alphabetically earlier.  We do this because we have tables like Order, OrderItem, Customer, CustomerAddress etc. which we prefer to group adjacent (even though one level apart in RI terms) because we find it easier to consider them "at the same time" when we are making decisions about "Do I want to transfer this table" - of course for more than two levels this is little use, unless you get very creative with table names - Customer, CustomerAddress, CustomerAddressDelivery would work I suppose!Kristen
 
 |  
                                          |  |  |  
                                    | KristenTest
 
 
                                    22859 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 06:25:33 
 |  
                                          | "It's close to the reverse, but not exactly"I could do with an example that would break the "exact reverse" please - we rely on that at present!Kristen |  
                                          |  |  |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                    7020 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 08:48:11 
 |  
                                          | Now that I’ve done the heavy lifting, it would be nice if someone developed a script that used this information to generate a DTS package to load all the tables in a database in the correct order, with proper flow-of-control, error handling, comments, and formatting, of course.  Maybe Peter could take this on when he isn’t busy helping the asshattery contingent.CODO ERGO SUM |  
                                          |  |  |  
                                    | KristenTest
 
 
                                    22859 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 08:59:07 
 |  
                                          | Joking apart does it need to be DTS? or would:INSERT INTO [TargetServer].TargetDatabase.dbo.TargetTableSELECT * FROM [SourceServer].SourceDatabase.dbo.SourceTabledo (for each table, in the right order, of course!)We mechanically generate the SQL scripts for "Synchronising" two databases - using R.I. ordering: a series of DELETE TargetTable for records no longer existent in the Source table, followed by (for each table) UPDATE with huge WHERE clauses comparing all columns, and INSERT for NOT EXISTS in Target database (taking into account any SET IDENTITY_INSERT stuff)I suppose I could get off my lazy ass and make that into a suitable form for general usage ... I wonder if there would be a demand?  Kristen |  
                                          |  |  |  
                                    | SwePesoPatron Saint of Lost Yaks
 
 
                                    30421 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 09:14:15 
 |  
                                          | Got this error: Load PK/FK referencesServer: Msg 2627, Level 14, State 1, Line 53Violation of PRIMARY KEY constraint 'PK__@r__257187A8'. Cannot insert duplicate key in object '#247D636F'.The statement has been terminated.Adding DISTINCT to print 'Load PK/FK references'insert into @rselect DISTINCT	PK_TABLE = 	b.TABLE_SCHEMA+'.'+b.TABLE_NAME,	FK_TABLE = 	c.TABLE_SCHEMA+'.'+c.TABLE_NAMEfrom	INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS a	join	INFORMATION_SCHEMA.TABLE_CONSTRAINTS b	on	a.CONSTRAINT_SCHEMA = b.CONSTRAINT_SCHEMA and	a.UNIQUE_CONSTRAINT_NAME = b.CONSTRAINT_NAME	join	INFORMATION_SCHEMA.TABLE_CONSTRAINTS c	on	a.CONSTRAINT_SCHEMA = c.CONSTRAINT_SCHEMA and	a.CONSTRAINT_NAME = c.CONSTRAINT_NAMEorder by	1,2helped.Peter LarssonHelsingborg, Sweden |  
                                          |  |  |  
                                    | jsmith8858Dr. Cross Join
 
 
                                    7423 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 09:28:20 
 |  
                                          | Beautiful Michael !  thank you !!- Jeff |  
                                          |  |  |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                    7020 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 10:43:21 
 |  
                                          | quote:It would be nice to be able to load tables in parallel where that is possible, and DTS is very good for that sort of thing.  Also, you don't have to worry about linked servers.We have done some work with generating DTS packages dynamically from VB script tasks, but nothing quite as elaborate a what I mentioned.  Mostly things like: Create a package object, add source and target data sources, add a data pump task, execute the task, and get rid of the package object.  It's a useful technique for "table-driven" DTS. CODO ERGO SUMOriginally posted by KristenJoking apart does it need to be DTS?...
 
 |  
                                          |  |  |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                    7020 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 10:48:42 
 |  
                                          | quote:Thanks, Peter.  I had just found that error myself.  It occurs when a table has multiple references to another table.I will revise the script and repost it later.  I'm making some other changes, and I want to wait to see if any other errors show up.CODO ERGO SUMOriginally posted by PesoGot this error...
 
 |  
                                          |  |  |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                    7020 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 11:02:33 
 |  
                                          | quote:Exact reverse will work, it just isn't exactly ideal.For example, the tables that are level 0 (no references either way) in this script will still be level 0, and can be truncated.  The next level would be tables that only reference other tables but are not referenced; these can also be truncated.  After that, each additional level would be tables that are referenced by lower levels only; the data in these must be deleted.CODO ERGO SUMOriginally posted by Kristen
 "It's close to the reverse, but not exactly"I could do with an example that would break the "exact reverse" please - we rely on that at present!Kristen
 
 |  
                                          |  |  |  
                                    | KristenTest
 
 
                                    22859 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 12:03:09 
 |  
                                          | Ah, got you.  Good point about whether you can TRUNCATE or DELETE.  I'm normally not that fussy any TRUNCATE everything, allowing those to fail that will fail, and then keeping running the DELETEs until I get no FK errors - but that's obviously a bit "rough and ready" !Kristen |  
                                          |  |  |  
                                    | madhivananPremature Yak Congratulator
 
 
                                    22864 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 12:26:54 
 |  
                                          | quote:http://sqlteam.com/forums/topic.asp?TOPIC_ID=65341MadhivananFailing to plan is Planning to failOriginally posted by Kristen
 Ah, got you.  Good point about whether you can TRUNCATE or DELETE.  I'm normally not that fussy any TRUNCATE everything, allowing those to fail that will fail, and then keeping running the DELETEs until I get no FK errors - but that's obviously a bit "rough and ready" !Kristen
 
 |  
                                          |  |  |  
                                    | rockmooseSQL Natt Alfen
 
 
                                    3279 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 15:03:50 
 |  
                                          | Great, thanks! could have sworn I had something similar in a recursive tvf. |  
                                          |  |  |  
                                    | Michael Valentine JonesYak DBA Kernel (pronounced Colonel)
 
 
                                    7020 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 16:19:45 
 |  
                                          | quote:What's a "recursive tvf"?CODO ERGO SUMOriginally posted by rockmoose
 Great, thanks! could have sworn I had something similar in a recursive tvf.
 
 |  
                                          |  |  |  
                                    | byrmolShed Building SQL Farmer
 
 
                                    1591 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 16:44:10 
 |  
                                          | I like it Michael.It uses a slightly different technique from my the method I use..http://weblogs.sqlteam.com/davidm/archive/2005/06/17/6069.aspxHere is the output from a db of mine.. MVJ 0	311	452	713	764	205	56	1and mine0	761	712	763	204	55	1Notice the "down shifted" levels...DavidMProduction is just another testing cycle |  
                                          |  |  |  
                                    | rockmooseSQL Natt Alfen
 
 
                                    3279 Posts | 
                                        
                                          |  Posted - 2006-10-04 : 17:03:03 
 |  
                                          | quote:Example:Originally posted by rockmoose
 Great, thanks! could have sworn I had something similar in a recursive tvf.
 
 create function dbo.rectvf(@nestlvl int)returns @nest table(	nest int)asbegin	if @nestlvl = 0		return	insert @nest select @nestlvl	set @nestlvl = @nestlvl -1	insert @nest select nest from dbo.rectvf(@nestlvl)	returnendgoselect * From dbo.rectvf(6)nest        ----------- 654321(6 row(s) affected)rockmoose |  
                                          |  |  |  
                                    | SwePesoPatron Saint of Lost Yaks
 
 
                                    30421 Posts | 
                                        
                                          |  Posted - 2006-10-05 : 04:50:12 
 |  
                                          | quote:OK. You asked for itOriginally posted by Michael Valentine Jones
 Maybe Peter could take this on when he isn’t busy helping the asshattery contingent.
 
  Here is a slightly different approach, which gets the database diagram as a resultset, ready to be put in any client application grid control.Or any ASP page for that matter... SET NOCOUNT ONDECLARE	@Constraints TABLE	(		ConstraintID SMALLINT IDENTITY(0, 1),		UNIQUE_CONSTRAINT_CATALOG NVARCHAR(128),		UNIQUE_CONSTRAINT_SCHEMA NVARCHAR(128),		UNIQUE_CONSTRAINT_NAME NVARCHAR(128),		CONSTRAINT_CATALOG NVARCHAR(128),		CONSTRAINT_SCHEMA NVARCHAR(128),		CONSTRAINT_NAME NVARCHAR(128),		TABLE_CATALOG NVARCHAR(128),		TABLE_SCHEMA NVARCHAR(128),		TABLE_NAME NVARCHAR(128),		COLUMN_NAME NVARCHAR(128),		DATA_TYPE NVARCHAR(128)	)INSERT		@Constraints		(			UNIQUE_CONSTRAINT_CATALOG,			UNIQUE_CONSTRAINT_SCHEMA,			UNIQUE_CONSTRAINT_NAME,			CONSTRAINT_CATALOG,			CONSTRAINT_SCHEMA,			CONSTRAINT_NAME,			TABLE_CATALOG,			TABLE_SCHEMA,			TABLE_NAME,			COLUMN_NAME,			DATA_TYPE		)SELECT		rc.UNIQUE_CONSTRAINT_CATALOG,		rc.UNIQUE_CONSTRAINT_SCHEMA,		rc.UNIQUE_CONSTRAINT_NAME,		tc.CONSTRAINT_CATALOG,		tc.CONSTRAINT_SCHEMA,		tc.CONSTRAINT_NAME,		kcu.TABLE_CATALOG,		kcu.TABLE_SCHEMA,		kcu.TABLE_NAME,		c.COLUMN_NAME,		c.DATA_TYPEFROM		INFORMATION_SCHEMA.TABLE_CONSTRAINTS tcINNER JOIN	INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON kcu.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG			AND kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA			AND kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAMEINNER JOIN	INFORMATION_SCHEMA.COLUMNS c ON c.TABLE_CATALOG = kcu.TABLE_CATALOG			AND c.TABLE_SCHEMA = kcu.TABLE_SCHEMA			AND c.TABLE_NAME = kcu.TABLE_NAME			AND c.COLUMN_NAME = kcu.COLUMN_NAMELEFT JOIN	INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc ON rc.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG			AND rc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA			AND rc.CONSTRAINT_NAME = tc.CONSTRAINT_NAMEWHERE		OBJECTPROPERTY(OBJECT_ID(tc.TABLE_NAME), 'IsMSShipped') = 0		AND tc.CONSTRAINT_TYPE IN ('PRIMARY KEY', 'FOREIGN KEY')DECLARE	@Tables TABLE	(		UNIQUE_CONSTRAINT_CATALOG NVARCHAR(128),		UNIQUE_CONSTRAINT_SCHEMA NVARCHAR(128),		UNIQUE_CONSTRAINT_NAME NVARCHAR(128),		CONSTRAINT_CATALOG NVARCHAR(128),		CONSTRAINT_SCHEMA NVARCHAR(128),		CONSTRAINT_NAME NVARCHAR(128),		TABLE_CATALOG NVARCHAR(128),		TABLE_SCHEMA NVARCHAR(128),		TABLE_NAME NVARCHAR(128),		COLUMN_NAME NVARCHAR(128),		DATA_TYPE NVARCHAR(128)	)INSERT		@Tables		(			UNIQUE_CONSTRAINT_CATALOG,			UNIQUE_CONSTRAINT_SCHEMA,			UNIQUE_CONSTRAINT_NAME,			CONSTRAINT_CATALOG,			CONSTRAINT_SCHEMA,			CONSTRAINT_NAME,			TABLE_CATALOG,			TABLE_SCHEMA,			TABLE_NAME,			COLUMN_NAME,			DATA_TYPE		)SELECT		NULL,		NULL,		NULL,		NULL,		NULL,		NULL,		TABLE_CATALOG,		TABLE_SCHEMA,		TABLE_NAME,		NULL,		NULLFROM		INFORMATION_SCHEMA.TABLESWHERE		OBJECTPROPERTY(OBJECT_ID(TABLE_NAME), 'IsMSShipped') = 0		AND TABLE_TYPE = 'BASE TABLE'DELETE		tFROM		@Tables tINNER JOIN	@Constraints c ON t.TABLE_CATALOG = c.TABLE_CATALOG			AND t.TABLE_SCHEMA = c.TABLE_SCHEMA			AND t.TABLE_NAME = c.TABLE_NAMEDECLARE	@Tree TABLE	(		RowID SMALLINT IDENTITY(0, 1),		RowKey VARBINARY(5966),		Generation SMALLINT,		ConstraintID SMALLINT,		CONSTRAINT_CATALOG NVARCHAR(128),		CONSTRAINT_SCHEMA NVARCHAR(128),		CONSTRAINT_NAME NVARCHAR(128),		TABLE_CATALOG NVARCHAR(128),		TABLE_SCHEMA NVARCHAR(128),		TABLE_NAME NVARCHAR(128),		COLUMN_NAME NVARCHAR(128),		DATA_TYPE NVARCHAR(128)	)INSERT		@Tree		(			Generation,			ConstraintID,			CONSTRAINT_CATALOG,			CONSTRAINT_SCHEMA,			CONSTRAINT_NAME,			TABLE_CATALOG,			TABLE_SCHEMA,			TABLE_NAME,			COLUMN_NAME,			DATA_TYPE		)SELECT		0,		ConstraintID,		CONSTRAINT_CATALOG,		CONSTRAINT_SCHEMA,		CONSTRAINT_NAME,		TABLE_CATALOG,		TABLE_SCHEMA,		TABLE_NAME,		COLUMN_NAME,		DATA_TYPEFROM		@ConstraintsWHERE		UNIQUE_CONSTRAINT_CATALOG IS NULL		AND UNIQUE_CONSTRAINT_SCHEMA IS NULL		AND UNIQUE_CONSTRAINT_NAME IS NULLUNIONSELECT		0,		NULL,		CONSTRAINT_CATALOG,		CONSTRAINT_SCHEMA,		CONSTRAINT_NAME,		TABLE_CATALOG,		TABLE_SCHEMA,		TABLE_NAME,		COLUMN_NAME,		DATA_TYPEFROM		@TablesORDER BY	TABLE_CATALOG,		TABLE_SCHEMA,		TABLE_NAME,		COLUMN_NAMEDELETE		tFROM		@Tree tINNER JOIN	@Constraints c ON c.TABLE_CATALOG = t.TABLE_CATALOG			AND c.TABLE_SCHEMA = t.TABLE_SCHEMA			AND c.TABLE_NAME = t.TABLE_NAME			and c.UNIQUE_CONSTRAINT_CATALOG IS NOT NULL			AND c.UNIQUE_CONSTRAINT_SCHEMA IS NOT NULL			AND c.UNIQUE_CONSTRAINT_NAME IS NOT NULLINNER JOIN	@Tree x ON x.CONSTRAINT_CATALOG = c.UNIQUE_CONSTRAINT_CATALOG			AND x.CONSTRAINT_SCHEMA = c.UNIQUE_CONSTRAINT_SCHEMA			AND x.CONSTRAINT_NAME = c.UNIQUE_CONSTRAINT_NAME			AND x.TABLE_CATALOG = t.TABLE_CATALOG			AND x.TABLE_SCHEMA = t.TABLE_SCHEMA			AND x.TABLE_NAME <> t.TABLE_NAMEDELETE		cFROM		@Constraints cINNER JOIN	@Tree t ON t.ConstraintID = c.ConstraintIDUPDATE	@TreeSET	RowKey = CAST(RowID AS VARBINARY)DECLARE	@Generation SMALLINTSELECT	@Generation = 0WHILE @@ROWCOUNT > 0	BEGIN		SELECT	@Generation = @Generation + 1				INSERT		@Tree				(					RowKey,					Generation,					ConstraintID,					CONSTRAINT_CATALOG,					CONSTRAINT_SCHEMA,					CONSTRAINT_NAME,					TABLE_CATALOG,					TABLE_SCHEMA,					TABLE_NAME,					COLUMN_NAME,					DATA_TYPE				)		SELECT		t.RowKey,				@Generation,				c.ConstraintID,				c.CONSTRAINT_CATALOG,				c.CONSTRAINT_SCHEMA,				c.CONSTRAINT_NAME,				c.TABLE_CATALOG,				c.TABLE_SCHEMA,				c.TABLE_NAME,				c.COLUMN_NAME,				c.DATA_TYPE		FROM		@Constraints c		INNER JOIN	(					SELECT	RowKey,						CONSTRAINT_CATALOG,						CONSTRAINT_SCHEMA,						CONSTRAINT_NAME					FROM	@Tree					WHERE	Generation = @Generation - 1				) t ON t.CONSTRAINT_CATALOG = c.UNIQUE_CONSTRAINT_CATALOG					AND t.CONSTRAINT_SCHEMA = c.UNIQUE_CONSTRAINT_SCHEMA					AND t.CONSTRAINT_NAME = c.UNIQUE_CONSTRAINT_NAME		ORDER BY	c.TABLE_CATALOG,				c.TABLE_SCHEMA,				c.TABLE_NAME,				c.COLUMN_NAME		UPDATE	@Tree		SET	RowKey = RowKey + CAST(RowID AS VARBINARY)		WHERE	Generation = @Generation		UPDATE		t		SET		t.ConstraintID = c.ConstraintID,				t.CONSTRAINT_CATALOG = c.CONSTRAINT_CATALOG,				t.CONSTRAINT_SCHEMA = c.CONSTRAINT_SCHEMA,				t.CONSTRAINT_NAME = c.CONSTRAINT_NAME		FROM		@Tree t		INNER JOIN	@Constraints c ON c.TABLE_CATALOG = t.TABLE_CATALOG					AND c.TABLE_SCHEMA = t.TABLE_SCHEMA					AND c.TABLE_NAME = t.TABLE_NAME		WHERE		t.Generation = @Generation				AND c.UNIQUE_CONSTRAINT_CATALOG IS NULL				AND c.UNIQUE_CONSTRAINT_SCHEMA IS NULL				AND c.UNIQUE_CONSTRAINT_NAME IS NULL		DELETE		c		FROM		@Constraints c		INNER JOIN	@Tree t ON t.ConstraintID = c.ConstraintID	ENDSELECT		Generation [Level],		TABLE_CATALOG,		TABLE_SCHEMA,		TABLE_NAME,		COLUMN_NAME,		DATA_TYPEFROM		@TreeORDER BY	RowKeyPeter LarssonHelsingborg, SwedenEDIT: Added code to include not referenced tables |  
                                          |  |  |  
                                    | KristenTest
 
 
                                    22859 Posts | 
                                        
                                          |  Posted - 2006-10-05 : 05:07:53 
 |  
                                          | Can it be optimised to run a bit faster please Peso?  I can't get it to complete the first step ... those pesky INFORMATION_SCHEMA views   |  
                                          |  |  |  
                                | Next Page |  |  |  |  |