How to get table constraint information in SQL Server - tsql

I need to output the constraint information for a given table. I've found lots of closely relevant information here at SO, but I'm not finding a couple of specific pieces of information.
I need the output to look like this in the query results:
CONSTRAINT_NAME CONSTRAINT_TYPE COLUMN_NAME REFERENCED_TABLE REFERENCED_COLUMN
Currently, I have this query:
SELECT
obj_Constraint.NAME AS CONSTRAINT_NAME,
obj_Constraint.type_desc AS CONSTRAINT_TYPE,
'' AS COLUMN_NAME,
'' AS REFERENCED_TABLE,
columns.NAME AS REFERENCED_COLUMN
FROM sys.objects AS obj_table
LEFT JOIN sys.objects AS obj_Constraint ON obj_table.object_id = obj_Constraint.parent_object_id
LEFT JOIN sys.sysconstraints AS constraints ON constraints.constid = obj_Constraint.object_id
LEFT JOIN sys.columns AS columns ON columns.object_id = obj_table.object_id AND columns.column_id = constraints.colid
WHERE obj_table.name = 'some_table'
ORDER BY obj_Constraint.type_desc
Where do I get the referenced (foreign key) table and column information from?
(I'd need a query compatible for SQL Server versions 2008 & later)
EDIT
Based on initial responses, I may not have been clear on the desired output.
Say I have a table "people" with columns "poepleID", "name", "city", "stateID". The Primary Key is on the poepleID column. I also have a table "states" with "stateID" and "state". The Primary Key on that is on the stateID column. You can guess that I have a Foreign Key constraint on people.stateID. I'll call that FK__people__states__stateID
So I need a query that will output this:
CONSTRAINT_NAME CONSTRAINT_TYPE COLUMN_NAME REFERENCED_TABLE REFERENCED_COLUMN
PK__people PRIMARY_KEY_CONSTRAINT peopleID
FK__people__states__stateID FOREIGN_KEY_CONSTRAINT stateID states stateID
I need to list all key constraints (primary, foreign - any) on the table in question ("people" in this case).

Use the schema view INFORMATION_SCHEMA.TABLE_CONSTRAINTS and INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS.
This query shows all foreign keys, for example:
SELECT
ConstraintName = C.CONSTRAINT_NAME,
PrimaryKeyTable = QUOTENAME(PK.CONSTRAINT_SCHEMA) + '.' + QUOTENAME(PK.TABLE_NAME),
PrimaryKeyColumn = CCU.COLUMN_NAME,
ForeignKeyTable = QUOTENAME(FK.CONSTRAINT_SCHEMA) + '.' + QUOTENAME(FK.TABLE_NAME),
ForeignKeyColumn = CU.COLUMN_NAME,
UpdateRule = C.UPDATE_RULE,
DeleteRule = C.DELETE_RULE
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON
C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME AND
C.CONSTRAINT_CATALOG = FK.CONSTRAINT_CATALOG AND
C.CONSTRAINT_SCHEMA = FK.CONSTRAINT_SCHEMA
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON
C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME AND
C.UNIQUE_CONSTRAINT_CATALOG = PK.CONSTRAINT_CATALOG AND
C.UNIQUE_CONSTRAINT_SCHEMA = PK.CONSTRAINT_SCHEMA
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON
C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME AND
C.CONSTRAINT_CATALOG = CU.CONSTRAINT_CATALOG AND
C.CONSTRAINT_SCHEMA = CU.CONSTRAINT_SCHEMA
INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CCU ON
PK.CONSTRAINT_NAME = CCU.CONSTRAINT_NAME AND
PK.CONSTRAINT_CATALOG = CCU.CONSTRAINT_CATALOG AND
PK.CONSTRAINT_SCHEMA = CCU.CONSTRAINT_SCHEMA
WHERE
FK.CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY
PK.TABLE_NAME,
FK.TABLE_NAME
I believe you are capable of working your way around this to get the results you want in a single query.
Example:
CREATE TABLE PKTable (
PKColumn INT PRIMARY KEY,
CheckValue INT,
CHECK (CheckValue > 0))
CREATE TABLE FKTable (
FKColumn INT,
FOREIGN KEY (FKColumn) REFERENCES PKTable (PKColumn))
DECLARE #TableName VARCHAR(100) = 'PKTable'
-- Primary keys, checks
SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS T WHERE T.TABLE_NAME = #TableName
-- Foreign keys
SELECT
ConstraintName = C.CONSTRAINT_NAME,
PrimaryKeyTable = QUOTENAME(PK.CONSTRAINT_SCHEMA) + '.' + QUOTENAME(PK.TABLE_NAME),
PrimaryKeyColumn = CCU.COLUMN_NAME,
ForeignKeyTable = QUOTENAME(FK.CONSTRAINT_SCHEMA) + '.' + QUOTENAME(FK.TABLE_NAME),
ForeignKeyColumn = CU.COLUMN_NAME,
UpdateRule = C.UPDATE_RULE,
DeleteRule = C.DELETE_RULE
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON
C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME AND
C.CONSTRAINT_CATALOG = FK.CONSTRAINT_CATALOG AND
C.CONSTRAINT_SCHEMA = FK.CONSTRAINT_SCHEMA
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON
C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME AND
C.UNIQUE_CONSTRAINT_CATALOG = PK.CONSTRAINT_CATALOG AND
C.UNIQUE_CONSTRAINT_SCHEMA = PK.CONSTRAINT_SCHEMA
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON
C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME AND
C.CONSTRAINT_CATALOG = CU.CONSTRAINT_CATALOG AND
C.CONSTRAINT_SCHEMA = CU.CONSTRAINT_SCHEMA
INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CCU ON
PK.CONSTRAINT_NAME = CCU.CONSTRAINT_NAME AND
PK.CONSTRAINT_CATALOG = CCU.CONSTRAINT_CATALOG AND
PK.CONSTRAINT_SCHEMA = CCU.CONSTRAINT_SCHEMA
WHERE
FK.CONSTRAINT_TYPE = 'FOREIGN KEY' AND
PK.TABLE_NAME = #TableName
ORDER BY
PK.TABLE_NAME,
FK.TABLE_NAME
Results:
CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE IS_DEFERRABLE INITIALLY_DEFERRED
RS1 dbo PK__PKTable__1EEFCD814EF90F36 RS1 dbo PKTable PRIMARY KEY NO NO
RS1 dbo CK__PKTable__CheckVa__6DBA0403 RS1 dbo PKTable CHECK NO NO
ConstraintName PrimaryKeyTable PrimaryKeyColumn ForeignKeyTable ForeignKeyColumn UpdateRule DeleteRule
FK__FKTable__FKColum__6FA24C75 [dbo].[PKTable] PKColumn [dbo].[FKTable] FKColumn NO ACTION NO ACTION

I think you could use sysreferences and check this post for the code.
There is also a tool called Advanced SQL Server Dependencies that may be useful.

Related

Getting the column name of a postgres check constraint from information_schema

I can get the constraint name, the table name and the check_clause of the check constraints on a table using this query:
SELECT tc.constraint_type, tc.constraint_name, tc.table_name, cc.check_clause
FROM information_schema.table_constraints tc
JOIN information_schema.check_constraints cc ON cc.constraint_name = tc.constraint_name
WHERE tc.table_name = $1 and tc.constraint_type = 'CHECK'
From information_schema.constraint_column_usage I can get the columns where a PRIMARY or UNIQUE constraint applies but not a CHECK constraint:
SELECT * FROM information_schema.constraint_column_usage where table_name = $1
This eventually worked following suggestion below:
SELECT
ccu.table_schema,
ccu.table_name,
ccu.constraint_name,
ccu.column_name,
cc.check_clause
FROM information_schema.constraint_column_usage ccu
JOIN information_schema.check_constraints cc ON ccu.constraint_name = cc.constraint_name
WHERE ccu.constraint_name IN
(
SELECT
constraint_name
FROM information_schema.check_constraints
)
AND ccu.table_name = $1;
CHECK constraints can exist on domains as well as tables. To get those that are on table columns only do:
SELECT
table_schema,
table_name,
column_name,
constraint_name
FROM
information_schema.constraint_column_usage
WHERE
constraint_name IN (
SELECT
constraint_name
FROM
information_schema.check_constraints);
The sub select will find all the CHECK constraints in the database and the constraint_name IN will filter to only those that are a column constraint.
UPDATE
An alternative is to use the system catalogs directly:
WITH c AS (
SELECT
conname,
conrelid,
unnest(conkey) AS col_id
FROM
pg_catalog.pg_constraint
WHERE
contype = 'c'
AND conrelid > 0
)
SELECT
conrelid::regclass AS table_name,
conname,
attname
FROM
pg_catalog.pg_attribute
JOIN c ON c.col_id = attnum
AND conrelid = attrelid;
What the above does is restrict the constraint type(contype) to 'c' for CHECK and then to only those CHECK constraints that exist on table conrelid > 0 in the CTE(WITH) and then joins the unnested array of columns(conkey) to the column information in pg_catalog.pg_attribute to get the column names.. The conrelid::regclass turns the conrelid oid value into a table name.
See pg_constraint for more information.

How to get the table a FK refers to in Postgres sql

For a given table, I am trying to get all the columns, to include their name, type, whether the column is a primary key, or a foreign key, and if it is a FK, what table it points to. I wrote the query below, but it seems to be give me the what the columns are referenced by, instead of the other way around:
select c.column_name, c.udt_name, constraint_type, kcu.table_name as references from information_schema.columns c
left outer join information_schema.constraint_column_usage u on c.column_name=u.column_name
left outer join information_schema.table_constraints t on u.constraint_name=t.constraint_name
left outer join information_schema.key_column_usage AS kcu on t.constraint_name = kcu.constraint_name
where c.table_name=#name
I am not too worried about edge cases, I am just trying to inverse the references column. Thanks for your time.
Here is a query to get all the foreign keys from a given table along with what they point to:
SELECT c.conname,
t1.relname AS from_table,
a1.attname AS from_column,
t2.relname AS to_table,
a2.attname AS to_column
FROM pg_catalog.pg_constraint c,
pg_catalog.pg_class t1,
pg_catalog.pg_class t2,
pg_catalog.pg_attribute a1,
pg_catalog.pg_attribute a2,
pg_catalog.pg_namespace n1,
pg_catalog.pg_namespace n2
WHERE c.conrelid = t1.oid
AND c.confrelid = t2.oid
AND c.contype = 'f'
AND a1.attrelid = t1.oid
AND a1.attnum = ANY(c.conkey)
AND a2.attrelid = t2.oid
AND a2.attnum = ANY(c.confkey)
AND t1.relkind = 'r'
AND t2.relkind = 'r'
AND n1.oid = t1.relnamespace
AND n2.oid = t2.relnamespace
AND n1.nspname NOT IN ('pg_catalog', 'pg_toast')
AND n2.nspname NOT IN ('pg_catalog', 'pg_toast')
AND pg_catalog.pg_table_is_visible(t1.oid)
AND pg_catalog.pg_table_is_visible(t2.oid)
AND t1.relname = #name;
I see you are using information_schema instead of pg_catalog, so the easiest way might be to turn my query into a CTE or subquery, then join to it based on your table and column name.
EDIT: It sounds like you just need some help joining my query to what you already have. Actually your query has a lot of errors in how it does the joins, so I've rewritten it to include the correct foreign key results:
SELECT c.column_name,
c.udt_name,
t.constraint_type,
t.constraint_name,
x.to_table
FROM information_schema.columns c
LEFT OUTER JOIN (
SELECT t.constraint_type,
t.constraint_catalog,
t.constraint_schema,
t.constraint_name,
t.table_catalog,
t.table_schema,
t.table_name,
u.column_name
FROM information_schema.constraint_column_usage u
LEFT OUTER JOIN information_schema.table_constraints t
ON t.table_catalog = u.table_catalog
AND t.table_schema = u.table_schema
AND t.table_name = u.table_name
AND t.constraint_catalog = u.constraint_catalog
AND t.constraint_schema = u.constraint_schema
AND t.constraint_name = u.constraint_name
WHERE t.constraint_type IS DISTINCT FROM 'FOREIGN KEY'
) t
ON c.table_catalog = t.table_catalog
AND c.table_schema = t.table_schema
AND c.table_name = t.table_name
AND c.column_name = t.column_name
LEFT OUTER JOIN (
SELECT c.conname,
t1.relname AS from_table,
a1.attname AS from_column,
t2.relname AS to_table,
a2.attname AS to_column
FROM pg_catalog.pg_constraint c,
pg_catalog.pg_class t1,
pg_catalog.pg_class t2,
pg_catalog.pg_attribute a1,
pg_catalog.pg_attribute a2,
pg_catalog.pg_namespace n1,
pg_catalog.pg_namespace n2
WHERE c.conrelid = t1.oid
AND c.confrelid = t2.oid
AND c.contype = 'f'
AND a1.attrelid = t1.oid
AND a1.attnum = ANY(c.conkey)
AND a2.attrelid = t2.oid
AND a2.attnum = ANY(c.confkey)
AND t1.relkind = 'r'
AND t2.relkind = 'r'
AND n1.oid = t1.relnamespace
AND n2.oid = t2.relnamespace
AND n1.nspname NOT IN ('pg_catalog', 'pg_toast')
AND n2.nspname NOT IN ('pg_catalog', 'pg_toast')
AND pg_catalog.pg_table_is_visible(t1.oid)
AND pg_catalog.pg_table_is_visible(t2.oid)
) x
ON x.from_table = c.table_name
AND x.from_column = c.column_name
WHERE c.table_name = #cards
;
This version also prevents lots of duplicate rows if you have NOT NULL or CHECK constraints.
I am not sure if you know how to programming with other languages(such as java, C#) besides writing Sql queries
If you know any of them, you can use the function which are int the JDBC/ODBC drivers to get the things you want
Here is an example of using jdbc
you open a connection to the database
from the connection you get, and you can get the meta data, here is the code
conn=dataSource.getConnection();
DatabaseMetaData dbmeta = conn.getMetaData();
After you get the meta data, you can get all the columns, primary keys, foreign keys and even indexed. See the sample codes below
ResultSet rsKey = dbmeta.getPrimaryKeys(null, schemaName, tableName);
ResultSet rs = dbmeta.getColumns(null, schemaName, tableName, "%");
ResultSet rsForeign=meta.getExportedKeys(null, schemaName, tableName);

Get list of tables that would be truncated by a cascade

Is there a way to get a list of tables that would also be truncated by a TRUNCATE CASCADE in postgres?
So for example, assuming we have three tables:
a
b (depends on a)
c (depends on b)
TRUNCATE a CASCADE; would also truncate b and c. How could we check this ahead of time?
With the help of this answer you can get the foreign table name by this query
SELECT tc.constraint_name
,tc.table_name
,kcu.column_name
,ccu.table_name AS foreign_table_name
,ccu.column_name AS foreign_column_name
FROM
information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY' AND ccu.table_name = 'a'
OR
Create a view with another query
CREATE VIEW vdepend_table
AS
SELECT s1.constraint_name
,s1.table_name
,s1.column_name
,s1.ordinal_position
,s2.table_name_ref
,s2.column_name_ref
,s2.ordinal_position_ref
FROM (
SELECT key_column_usage.constraint_name
,key_column_usage.table_name
,key_column_usage.column_name
,columns.ordinal_position
FROM information_schema.key_column_usage
JOIN information_schema.columns USING (
table_name
,column_name
)
) s1
JOIN (
SELECT constraint_column_usage.constraint_name
,constraint_column_usage.table_name AS table_name_ref
,constraint_column_usage.column_name AS column_name_ref
,cols_ref.ordinal_position AS ordinal_position_ref
FROM information_schema.constraint_column_usage
JOIN information_schema.columns cols_ref ON cols_ref.table_name::TEXT = constraint_column_usage.table_name::TEXT
AND cols_ref.column_name::TEXT = constraint_column_usage.column_name::TEXT
) s2 ON s1.constraint_name::TEXT = s2.constraint_name::TEXT
AND NOT s1.table_name::TEXT = s2.table_name_ref::TEXT;
usage:
select table_name from vdepend_table where table_name_ref='a'

Find primary key of table in Postgresql from information_schema with only SELECT

I am using the following query to discover (1) the primary key columns and (2) if the columns have a default value from the information_schema in Postgresql 9.1.
SELECT kcu.column_name, (c.column_default is not null) AS has_default
FROM information_schema.key_column_usage kcu
JOIN information_schema.table_constraints tc ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.columns c on c.column_name = kcu.column_name and c.table_name = kcu.table_name
WHERE tc.constraint_type = 'PRIMARY KEY' AND kcu.table_name like :tablename
It works fine when run as the database owner, but when I run it as a "read-only" user (which I need to do in my application), it returns no data. Some research revealed that the problem is the information.table_constraints view; from the documentation:
The view table_constraints contains all constraints belonging to tables that the current user owns or has some non-SELECT privilege on.
So in order to retrieve table_constraints, my login role needs more than SELECT on the table? Is there no way to get the information from information_schema without giving write permissions to the login role?
Use pg_* views instead of information_schema views. pg_* views display all information regardles of granted privileges.
Try this query:
select
t.relname as table_name,
i.relname as index_name,
a.attname as column_name,
d.adsrc as default_value
from
pg_class t
join pg_attribute a on a.attrelid = t.oid
join pg_index ix on t.oid = ix.indrelid AND a.attnum = ANY(ix.indkey)
join pg_class i on i.oid = ix.indexrelid
left join pg_attrdef d on d.adrelid = t.oid and d.adnum = a.attnum
where
t.relkind = 'r'
and t.relname in ( 'aa', 'bb', 'cc' )
order by
t.relname,
i.relname,
a.attnum;
An example of the query results:
create table aa(
x int primary KEY
);
create table bb(
x int default 1,
constraint pk primary key ( x )
);
create table cc(
x int default 20,
y varchar(10) default 'something',
constraint cc_pk primary key ( x, y )
);
table_name | index_name | column_name | default_value
------------+------------+-------------+--------------------------------
aa | aa_pkey | x |
bb | pk | x | 1
cc | cc_pk | x | 20
cc | cc_pk | y | 'something'::character varying
This is correct, the official postgresql query is below
http://wiki.postgresql.org/wiki/Retrieve_primary_key_columns
if schema is needed the query is as follows
SELECT
pg_attribute.attname,
format_type(pg_attribute.atttypid, pg_attribute.atttypmod)
FROM pg_index, pg_class, pg_attribute, pg_namespace
WHERE
pg_class.oid = 'MY TABLE'::regclass AND
indrelid = pg_class.oid AND
nspname = 'MY CLASS' AND
pg_class.relnamespace = pg_namespace.oid AND
pg_attribute.attrelid = pg_class.oid AND
pg_attribute.attnum = any(pg_index.indkey)
AND indisprimary
The difference can be up to 6000~7000 times. The pg_ one runs often in 0.56ms where the schema based one can run up 6500ms. This is a huge difference especially if you have a high load on the server.
There is another way to provide access for data in information_schema.
A. Envelop SQL in a function with SECURITY DEFINER modifier
CREATE FUNCTION fn_inf(name)
RETURNS TABLE (column_name information_schema.sql_identifier, has_default bool)
LANGUAGE SQL
SECURITY DEFINER
AS $$
SELECT kcu.column_name, (c.column_default is not null) AS has_default
FROM information_schema.key_column_usage kcu
JOIN information_schema.table_constraints tc ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.columns c on c.column_name = kcu.column_name and c.table_name = kcu.table_name
WHERE tc.constraint_type = 'PRIMARY KEY' AND kcu.table_name like $1;
$$;
B. GRANT EXECUTE to user read_only
GRANT EXECUTE ON FUNCTION fn_inf to read_only;
C. Use as user read_only
SELECT * FROM fn_inf('spatial_ref_sys');
column_name
has_default
srid
false

How can I find which integer Primary Keys in my DB are NOT marked as auto-increment?

Is there a query I can run that will tell me which of my fields in all of the database tables that are integer primary keys are NOT marked as auto-increment? Thanks.
Try this:
SELECT
pk_column_name = c.name,
table_name = o.name
FROM sys.indexes AS i
INNER JOIN sys.index_columns AS ic
ON i.object_id = ic.object_id AND i.index_id = ic.index_id
INNER JOIN sys.syscolumns c
ON ic.object_id = c.id AND COL_NAME(ic.object_id, ic.column_id) = c.name
INNER JOIN sys.sysobjects o
ON o.id = c.id
WHERE i.is_primary_key = 1 -- the column is part of a primary key
AND c.status < 128 -- the column is NOT identity enabled
AND o.xtype = 'U' -- the object is a user table
AND c.xtype = 56 -- the column type is int
I am including columns that are of the type 'int'. If you want to include types like smallint as well, you can look up its xtype by doing a select on the sys.systypes DMV:
SELECT name, xtype FROM sys.systypes
Here is an alternative way of doing this using information schema views:
SELECT
pk_column_name = c.COLUMN_NAME,
table_name = c.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS k
ON c.TABLE_NAME = k.TABLE_NAME
AND c.TABLE_SCHEMA = k.TABLE_SCHEMA
AND c.TABLE_CATALOG = k.TABLE_CATALOG
AND c.COLUMN_NAME = k.COLUMN_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS const
ON k.TABLE_NAME = const.TABLE_NAME
AND k.CONSTRAINT_CATALOG = const.CONSTRAINT_CATALOG
AND k.CONSTRAINT_SCHEMA = const.CONSTRAINT_SCHEMA
AND k.CONSTRAINT_NAME = const.CONSTRAINT_NAME
WHERE COLUMNPROPERTY(OBJECT_ID(c.TABLE_NAME), c.COLUMN_NAME, 'IsIdentity') = 0
AND const.CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.DATA_TYPE = 'int'
Hope this helps. Note that this only works for SQL Server 2005 and above.