Hierarchical List of All tables

  • In a SQL Server DB, i have to find all the "Master"(Parent) tables and also build a

    Hierarchical list of Paerent/Child tables. Finally i would like to traverse that Hierarchial

    list from down and delete all the child table data at the end i can able to delete

    the parent data also.

    I have tried in one way, that is, Using system tables (like sys.objects etc) i

    queried the metadata of the db (like its primary and Foreign keys). But i dont know how

    to formulate the tree like structure.. Pls help in this regard.

    Thank U.

  • actually, MS provides a stored procedure that will give you all objects(procs, views, tables, etc) in dependency order:

    try this in SSMS:

    EXEC sp_msdependencies @intrans = 1

    if you insert the results into a temp table, you could then filter it to be just tables, just views, or use the other, alternative parameters for the proc to do the same thing

    EXEC sp_msdependencies @intrans = 1 ,@objtype=8 --8 = tables

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • how about

    ;

    ;

    with cteTbHierarchy

    as ( /* Select all table without (selfreferencing) FK */

    select distinct

    1 as LevelID

    , OBJECT_SCHEMA_NAME(Parent.object_id) as TableOwner

    , Parent.name as TableName

    , Parent.object_id as TbObjID

    from sys.objects Parent

    left join sys.foreign_key_columns RefKey

    On RefKey.parent_object_id = Parent.object_id

    and RefKey.parent_object_id <> RefKey.referenced_object_id

    and RefKey.constraint_column_id = 1

    where RefKey.parent_object_id is null

    and Parent.type = 'U'

    and Parent.name <> 'dtproperties'

    UNION ALL

    /* add tables that reference the anchor rows */

    SELECT H.LevelID + 1

    , OBJECT_SCHEMA_NAME(Parent.object_id) as TableOwner

    , OBJECT_NAME(Parent.object_id) as TableName

    , Parent.object_id as TbObjID

    from sys.objects Parent

    inner join sys.foreign_key_columns RefKey

    On RefKey.parent_object_id = Parent.object_id

    and RefKey.parent_object_id <> RefKey.referenced_object_id

    and RefKey.constraint_column_id = 1

    inner join cteTbHierarchy H

    on H.TbObjID = RefKey.referenced_object_id

    where Parent.type = 'U'

    and Parent.name <> 'dtproperties'

    )

    select *

    from cteTbHierarchy

    order by LevelID desc -- descending order = order of row deletes

    , TableOwner

    , TableName ;

    Johan

    Learn to play, play to learn !

    Dont drive faster than your guardian angel can fly ...
    but keeping both feet on the ground wont get you anywhere :w00t:

    - How to post Performance Problems
    - How to post data/code to get the best help[/url]

    - How to prevent a sore throat after hours of presenting ppt

    press F1 for solution, press shift+F1 for urgent solution 😀

    Need a bit of Powershell? How about this

    Who am I ? Sometimes this is me but most of the time this is me

  • Hi Lowell and Alzdba,

    Both guys gave to me an excellent way to built a hierarchy of tables.

    Alzdba, your script includes a duplicates of record also. I dont know where it have defect. Pls take a look on it.

    Lowell's (Microsoft's) way gave a nice solution.

    Now the things get more complex. That is my situation or condition changed.After fetching the hierarchy, i'm not going to delete entire database tables from traversing the leaf node. For example, my 1st level (Master) table contains a specific column. consider, if that column value is 0 then we have to delete the record from that Master table.

    But the problem is, when we delete that record, since its a master table we MUST delete the respective related records in Child tables FIRST. My question is the above Microsoft SP return a full set of hierarchy. Its fine. How we can start the delete process from the Master table? Put it in other way, how we can traverse from

    the Leaf child and go to top (may be we can traverse up levels using Foreign key relations) and delete those records from starting Child to Master tables?

  • How we can start the delete process from the Master table? Put it in other way, how we can traverse from

    the Leaf child and go to top (may be we can traverse up levels using Foreign key relations) and delete those records from starting Child to Master tables?

    have a look of DELETE CASCADE option

    ----------
    Ashish

  • Hi Ashish,

    Thanks for ur reply. But the fact is, in my db there is no cascde delete option. (i.e., we didnt used that one or enabled that one). Is there any other way other than this?

    Thanks.

  • you can build your tree based on the foreign keys, but the decision on what to do with rows of data containing FK references is still a business decision. Once business can decide that "data in this row , the FK should be set to null" or"this row can be deleted", you could fix the foreign keys to do that for you with the powerful CASCADE options mentioned above.

    the problem is, you cannot(well.. SHOULD not) just assume that any row int eh database that has a FK can be deleted;, someone has to review the data for consistency.

    for example, suppose an invoice(master record) is going to be deleted.

    but if there is a payroll record which kept track of Invoices "Jack" was working on exists...and the sum of those line items, in another table, decides "Jacks" commission.

    you blindly delete all child rows with a reference to that invoice...now the commissions are off, overall payroll is off, company profit is off, and noone knows why, "Jack" was paid too much and HR wants the money back, nor can they track down why he was overpaid in the first place.

    this specific scenario might not be yours, but the idea is the same...deleting data due to FK constraints can have unintended consequences unless you think it through.

    anyway, you seem pretty sure you want to delete all the child data, so here's another helpful hint.

    another built in MS option is to run "EXEC sp_fkeys YourTableName"

    that lists all the tables(and their columns) that have a FK to your table, which might be a better starting point for identifying data to delete from child tables.

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • You can disable all contraint from table

    EXEC sp_MSforeachtable @command1="ALTER TABLE ? NOCHECK CONSTRAINT ALL"

    delete data from table

    enable all contrainst

    EXEC sp_MSforeachtable @command1="ALTER TABLE ? CHECK CONSTRAINT ALL"

  • Hi Lowell,

    Excellent Answer Lowell. You are 100% correct. So if after getting the hierarchy

    with the SP "sp_msdependencies" i started from the last level. Consider the level is 10.

    I have to pass the (10th level) table to the "sp_fkeys" SP and find out the Tables which meant

    we have to made a recursive call. Am i correct?

    Lowell, Could you able to write some line of code so that i can catch the remaining...

    Thanks

  • ok see if this is even close to what you are looking for;

    i'm assuming you need to generate the delete statements because you know a certain key must be deleted in some master table.

    this generates two levels of deletes: the direct child tables, and potentially grandchildren tables;

    SELECT

    DeleteID = 1,

    RefID = conz.referenced_object_id,

    refTable = QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.referenced_object_id)),

    refColumn = ParentColz.name ,

    childID = conz.parent_object_id,

    childTable = QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.parent_object_id)),

    childColumn = ChildColz.name,

    cmd = 'DELETE FROM ' + QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.parent_object_id))

    + ' WHERE ' + ChildColz.name + ' IN (SELECT ' + ParentColz.name

    + ' FROM ' + QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.referenced_object_id)) + ' WHERE ' + ParentColz.name + ' = 0 ) --change to the correct value'

    INTO #Delete

    FROM sys.foreign_keys conz

    INNER JOIN sys.foreign_key_columns FKcolz

    ON conz.object_id = FKcolz.constraint_object_id

    INNER JOIN sys.columns ChildColz

    ON FKcolz.parent_object_id = ChildColz.object_id

    AND FKcolz.parent_column_id = ChildColz.column_id

    INNER JOIN sys.columns ParentColz

    ON FKcolz.referenced_object_id = ParentColz.object_id

    AND FKcolz.referenced_column_id = ParentColz.column_id

    WHERE conz.referenced_object_id = object_id('dbo.GMACT')

    --now, potentially, these child table rows cannot be deleted if they, themseleves, have foreign keys.

    --add them to the list

    INSERT INTO #DELETE

    SELECT

    DeleteID = 2,

    RefID = conz.referenced_object_id,

    refTable = QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.referenced_object_id)),

    refColumn = ParentColz.name ,

    childID = conz.parent_object_id,

    childTable = QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.parent_object_id)),

    childColumn = ChildColz.name,

    cmd = 'DELETE FROM ' + QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.parent_object_id))

    + ' WHERE ' + ChildColz.name + ' IN (SELECT ' + ParentColz.name

    + ' FROM ' + QUOTENAME(schema_name(conz.schema_id)) + '.' + QUOTENAME(object_name(conz.referenced_object_id)) + ' WHERE ' + ParentColz.name + ' = 0 ) --change to the correct value'

    FROM sys.foreign_keys conz

    INNER JOIN sys.foreign_key_columns FKcolz

    ON conz.object_id = FKcolz.constraint_object_id

    INNER JOIN sys.columns ChildColz

    ON FKcolz.parent_object_id = ChildColz.object_id

    AND FKcolz.parent_column_id = ChildColz.column_id

    INNER JOIN sys.columns ParentColz

    ON FKcolz.referenced_object_id = ParentColz.object_id

    AND FKcolz.referenced_column_id = ParentColz.column_id

    WHERE conz.referenced_object_id IN (SELECT childID FROM #DELETE)

    --how many hierarchys / how deep does the rabbit hole go?

    SELECT cmd FROM #DELETE ORDER BY DELETEID DESC

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • Hi lowell

    thank you very much for your response. For example, if i have more than 5 levels means we have to recursive the above statement. Am i correct? Had any idea on this?

    For example, if i have a master tables A and B.

    A B

    C D F

    E G H

    The above is the table hierarchy means, how can we traverse?

    Master Child Grandchild

    A C,D E

    C,D E -

    B F G,H

    F G,H -

    The Top-most master table can be identified by a spl column, say "SplCol". If that is

    0, means we have to delete the respective columns in the child tables. This is my actual

    scenario. Pls help.

  • the # levels returned by any of our queries for hierarchys above can be more than the number of actual queries needed to delete from a parent table....it's very much dependandt on the data itself.

    for example, suppose you have and a table TBPERSON that contains info about a person, and it appears at level "10" in the hierarchy above.

    a table that refers to that person TBCHILD might appear at level 15.

    you can delete from TBCHILD, and then TBPERSON in two statements, and the levels never enter into teh equation...

    that's because, for example, if each of my fictitious tables have OTHER foreign keys to other lookup tables...tbstate, tbcity, tbzipcode.

    those tables, when added to the total hierarchy add more levels, but do not affect the direct FK between those two tables.

    that's why the last post i made is following the FK's from one table to the next level, because the levels don't really mean anything to a single table...its for the overall database if you were deleting all rows from all tables that it becomes important.

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • ALZDBA (3/22/2011)


    how about

    ;

    ;

    with cteTbHierarchy

    as ( /* Select all table without (selfreferencing) FK */

    select distinct

    1 as LevelID

    , OBJECT_SCHEMA_NAME(Parent.object_id) as TableOwner

    , Parent.name as TableName

    , Parent.object_id as TbObjID

    from sys.objects Parent

    left join sys.foreign_key_columns RefKey

    On RefKey.parent_object_id = Parent.object_id

    and RefKey.parent_object_id <> RefKey.referenced_object_id

    and RefKey.constraint_column_id = 1

    where RefKey.parent_object_id is null

    and Parent.type = 'U'

    and Parent.name <> 'dtproperties'

    UNION ALL

    /* add tables that reference the anchor rows */

    SELECT H.LevelID + 1

    , OBJECT_SCHEMA_NAME(Parent.object_id) as TableOwner

    , OBJECT_NAME(Parent.object_id) as TableName

    , Parent.object_id as TbObjID

    from sys.objects Parent

    inner join sys.foreign_key_columns RefKey

    On RefKey.parent_object_id = Parent.object_id

    and RefKey.parent_object_id <> RefKey.referenced_object_id

    and RefKey.constraint_column_id = 1

    inner join cteTbHierarchy H

    on H.TbObjID = RefKey.referenced_object_id

    where Parent.type = 'U'

    and Parent.name <> 'dtproperties'

    )

    select *

    from cteTbHierarchy

    order by LevelID desc -- descending order = order of row deletes

    , TableOwner

    , TableName ;

    Hi ALZDBA,

    can i ask question here,i am using bellow qurey to get all relationship tables and i am getting all the relation ship tables also now what i wan to do is i wanto get reference level of all the tables which tables are i am getting from bellow qurey. My problem is i tried some ways to get that refernce level but i am not sucessed so please could you help me to get that

    DECLARE @TABLENAME VARCHAR(100)='PROJECTDOCUMENT';

    with Ancestors_cte (CHILD,PARENT)

    as

    (

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f where OBJECT_NAME(f.referenced_object_id)=@TABLENAME

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join Ancestors_cte a on a.CHILD=OBJECT_NAME(f.referenced_object_id))

    ,

    parent_CTE(CHILD,PARENT)

    AS

    (

    select CHILD,PARENT

    from Ancestors_cte

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join parent_CTE p on p.PARENT=OBJECT_NAME(f.parent_object_id))

    ,

    parent_child_CTE(CHILD,PARENT)

    AS

    (

    select CHILD,PARENT

    from parent_CTE

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join parent_CTE p on p.PARENT=OBJECT_NAME(f.referenced_object_id))

    ,

    child_subchild_CTE(CHILD,PARENT)

    AS

    (

    select CHILD,PARENT

    from parent_child_CTE

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join parent_child_CTE cs on cs.CHILD=OBJECT_NAME(f.referenced_object_id))

    ,

    sp_table_parent_CTE (CHILD,PARENT)

    as

    (

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f where OBJECT_NAME(f.parent_object_id)=@TABLENAME

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join sp_table_parent_CTE sp on sp.PARENT=OBJECT_NAME(f.parent_object_id))

    ,

    sp_table_parent_child_CTE(CHILD,PARENT)

    AS

    (

    select CHILD,PARENT

    from sp_table_parent_CTE

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join sp_table_parent_CTE spcp on spcp.PARENT=OBJECT_NAME(f.referenced_object_id))

    ,

    sp_table_child_CTE (CHILD,PARENT)

    as

    (

    select CHILD,PARENT

    from sp_table_parent_CTE

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join sp_table_parent_child_CTE spcp on spcp.CHILD=OBJECT_NAME(f.referenced_object_id))

    ,

    sp_table_child_parent_CTE (CHILD,PARENT)

    as

    (

    select CHILD,PARENT

    from sp_table_child_CTE

    UNION ALL

    select OBJECT_NAME(f.parent_object_id)as child,OBJECT_NAME(f.referenced_object_id)as parent

    from sys.foreign_keys f join sp_table_child_CTE spc on spc.CHILD=OBJECT_NAME(f.parent_object_id))

    select * into #result from Ancestors_cte

    union

    select * from parent_CTE

    union

    select * from parent_child_CTE

    union

    select * from child_subchild_CTE

    union

    select * from sp_table_parent_CTE

    union

    select * from sp_table_parent_child_CTE

    union

    select * from sp_table_child_CTE

    union

    select * from sp_table_child_parent_CTE

    OPTION (MAXRECURSION 0)

  • manju3606 (1/4/2012)


    Hi ALZDBA,

    can i ask question here,i am using bellow qurey to get all relationship tables and i am getting all the relation ship tables also now what i wan to do is i wanto get reference level of all the tables which tables are i am getting from bellow qurey. My problem is i tried some ways to get that refernce level but i am not sucessed so please could you help me to get that

    ...

    The way I troubleshoot that kind of queries is I add a column and provide trail info.

    e.g.

    with Ancestors_cte

    as (

    select OBJECT_NAME(f.parent_object_id) as child

    , OBJECT_NAME(f.referenced_object_id) as parent

    , 0 as ReferenceLevel

    from sys.foreign_keys f

    where OBJECT_NAME(f.referenced_object_id) = @TABLENAME

    UNION ALL

    select OBJECT_NAME(f.parent_object_id) as child

    , OBJECT_NAME(f.referenced_object_id) as parent

    , 1

    from sys.foreign_keys f

    join Ancestors_cte a

    on a.CHILD = OBJECT_NAME(f.referenced_object_id)

    ) ,

    parent_CTE

    AS (

    select CHILD

    , PARENT

    , ancestors_cte.ReferenceLevel

    from Ancestors_cte

    UNION ALL

    select OBJECT_NAME(f.parent_object_id) as child

    , OBJECT_NAME(f.referenced_object_id) as parent

    , 2

    from sys.foreign_keys f

    join parent_CTE p

    on p.PARENT = OBJECT_NAME(f.parent_object_id)

    ) ,

    This way you can discover where the data comes from and optimize your query from there on.

    btw please don't use object names to join or link your data !

    use the object_id info !!

    The reason is when you use object_name, you _must_ use object_schema_name as well !!

    Object_id is the unique identity key for an object and links to the combination "schemaname.objectname"

    Johan

    Learn to play, play to learn !

    Dont drive faster than your guardian angel can fly ...
    but keeping both feet on the ground wont get you anywhere :w00t:

    - How to post Performance Problems
    - How to post data/code to get the best help[/url]

    - How to prevent a sore throat after hours of presenting ppt

    press F1 for solution, press shift+F1 for urgent solution 😀

    Need a bit of Powershell? How about this

    Who am I ? Sometimes this is me but most of the time this is me

  • Is it possible to get insert stmt using similar logic?

    Like if I want to transfer data (from prod to test env) for a given AccountID from Accounts table.. I should be able to pull all child tables in the hierarchy..

    Thanks

Viewing 15 posts - 1 through 15 (of 15 total)

You must be logged in to reply to this topic. Login to reply