Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find foreign key dependencies of a specific row?

Tags:

sql-server

If I have a table, TableA:

Id
1
2
3
...

And two other tables:

TableB:

Id, TableAId
1   1
2   1

TableC:

Id, TableAId
1,  1
2,  2

Where TableAId is a FK relationship with TableA.Id.

How do I determine that TableA, Id 1, has three rows pointing to it? And that TableA, Id 2 has one row pointing to it? And more specifically, how do I identify what those rows are? (their table name and Id)

like image 545
Kirk Woll Avatar asked Sep 30 '11 21:09

Kirk Woll


2 Answers

You can use the INFORMATION_SCHEMA views to generate select statements to display the rows in question. I have only tested this against the tables provided in the question, but it could be expanded to work in cases where the keys are multiple columns.

declare @table_schema nvarchar(50) = 'dbo',
        @table_name nvarchar(50) = 'TableA',
        @id int = 1

select fk_col.TABLE_SCHEMA, fk_col.TABLE_NAME, fk_col.COLUMN_NAME, 
    'select * from ' + fk_col.TABLE_SCHEMA + '.' + fk_col.TABLE_NAME + ' t1 '
        + ' inner join ' + @table_schema + '.' + @table_name + ' t2 '
        + ' on t1.' + fk_col.COLUMN_NAME + ' = t2.' + pk_col.COLUMN_NAME
        + ' where t2.' + pk_col.COLUMN_NAME + ' = ' + cast(@id as nvarchar)

from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk

    join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE pk_col
        on pk.CONSTRAINT_SCHEMA = pk_col.CONSTRAINT_SCHEMA
        and pk.CONSTRAINT_NAME = pk_col.CONSTRAINT_NAME

    join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS fk 
        on pk.CONSTRAINT_SCHEMA = fk.UNIQUE_CONSTRAINT_SCHEMA 
        and pk.CONSTRAINT_NAME = fk.UNIQUE_CONSTRAINT_NAME

    join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE fk_col
        on fk_col.CONSTRAINT_SCHEMA = fk.CONSTRAINT_SCHEMA
        and fk_col.CONSTRAINT_NAME = fk.CONSTRAINT_NAME

where pk.TABLE_SCHEMA = @table_schema 
    and pk.TABLE_NAME = @table_name
    and pk.CONSTRAINT_TYPE = 'PRIMARY KEY'

The select statements generated:

select * from dbo.TableB t1  inner join dbo.TableA t2  on t1.TableAId = t2.Id where t2.Id = 1
select * from dbo.TableC t1  inner join dbo.TableA t2  on t1.TableAId = t2.Id where t2.Id = 1

and the query results:

Id          TableAId    Id
----------- ----------- -----------
1           1           1
2           1           1

Id          TableAId    Id
----------- ----------- -----------
1           1           1
like image 137
Jeff Ogata Avatar answered Oct 10 '22 06:10

Jeff Ogata


I don't have SQL on this computer so I can't give You exact code but here is the way you should go. Please, note that I will use SQL Server terminology.

I see no way to do this without dynamic sql, at least in SQL Server.

  1. Create temporary table #t with columns FK_TBL_NM, FK_CLMN_VAL.
  2. It should not be difficult to get all the fk relationships for pk of TableA:

    SELECT *  FROM sys.foreign_keys 
    WHERE referenced_object_id = OBJECT_ID('TableA') 
    
  3. Use a cursor to iterate thru the result of this query and for each generate and execute dynamic sql that will join TableA and table retrieved from cursor and returns FK_TBL_NM (TableB, TableC, ...) and value of fk column.

  4. Insert the result into #t (it is tricky to get dynamic sql result into table but do a research on stackoverflow)

Now you have the table that contains one row for each row in TableB, TableC, ...

I know this is feasible because I wrote the code with similar logic for my current project at work just few days ago.

Note that you should probably make your code work with pk/fk with more that one column. Also there are different data types for columns. It complicates things a bit but it is possible.

Every step I listed above is not difficult to implement. However, if you have any difficulties, search on stackoverflow :)

like image 29
Filip Popović Avatar answered Oct 10 '22 07:10

Filip Popović