Find referenced field(s) of foreign key constraint - sql

I have a table Users with a field called org_id which is a foreign key to a table organisation, with primary key field organisation_id. Knowing the table name (users) and the field name (users.org_id), is there a query that can tell me the name and field that org_id references?
I've found a Stackoverflow post similar to this where a query was provided to determine the referenced table name, but I also need to know the field name that is referenced:
SELECT c.confrelid::regclass::text AS referenced_table
,c.conname AS fk_name
,pg_get_constraintdef(c.oid) AS fk_definition
FROM pg_attribute a
JOIN pg_constraint c ON (c.conrelid, c.conkey[1]) = (a.attrelid, a.attnum)
WHERE a.attrelid = '"Schema"."Users"'::regclass -- table name
AND a.attname = 'org_id' -- column name
AND c.contype = 'f'
ORDER BY conrelid::regclass::text, contype DESC;
So the above query would return the name of the table (organisation), the fk name and fk definition. Is there a way to also get the name of the field that is referenced? I know I could probably perform another query to determine the name of pk given a table but I would like to avoid performing multiple queries for this.

This query adds the referenced column(s) for the foreign key constraint:
SELECT c.confrelid::regclass::text AS referenced_table
,string_agg(f.attname, ', ') AS referenced_columns
,c.conname AS fk_name
,pg_get_constraintdef(c.oid) AS fk_definition
FROM pg_attribute a
JOIN pg_constraint c ON (c.conrelid, c.conkey[1]) = (a.attrelid, a.attnum)
JOIN pg_attribute f ON f.attrelid = c.confrelid
AND f.attnum = ANY (confkey)
WHERE a.attrelid = '"Schema"."Users"'::regclass -- table name
AND a.attname = 'org_id' -- column name
AND c.contype = 'f'
GROUP BY c.confrelid, c.conname, c.oid;
A fk constraint can reference multiple columns. That's the reason for the aggregate function string_agg() in the query.

Related

How to list all columns an PK name for relevant columns in Oracle

I want to display all columns of a table and the Primary Key name for a column, if that column belongs to the PK.
So, expected final result will be a 2 column output, the first being the column_name and the second being constraint_name, where constraint_type='P'. For those columns that are not part of the PK, the second column in the display resultset will be NULL and for those columns that are part of the PK, the second column in the resultset will show the PK name.
I am trying a combination of OUTER JOIN involving DBA_TAB_COLS, DBA_CONSTRAINTS, DBA_CONS_COLUMNS, but not getting the desired result.
I am looking for all columns to be listed and not just PK columns.
First join the primary key constraint (dba_constraints) to the constraint columns (dba_cons_columns). Then, outer join that result to all columns in the table (dba_tab_cols).
SELECT dtc.column_name
,iv.constraint_name
FROM dba_tab_cols dtc
LEFT JOIN (
SELECT dcc.owner
,dcc.table_name
,dcc.column_name
,dcc.constraint_name
FROM dba_constraints dc
JOIN dba_cons_columns dcc ON (dcc.owner = dc.owner
AND dcc.constraint_name = dc.constraint_name)
WHERE dc.constraint_type = 'P'
) iv ON (dtc.owner = iv.owner
AND dtc.table_name = iv.table_name
AND dtc.column_name = iv.column_name)
WHERE dtc.hidden_column = 'NO'
AND dtc.owner = ?
AND dtc.table_name IN (?)

How to know if a foreign key has cascade on delete clause

