Get all the rows referencing (via foreign keys) a particular row in a table
You can do one of the following:
1) Add reference_count
field to master table. Using triggers on detail tables increase the reference count
whenever a row with this master_id
is added. Decrease the count, when row gets deleted. When reference_count
reaches 0 - delete the record.
2) Use pg_constraint
table (details here) to get the list of referencing tables and create a dynamic SQL query.
3) Create triggers on every detail table, that deletes master_id
in main table. Silence error messages with BEGIN ... EXCEPTION ... END
.
How can I list all foreign keys referencing a given table in SQL Server?
Not sure why no one suggested but I use sp_fkeys
to query foreign keys for a given table:
EXEC sp_fkeys 'TableName'
You can also specify the schema:
EXEC sp_fkeys @pktable_name = 'TableName', @pktable_owner = 'dbo'
Without specifying the schema, the docs state the following:
If pktable_owner is not specified, the default table visibility rules
of the underlying DBMS apply.In SQL Server, if the current user owns a table with the specified
name, that table's columns are returned. If pktable_owner is not
specified and the current user does not own a table with the specified
pktable_name, the procedure looks for a table with the specified
pktable_name owned by the database owner. If one exists, that table's
columns are returned.
How can I find tables which reference a particular row via a foreign key?
NULL values in referencing columns
This query produces the DML statement to find all rows in all tables, where a column has a foreign-key constraint referencing another table but hold a NULL
value in that column:
WITH x AS (
SELECT c.conrelid::regclass AS tbl
, c.confrelid::regclass AS ftbl
, quote_ident(k.attname) AS fk
, quote_ident(pf.attname) AS pk
FROM pg_constraint c
JOIN pg_attribute k ON (k.attrelid, k.attnum) = (c.conrelid, c.conkey[1])
JOIN pg_attribute f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
LEFT JOIN pg_constraint p ON p.conrelid = c.conrelid AND p.contype = 'p'
LEFT JOIN pg_attribute pf ON (pf.attrelid, pf.attnum)
= (p.conrelid, p.conkey[1])
WHERE c.contype = 'f'
AND c.confrelid = 'fk_tbl'::regclass -- references to this tbl
AND f.attname = 'fk_tbl_id' -- and only to this column
)
SELECT string_agg(format(
'SELECT %L AS tbl
, %L AS pk
, %s::text AS pk_val
, %L AS fk
, %L AS ftbl
FROM %1$s WHERE %4$s IS NULL'
, tbl
, COALESCE(pk 'NONE')
, COALESCE(pk 'NULL')
, fk
, ftbl), '
UNION ALL
') || ';'
FROM x;
Produces a query like this:
SELECT 'some_tbl' AS tbl
, 'some_tbl_id' AS pk
, some_tbl_id::text AS pk_val
, 'fk_tbl_id' AS fk
, 'fk_tbl' AS ftbl
FROM some_tbl WHERE fk_tbl_id IS NULL
UNION ALL
SELECT 'other_tbl' AS tbl
, 'other_tbl_id' AS pk
, other_tbl_id::text AS pk_val
, 'some_name_id' AS fk
, 'fk_tbl' AS ftbl
FROM other_tbl WHERE some_name_id IS NULL;
Produces output like this:
tbl | pk | pk_val | fk | ftbl
-----------+--------------+--------+--------------+--------
some_tbl | some_tbl_id | 49 | fk_tbl_id | fk_tbl
some_tbl | some_tbl_id | 58 | fk_tbl_id | fk_tbl
other_tbl | other_tbl_id | 66 | some_name_id | fk_tbl
other_tbl | other_tbl_id | 67 | some_name_id | fk_tbl
Does not cover multi-column foreign or primary keys reliably. You have to make the query more complex for this.
I cast all primary key values to
text
to cover all types.Adapt or remove these lines to find foreign key pointing to an other or any column / table:
AND c.confrelid = 'fk_tbl'::regclass
AND f.attname = 'fk_tbl_id' -- and only this columnTested with PostgreSQL 9.1.4. I use the
pg_catalog
tables. Realistically nothing of what I use here is going to change, but that is not guaranteed across major releases. Rewrite it with tables frominformation_schema
if you need it to work reliably across updates. That is slower, but sure.I did not sanitize table names in the generated DML script, because
quote_ident()
would fail with schema-qualified names. It is your responsibility to avoid harmful table names like"users; DELETE * FROM users;"
. With some more effort, you can retrieve schema-name and table name separately and usequote_ident()
.
NULL values in referenced columns
My first solution does something subtly different from what you ask, because what you describe (as I understand it) is non-existent. The value NULL
is "unknown" and cannot be referenced. If you actually want to find rows with a NULL
value in a column that has FK constraints pointing to it (not to the particular row with the NULL
value, of course), then the query can be much simplified:
WITH x AS (
SELECT c.confrelid::regclass AS ftbl
,quote_ident(f.attname) AS fk
,quote_ident(pf.attname) AS pk
,string_agg(c.conrelid::regclass::text, ', ') AS referencing_tbls
FROM pg_constraint c
JOIN pg_attribute f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
LEFT JOIN pg_constraint p ON p.conrelid = c.confrelid AND p.contype = 'p'
LEFT JOIN pg_attribute pf ON (pf.attrelid, pf.attnum)
= (p.conrelid, p.conkey[1])
WHERE c.contype = 'f'
-- AND c.confrelid = 'fk_tbl'::regclass -- only referring this tbl
GROUP BY 1, 2, 3
)
SELECT string_agg(format(
'SELECT %L AS ftbl
, %L AS pk
, %s::text AS pk_val
, %L AS fk
, %L AS referencing_tbls
FROM %1$s WHERE %4$s IS NULL'
, ftbl
, COALESCE(pk, 'NONE')
, COALESCE(pk, 'NULL')
, fk
, referencing_tbls), '
UNION ALL
') || ';'
FROM x;
Finds all such rows in the entire database (commented out the restriction to one table). Tested with Postgres 9.1.4 and works for me.
I group multiple tables referencing the same foreign column into one query and add a list of referencing tables to give an overview.
SQL get ALL rows which share foreign keys
With EXISTS:
SELECT m.* FROM mytable m
WHERE EXISTS (
SELECT 1 FROM mytable
WHERE id <> m.id AND fk_id = m.fk_id
)
See the demo.
Or with COUNT() window function:
SELECT m.id, m.fk_id
FROM (
SELECT *, COUNT(id) OVER (PARTITION BY fk_id) counter
FROM mytable
) m
WHERE m.counter > 1
See the demo.
Results:
| id | fk_id |
| --- | ----- |
| 2 | 200 |
| 3 | 200 |
| 4 | 300 |
| 5 | 300 |
| 6 | 300 |
Fetch all rows for a foreign key if even one row matches condition
You could also use an INNER JOIN
to accomplish this instead of a WHERE id IN...
statement:
SELECT A.ID,
A.Name,
A.Value
FROM props A
INNER JOIN (SELECT ID
FROM props
WHERE (Name = 'mat' AND value < 50)) B ON B.ID = A.ID
So in B
get all the ID
s that match the criteria then simply join them to the props
table (alias A
).
How do I see all foreign keys to a table or column?
For a Table:
SELECT
TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME
FROM
INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE
REFERENCED_TABLE_SCHEMA = '<database>' AND
REFERENCED_TABLE_NAME = '<table>';
For a Column:
SELECT
TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME
FROM
INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE
REFERENCED_TABLE_SCHEMA = '<database>' AND
REFERENCED_TABLE_NAME = '<table>' AND
REFERENCED_COLUMN_NAME = '<column>';
Basically, we changed REFERENCED_TABLE_NAME with REFERENCED_COLUMN_NAME in the where clause.
How to find foreign key dependencies of a specific row?
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
How to get specific row data from 3 table using foreign key?
Add where [conditions]
to the end of your query to get what you want:
For example, to get only the data where id=2
SELECT
airport.id, airport.airport_name, runway.id, runway.runway_no, runway.runway_length, runway.runway_width
FROM
airport
INNER JOIN
runway
ON
airport.id=runway.id
where airport.id=2
How do I find all references from other tables to a specific row?
You're going to have to query each of the other tables.
I would do it as a UNION query:
SELECT id, "schools" as whichTable from schools where address_id=1
UNION
SELECT id, "parks" as whichTable from parks where address_id=1
UNION
...
so that you only have to run one query and get back the results as a single dataset that you work with.
If you have a list of tables (or a table of tables), you could generate the query programmaticly -- that would save you having to update your query when the tables are changed.
Related Topics
Cannot Delete and Update Records on Access Linked Table
Firstname, Lastname in Sql, Too Complex
Sql Server Bigint or Decimal(18,0) for Primary Key
Oracle SQL Merge to Insert and Delete But Not Update
Composing Database.Esqueleto Queries, Conditional Joins and Counting
Sqlite Like & Order by Match Query
Date Split-Up Based on Fiscal Year
Export Data Frame to SQL Server Using Rodbc Package
Datareader.Getfieldtype Returned Null
Datetime Query on Only Year in SQL Server
T-Sql: Comparing Two Tables - Records That Don't Exist in Second Table
Grouping by Date, Return Row Even If No Records Found
Select Multiple Rows from Single Column into Single Row
Insert Binary File into Mssql Db (Varbinary) with Python Pymssql
Are SQL Queries Guaranteed to Execute Atomically When Using Union
Inserting a String with Double Quotes into a Table
T/F: Using If Statements in a Procedure Produces Multiple Plans