Due to a misconception of the PostgreSQL database I work with everyday, on the 155 tables that the schema of the database contains, some tables with foreign key doesn't have the 'on delete cascade' clause which cause serious trouble as you can expect.
I would like to correct this and with PGAdmin I'm able to see all FK of a table but not if the FK as the clause. Instead of, for each tables, dropping manually each FK and creating new ones with the 'on delete cascade', I would like to know how, for a specific table, the full definition of the foreign keys. Like this I will not drop foreign keys that already has the clause.
You can look in pg_constraint:
postgres=# create table car (id int primary key, name text);
CREATE TABLE
postgres=# create table driver (id int, car int references car(id) on delete cascade);
CREATE TABLE
postgres=# select connamespace, conname, c.relname as child_table, p.relname as parent_table, confdeltype
from pg_constraint
join pg_class c on c.oid=conrelid
join pg_class p on p.oid=confrelid;
connamespace | conname | child_table | parent_table | confdeltype
--------------+-----------------+-------------+--------------+-------------
2200 | driver_car_fkey | driver | car | c
(1 row)
This will show you all the foreign keys (along with their source and target tables) that do not have ON DELETE CASCADE:
select connamespace, conname, c.relname as child_table, p.relname as parent_table, confdeltype
from pg_constraint
join pg_class c on c.oid=conrelid
join pg_class p on p.oid=confrelid
where confdeltype <> 'c';
More information in the documentation
Disclosure: I work for EnterpriseDB (EDB)
This query will show all foreign constraints that are defined without cascading delete and the tables on which they are defined:
SELECT conname AS constraint_name,
conrelid::regclass AS table
FROM pg_constraint
WHERE contype = 'f'
AND confdeltype <> 'c';
Constraints can be queried from pg_constraint. Foreign key constraints have an 'f' in the column contype. The delete action can be taken from the column confdeltype. For cascading action it holds a 'c'. So you want all constraints where contype is 'f' and confdeltype isn't 'c'.
You can join pg_class for the involved tables and pg_namespace for their schemas. That way you can also limit the search to the tables of a specific schema.
In conkey the referencing and in confkey the referenced are stored as array of the ordinal numbers of the columns in the respective table. You can get the names from pg_attribute.
The following query would give you all foreign key constraints on any table in the schema public which's delete action isn't cascade along with the tables' schema names and the columns involved in the constraint.
SELECT con.conname,
con.confdeltype,
nsp.nspname,
rel.relname,
(SELECT array_agg(att.attname ORDER BY un.ord)
FROM unnest(con.conkey) WITH ORDINALITY un (attnum, ord)
INNER JOIN pg_attribute att
ON att.attnum = un.attnum
WHERE att.attrelid = rel.oid) conkeyattnames,
fnsp.nspname,
frel.relname,
(SELECT array_agg(att.attname ORDER BY un.ord)
FROM unnest(con.confkey) WITH ORDINALITY un (attnum, ord)
INNER JOIN pg_attribute att
ON att.attnum = un.attnum
WHERE att.attrelid = frel.oid) confkeyattnames
FROM pg_constraint con
INNER JOIN pg_class rel
ON rel.oid = con.conrelid
INNER JOIN pg_namespace nsp
ON nsp.oid = rel.relnamespace
INNER JOIN pg_class frel
ON frel.oid = con.confrelid
INNER JOIN pg_namespace fnsp
ON fnsp.oid = frel.relnamespace
WHERE con.contype = 'f'
AND con.confdeltype <> 'c'
AND nsp.nspname = 'public';

ORACLE SQL get all target table columns where source table is referenced as a FK

Using: Oracle SQL Developer
Hi I try to explain my Problem with a simple example:
I need a Oracle SQL Query that returns me all Foreign Key related columns from all child tables ( Table Employee and Table Building) that reference the parent table Table Organizational unit
The result for my example would look like this:
SELECT All COLUMNS OF All Tables THAT have an entry referenced as foreign key for table Table Organizational unit
WHERE Table Organizational unit.Code = 'HR'
--Result
Table Building
ID , ORG_UNIT_REF, Building Type
152, 2 , Main Building
Table Employee
ID, ORG_UNIT_REF, Employee Name
13, 2 , Max Doe
This means all Tables with their table name, column names and matching column contents have to be printed out.
I already found all the Referening tables and Constraint names by using this answer
SELECT a.table_name, a.column_name, a.constraint_name, c.owner,
-- referenced pk
c.r_owner, c_pk.table_name r_table_name, c_pk.constraint_name r_pk
FROM all_cons_columns a
JOIN all_constraints c ON a.owner = c.owner
AND a.constraint_name = c.constraint_name
JOIN all_constraints c_pk ON c.r_owner = c_pk.owner
AND c.r_constraint_name = c_pk.constraint_name
WHERE c.constraint_type = 'R'
AND a.table_name = :TableName
For my example the above query gives me something like that:
"TABLE_NAME","CONSTRAINT_NAME","STATUS","OWNER"
"Table Employee","FK_CONSTRAINT","ENABLED","TESTSCHEMA"
"Table Building","FK_CONST","ENABLED","TESTSCHEMA"
Now I know all the child tables that reference my parent table Organizational unit. Now I also want a query that fetches me all the rows in all the tables where the foreign key matches.

Retrieving column names from table -Redshift

I have a table as below:
create table venue_ident
(venueid bigint identity(0, 1)NOT NULL,
venuename varchar(100) default 'city',
venuecity varchar(30),
venuestate char(2),
venueseats integer,
primary key(venueid));
Whenever I use the query
select DISTINCT c.column_name,
a.attencodingtype,
a.attnotnull,
a.atttypid::regtype,
a.attnum
FROM pg_attribute a , information_schema.columns c ,PG_TABLE_DEF as p
where attnum > 0
AND NOT attisdropped
AND c.ordinal_position=a.attnum
and c.column_name=a.attname
and c.table_name='venue_ident'
and a.attnotnull=p.notnull
ORDER by a.attnum;
It always outputs 6 columns instead of 5 . It shows primary key column (venue_id) twice with different values for not null condition.
How do i get just the 5 columns
(venueid, venuename, venuecity, venuestate, venueseats)
The pg_attribute table contains rows to represent primary keys etc. as well as columns. Since you've defined venueid as an IDENTITY column, there is an additional row for venueid that represents the identity property of the table.
You can see this by referencing pg_class in the query and looking at the value for relname:
SELECT
c.column_name,
a.attencodingtype,
a.attnotnull,
a.atttypid::regtype,
a.attnum,
pc.relname
FROM
pg_attribute a
INNER JOIN information_schema.columns c ON c.ordinal_position=a.attnum and c.column_name=a.attname
INNER JOIN pg_class pc ON a.attrelid = pc.oid
where
a.attnum > 0
AND NOT a.attisdropped
and c.table_name='venue_ident'
ORDER by a.attnum;
For one of the "venueid" rows, you'll see relname is "venue_ident_pkey" instead of "venue_ident".
If you want only the column names of the table, then add an additional WHERE clause filter on pc.relname='venue_ident' (the table name).

Finding Cross Dependency Tables in SQL

I need to List all the tables which depends on each other
Example
TableA has foreign Key relation to TableB
TableB has foreign Key relation to TableA
So far I got the script to find the foreign key relations for the tables but how can I filter them down to get just the cross table reference one.
my script so are is:
SELECT CAST(p.name AS VARCHAR(255)) AS [Primary Table] ,
CAST(c.name AS VARCHAR(255)) AS [Foreign Table]
FROM sysobjects f
INNER JOIN sysobjects c ON f.parent_obj = c.id
INNER JOIN sysreferences r ON f.id = r.constid
INNER JOIN sysobjects p ON r.rkeyid = p.id
INNER JOIN syscolumns rc ON r.rkeyid = rc.id
AND r.rkey1 = rc.colid
INNER JOIN syscolumns fc ON r.fkeyid = fc.id
AND r.fkey1 = fc.colid
LEFT JOIN syscolumns rc2 ON r.rkeyid = rc2.id
AND r.rkey2 = rc.colid
LEFT JOIN syscolumns fc2 ON r.fkeyid = fc2.id
AND r.fkey2 = fc.colid
WHERE f.type = 'F'
ORDER BY CAST(p.name AS VARCHAR(255))
This simple select statement returns the circular direct foreign key references in a database:
IF OBJECT_ID('dbo.d') IS NOT NULL DROP TABLE dbo.d;
IF OBJECT_ID('dbo.c') IS NOT NULL DROP TABLE dbo.c;
IF OBJECT_ID('dbo.b') IS NOT NULL
BEGIN
ALTER TABLE dbo.a DROP CONSTRAINT [dbo.a(bid)->dbo.b(bid)];
DROP TABLE dbo.b;
END
IF OBJECT_ID('dbo.a') IS NOT NULL DROP TABLE dbo.a;
CREATE TABLE dbo.a(aid INT PRIMARY KEY CLUSTERED, bid INT);
CREATE TABLE dbo.b(aid INT CONSTRAINT [dbo.b(aid)->dbo.a(aid)] REFERENCES dbo.a(aid), bid INT PRIMARY KEY CLUSTERED);
ALTER TABLE dbo.a ADD CONSTRAINT [dbo.a(bid)->dbo.b(bid)] FOREIGN KEY(bid) REFERENCES dbo.b(bid);
CREATE TABLE dbo.c(cid INT PRIMARY KEY CLUSTERED);
CREATE TABLE dbo.d(did INT PRIMARY KEY CLUSTERED, cid INT CONSTRAINT [dbo.d(cid)->dbo.c(cid)] REFERENCES dbo.c(cid));
SELECT *
FROM sys.foreign_keys fk1
JOIN sys.foreign_keys fk2
ON fk1.parent_object_id = fk2.referenced_object_id
AND fk2.parent_object_id = fk1.referenced_object_id;
From here you can join to the DMVs sys.tables and sys.columns to get the additional information like table and column names.
Two things to be aware of:
You should stop using the compatibility views. They are there to support old scripts that where written for SQL 2000 and should not be used in new development.
You can have up to 16 columns in a foreign key. Your script supports only two. However, you are not even returning the column names so you should not join to sys.columns at all, if you don't need the column names.
If you just need the names of the tables you can get away without an additional join by using this select statement instead:
SELECT
QUOTENAME(OBJECT_SCHEMA_NAME(fk1.parent_object_id))+'.'+QUOTENAME(OBJECT_NAME(fk1.parent_object_id))+
' <-> '+
QUOTENAME(OBJECT_SCHEMA_NAME(fk1.referenced_object_id))+'.'+QUOTENAME(OBJECT_NAME(fk1.referenced_object_id))
FROM sys.foreign_keys fk1
JOIN sys.foreign_keys fk2
ON fk1.parent_object_id = fk2.referenced_object_id
AND fk2.parent_object_id = fk1.referenced_object_id
AND fk1.parent_object_id < fk1.referenced_object_id;
I also added an additional condition to the WHERE clause of the query to include each pair only once.