In Oracle SQL Developer, if I'm viewing the information on a table, I can view the constraints, which let me see the foreign keys (and thus which tables are referenced by this table), and I can view the dependencies to see what packages and such reference the table. But I'm not sure how to find which tables reference the table.
For example, say I'm looking at the emp table. There is another table emp_dept which captures which employees work in which departments, which references the emp table through emp_id, the primary key of the emp table. Is there a way (through some UI element in the program, not through SQL) to find that the emp_dept table references the emp table, without me having to know that the emp_dept table exists?
No. There is no such option available from Oracle SQL Developer.
You have to execute a query by hand or use other tool (For instance PLSQL Developer has such option). The following SQL is that one used by PLSQL Developer:
select table_name, constraint_name, status, owner
from all_constraints
where r_owner = :r_owner
and constraint_type = 'R'
and r_constraint_name in
(
select constraint_name from all_constraints
where constraint_type in ('P', 'U')
and table_name = :r_table_name
and owner = :r_owner
)
order by table_name, constraint_name
Where r_owner is the schema, and r_table_name is the table for which you are looking for references. The names are case sensitive
Be careful because on the reports tab of Oracle SQL Developer there is the option "All tables / Dependencies" this is from ALL_DEPENDENCIES which refers to "dependencies between procedures, packages, functions, package bodies, and triggers accessible to the current user, including dependencies on views created without any database links.". Then, this report have no value for your question.
To add this to SQL Developer as an extension do the following:
Save the below code into an xml file (e.g. fk_ref.xml):
<items>
<item type="editor" node="TableNode" vertical="true">
<title><![CDATA[FK References]]></title>
<query>
<sql>
<![CDATA[select a.owner,
a.table_name,
a.constraint_name,
a.status
from all_constraints a
where a.constraint_type = 'R'
and exists(
select 1
from all_constraints
where constraint_name=a.r_constraint_name
and constraint_type in ('P', 'U')
and table_name = :OBJECT_NAME
and owner = :OBJECT_OWNER)
order by table_name, constraint_name]]>
</sql>
</query>
</item>
</items>
Add the extension to SQL Developer:
Tools > Preferences
Database > User Defined Extensions
Click "Add Row" button
In Type choose "EDITOR", Location is where you saved the xml file above
Click "Ok" then restart SQL Developer
Navigate to any table and you should now see an additional tab next to SQL one, labelled FK References, which displays the new FK information.
Reference
http://www.oracle.com/technetwork/issue-archive/2007/07-jul/o47sql-086233.html
Replace [Your TABLE] with emp in the query below
select owner,constraint_name,constraint_type,table_name,r_owner,r_constraint_name
from all_constraints
where constraint_type='R'
and r_constraint_name in (select constraint_name
from all_constraints
where constraint_type in ('P','U')
and table_name='[YOUR TABLE]');
You may be able to query this from the ALL_CONSTRAINTS view:
SELECT table_name
FROM ALL_CONSTRAINTS
WHERE constraint_type = 'R' -- "Referential integrity"
AND r_constraint_name IN
( SELECT constraint_name
FROM ALL_CONSTRAINTS
WHERE table_name = 'EMP'
AND constraint_type IN ('U', 'P') -- "Unique" or "Primary key"
);
SQL Developer 4.1, released in May of 2015, added a Model tab which shows table foreign keys which refer to your table in an Entity Relationship Diagram format.
SELECT DISTINCT table_name,
constraint_name,
column_name,
r_table_name,
position,
constraint_type
FROM (SELECT uc.table_name,
uc.constraint_name,
cols.column_name,
(SELECT table_name
FROM user_constraints
WHERE constraint_name = uc.r_constraint_name) r_table_name,
(SELECT column_name
FROM user_cons_columns
WHERE constraint_name = uc.r_constraint_name
AND position = cols.position) r_column_name,
cols.position,
uc.constraint_type
FROM user_constraints uc
inner join user_cons_columns cols
ON uc.constraint_name = cols.constraint_name
WHERE constraint_type != 'C')
START WITH table_name = '&&tableName'
AND column_name = '&&columnName'
CONNECT BY NOCYCLE PRIOR table_name = r_table_name
AND PRIOR column_name = r_column_name;
This has been in the product for years - although it wasn't in the product in 2011.
But, simply click on the Model page.
Make sure you are on at least version 4.0 (released in 2013) to access this feature.
How about something like this:
SELECT c.constraint_name, c.constraint_type, c2.constraint_name, c2.constraint_type, c2.table_name
FROM dba_constraints c JOIN dba_constraints c2 ON (c.r_constraint_name = c2.constraint_name)
WHERE c.table_name = <TABLE_OF_INTEREST>
AND c.constraint_TYPE = 'R';
To add to the above answer for sql developer plugin, using the below xml will help in getting the column associated with the foreign key.
<items>
<item type="editor" node="TableNode" vertical="true">
<title><![CDATA[FK References]]></title>
<query>
<sql>
<![CDATA[select a.owner,
a.constraint_name,
a.table_name,
b.column_name,
a.status
from all_constraints a
join all_cons_columns b ON b.constraint_name = a.constraint_name
where a.constraint_type = 'R'
and exists(
select 1
from all_constraints
where constraint_name=a.r_constraint_name
and constraint_type in ('P', 'U')
and table_name = :OBJECT_NAME
and owner = :OBJECT_OWNER)
order by table_name, constraint_name]]>
</sql>
</query>
</item>
</items>
I like to do this with a straight SQL query, rather than messing about with the SQL Developer application.
Here's how I just did it. Best to read through this and understand what's going on, so you can tweak it to fit your needs...
WITH all_primary_keys AS (
SELECT constraint_name AS pk_name,
table_name
FROM all_constraints
WHERE owner = USER
AND constraint_type = 'P'
)
SELECT ac.table_name || ' table has a foreign key called ' || upper(ac.constraint_name)
|| ' which references the primary key ' || upper(ac.r_constraint_name) || ' on table ' || apk.table_name AS foreign_keys
FROM all_constraints ac
LEFT JOIN all_primary_keys apk
ON ac.r_constraint_name = apk.pk_name
WHERE ac.owner = USER
AND ac.constraint_type = 'R'
AND ac.table_name = nvl(upper(:table_name), ac.table_name)
ORDER BY ac.table_name, ac.constraint_name
;
Only Replace table_name with your primary table name
select *
from all_constraints
where r_constraint_name in (
select constraint_name
from all_constraints
where table_name='table_name'
);
Replace MY_OWNER_NAME and MY_TABLE_NAME below and you are ready to go RECURSIVELY:
DECLARE
FUNCTION list_all_child_tables_and_constraints(asked_table_name in VARCHAR2, parent_table_name in VARCHAR2)
RETURN VARCHAR2 IS
current_path VARCHAR2(100);
BEGIN
FOR item IN
(SELECT fk.TABLE_NAME, constraint_parent.FK FK1, constraint_child.FK FK2
FROM all_constraints fk, all_constraints pk,
(SELECT acc.CONSTRAINT_NAME, LISTAGG(acc.COLUMN_NAME, ', ') WITHIN GROUP (ORDER BY acc.COLUMN_NAME) AS FK
FROM ALL_CONS_COLUMNS acc
WHERE acc.OWNER = 'MY_OWNER_NAME'
GROUP BY acc.CONSTRAINT_NAME) constraint_parent,
(SELECT acc.CONSTRAINT_NAME, LISTAGG(acc.COLUMN_NAME, ', ') WITHIN GROUP (ORDER BY acc.COLUMN_NAME) AS FK
FROM ALL_CONS_COLUMNS acc
WHERE acc.OWNER = 'MY_OWNER_NAME'
GROUP BY acc.CONSTRAINT_NAME) constraint_child
WHERE pk.owner = fk.r_owner
AND pk.constraint_name = fk.r_constraint_name
AND fk.constraint_type = 'R'
AND pk.table_name = asked_table_name
AND constraint_parent.CONSTRAINT_NAME = fk.CONSTRAINT_NAME
AND constraint_child.CONSTRAINT_NAME = fk.R_CONSTRAINT_NAME
AND pk.owner = 'MY_OWNER_NAME'
AND fk.owner = 'MY_OWNER_NAME')
LOOP
current_path := parent_table_name || ' // ' || item.TABLE_NAME;
DBMS_OUTPUT.PUT_LINE(current_path);
DBMS_OUTPUT.PUT_LINE(' [' || item.FK1 || '] [' || item.FK2 || ']');
DBMS_OUTPUT.PUT_LINE('');
current_path := list_all_child_tables_and_constraints(item.TABLE_NAME, current_path);
END LOOP;
RETURN '-----------FINISHED-----------';
EXCEPTION
WHEN OTHERS THEN
RETURN '-----------FINISHED-----------';
END list_all_child_tables_and_constraints;
BEGIN
DBMS_OUTPUT.PUT_LINE(list_all_child_tables_and_constraints('MY_TABLE_NAME', ''));
END;
Related
Good afternoon.
I am using SQL Server 2008/TSQL. It is probably important to note I do not have write access to any db (I am a read only user). I do not have write access to the db but can insert temp tables if absolutely needed.
I would like to preface what I am about to say with letting everyone know I have no formal education in SQL. Hopefully this makes sense - I may be inaccurate in some vocabulary etc.
Scope of what I am trying to do:
1. Select a specific recordID (value) in a column (primary key) from a table
2. Find where that specific number/recordID is used in all dependents/foreign keys
3. Return the tablename and columnname with a count of how many times that value was found
So, as an example...
You have a table with information on a person tied to a recordID, say something like:
dbo.MemberInfo with RecordID, Name Etc.
The ID number of the member (MemberInfo.RecordID) is used in other
tables, say: dbo.Awards as [HonoreeID]
(dbo.Awards.HonoreeID=MemberInfo.RecordID) dbo.Address as [MemberID]
(dbo.Address.MemberID=MemberInfo.RecordID) dbo.Contact as [PersonID]
(dbo.Contact.PersonID=MemberInfo.RecordID) ...and potentially a few
hundred others
I basically want to run through all the tables and see how many times a particular value/record is in use. Now, to add some complexity to this, it needs to be generic, as the column I may be looking up dependents on may change from day to day. (Ex. I may be looking for dependents of EventID tomorrow)
My current process is:
-Use a select to find the ID of the person I need
-Look at all the foreign keys linked to RecordID (Primary Key) of dbo.Members
-Dump the tablenames and columns of the foreign keys out into Excel
-Do a find and replace to make a bunch of SELECT COUNTS with a WHERE=#Variable
-Put it into SQL, define my variable and set it equal to the initial ID number
There has to be a better way. I have attempted many variations of the following with lots of errors and no success:
--DECLARE #Selected CHAR
SELECT T.Name, C.Name
--SET #Selected=(SELECT T.Name FROM sys.tables T)
--CASE WHEN (T.NAME IS NOT NULL) THEN 1
--ELSE '0' END AS 'MyTrial'
FROM
--sys.tables t
sys.foreign_key_columns AS fk
INNER JOIN sys.tables AS t ON fk.parent_object_id = t.object_id
INNER JOIN sys.columns AS c ON fk.parent_object_id = c.object_id AND fk.parent_column_id = c.column_id
WHERE Referenced_Object_ID=--Insert object ID here
My line of thinking was/is:
1. Query foreign keys used in a table/column, return the table name and column name from the dependent tables
2. Feed these results into something that can build me a new query to return a count of a value in each of the tables/columns where applicable
3. Return the tablename as well, so it can easily be fed into another select statement should I need to look at the details making up the count.
So my results might look something like this:
Tablename, Columnname, Count of Value in Column
Ideally, no value, table name etc. would be returned if the count is less than one.
My process may be extremely flawed out of the gate, but anything offered helps me learn. Thanks!
The script below uses no stored procedures but does use a temporary table. As far as I can conceive, there's no way around it. It runs very quickly for me, although I was working with a relatively small test database.
This is my first venture into dynamic SQL, so I can't testify to the safety of this code against SQL injection attacks and such. I, too, am self-taught.
Declare #Primary_Table varchar(100) = '';
Declare #Column_Name varchar(100) = '';
Declare #Specific_Value int = ;
IF(OBJECT_ID('tempdb..#Selected_Tables') is not null)
Begin
Drop Table #Selected_Tables;
End
Select Distinct T.Name as Table_Name
, C.Name as Column_Name
, CAST(null as bigint) as Referenced_Records
Into #Selected_Tables
FROM sys.foreign_key_columns AS fk
INNER JOIN sys.tables AS t
ON fk.parent_object_id = t.object_id
INNER JOIN sys.columns AS c
ON fk.parent_object_id = c.object_id
AND fk.parent_column_id = c.column_id
Inner Join sys.tables AS t2
ON fk.referenced_object_id = t2.object_id
Inner Join sys.columns AS c2
on fk.referenced_column_id = c2.column_id
Where t2.name = #Primary_Table
and c2.Name = #Column_Name;
Declare #sqlCommand nvarchar(max);
Declare #Unprocessed_Records int = (Select Count(1)
From #Selected_Tables
Where Referenced_Records is null);
Declare #Processing_Table varchar(1000) = (Select Top 1 Table_Name
From #Selected_Tables
Where Referenced_Records is null);
Declare #Processing_Column varchar(1000) = (Select Top 1 Column_Name
From #Selected_Tables
Where Referenced_Records is null
and Table_Name = #Processing_Table);
While #Unprocessed_Records > 0
Begin
Set #sqlCommand = 'Update #Selected_Tables '
+ 'Set Referenced_Records = (Select Count(1) '
+ 'From ' + #Processing_Table + ' '
+ 'Where ' + #Processing_Column + ' = ' + CAST(#Specific_Value as nvarchar(1000)) + ') '
+ 'Where Table_Name = ''' + #Processing_Table + ''' '
+ 'and Column_Name = ''' + #Processing_Column + ''';'
Exec (#sqlCommand);
Set #Unprocessed_Records = (Select Count(1)
From #Selected_Tables
Where Referenced_Records is null);
Set #Processing_Table = (Select Top 1 Table_Name
From #Selected_Tables
Where Referenced_Records is null);
Set #Processing_Column = (Select Top 1 Column_Name
From #Selected_Tables
Where Referenced_Records is null
and Table_Name = #Processing_Table);
End;
Select * From #Selected_Tables;
I'm learning SQL utilizing the common Microsoft AdventureWorks2014 sample database in SQL Server 2014.
I just learned about HAVING and Information Schema today and am trying to combine the two.
Reason being, I'd really like to quickly tell which columns from all tables are shared. This works:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
ORDER BY COLUMN_NAME, TABLE_SCHEMA
But... the output gives me unique column names that only slows me down.
I've tried applying answers from "How to select non 'unique' rows" (among 5-7 other SOF pages!) such as:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
GROUP BY COLUMN_NAME
HAVING COUNT(COLUMN_NAME) > 1
...but I get this error:
Msg 8120, Level 16, State 1, Line 1 Column
'information_schema.columns.TABLE_SCHEMA' is invalid in the select
list because it is not contained in either an aggregate function or
the GROUP BY clause.
You can use your query, to retrive all the columns that are shared, and then join to the original table for all the information (schema, name):
SELECT t.TABLE_SCHEMA,
t.table_name,
t.column_name
FROM INFORMATION_SCHEMA.COLUMNS t
INNER JOIN (
SELECT s.column_name
FROM INFORMATION_SCHEMA.COLUMNS s
GROUP BY s.column_name
HAVING COUNT(s.column_name) > 1
) tt ON (t.column_name = tt.column_name)
SELECT *
FROM (
SELECT
col = c.name,
obj_name = o.name,
sch_name = SCHEMA_NAME(o.[schema_id]),
col_type = TYPE_NAME(c.system_type_id),
RowNum = COUNT(1) OVER (PARTITION BY c.name, o.[type] ORDER BY 1/0)
FROM sys.columns c
JOIN sys.objects o ON c.[object_id] = o.[object_id]
WHERE o.[type] = 'U'
) t
WHERE t.RowNum > 1
ORDER BY t.col
Output:
col obj_name sch_name col_type
----------------------- ------------------- --------- ------------
dbid spt_fallback_usg dbo smallint
dbid spt_fallback_db dbo smallint
xserver_name spt_fallback_usg dbo varchar
xserver_name spt_fallback_db dbo varchar
xserver_name spt_fallback_dev dbo varchar
We have a master 'users' table and many tables that refer to the UserId either
Directly: UserId is a FK in the child table OR
Indirectly: another FK in the 'grandchild' table that references a record in the child table, that in turn refers to UserID via a FK constraint.
Now if everything is perfect, deleting a user should be as straightforward as deleting them in the master table and the ON CASCADE constraints rippling it thru the rest of the tables. The issue is that we're not 100% sure if every FK relationship in every table referenced (directly or indirectly) has the ON CASCADE constraint. We need some way to issue that delete and watch which tables SQL server actually touches to delete. I read this and tried it but it doesn't display any tables cascaded into - just the entries in the master table only
Here is what I tried:
DELETE umt
OUTPUT DELETED.*
FROM [OurAppDb].[dbo].[UserMasterTable] umt
WHERE umt.UserId LIKE 'ABCDABCD-ABCD-ABCD-ABCD-ABCDABCDABCD'
How can I see all the tables the above query would touch?
NOTE: The ON CASCADE constraint is a constraint in the database that we think we added for every table when each table was built. Example of it being added on one table
ALTER TABLE [dbo].[UserEmailPrefs]
WITH CHECK ADD CONSTRAINT [FK_UserEmailPrefs_UserMasterTable_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[UserMasterTable] ([UserId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[UserEmailPrefs] CHECK CONSTRAINT [FK_UserEmailPrefs_UserMasterTable_UserId]
GO
To inspect the referential constraints in your entire database that reference the UserMasterTable, use INFORMATION_SCHEMA views.
SELECT RC.CONSTRAINT_NAME, TU.TABLE_NAME, RC.DELETE_RULE, RC.UPDATE_RULE
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE TU
ON RC.CONSTRAINT_CATALOG = TU.CONSTRAINT_CATALOG
AND RC.CONSTRAINT_NAME = TU.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.table_constraints TC
ON RC.unique_constraint_name = TC.CONSTRAINT_NAME
WHERE TC.TABLE_NAME='Users'
This returns a list of the referential constraints targeting UserMasterTable, and for each one, which table is referencing UserMasterTable, and what the ON DELETE and ON UPDATE rules are. From this you can quickly see which referential constraints are lacking the desired CASCADE rule. No need to get trigger-happy.
To extend this to "grandchild" references, add two more join clauses.
To extend it to any number of levels, go for a recursive CTE.
I think you can do this in trigger(analyse [inserted] table and search for subordinated tables).
As example: you can create table which will store queries for detecting Foreign Key links :
IF NOT EXISTS (
SELECT *
FROM [sys].tables T
WHERE T.NAME = 'FKCheck'
)
CREATE TABLE FKCheck (
TableName SYSNAME
,ChildTable SYSNAME
,QueryText NVARCHAR(MAX)
)
ELSE
EXEC('DROP TABLE FKCheck')
Then fill it with dynamic query extracted from metadata
;WITH CTE_FKs
AS (
SELECT FK.NAME
,OBJECT_name(fk.parent_object_id) AS ChildTable
,OBJECT_name(fk.referenced_object_id) AS ParentTable
,delete_referential_action_desc AS DeleteAction
,MainTable.NAME AS MainTableColumn
,ChildObject.NAME AS ChildColumnName
FROM sys.foreign_keys FK
INNER JOIN sys.foreign_key_columns FKC ON FKC.constraint_object_id = FK.object_id
INNER JOIN sys.columns AS ChildObject ON ChildObject.object_id = FKc.parent_object_id
AND FKC.parent_column_id = ChildObject.column_id
INNER JOIN sys.columns AS MainTable ON MainTable.object_id = FK.referenced_object_id
AND MainTable.column_id = FKC.referenced_column_id
)
,CTE_Tables
AS (
SELECT DISTINCT C.NAME
,C.ParentTable
,C.DeleteAction
,C.ChildTable
FROM [CTE_FKs] C
)
INSERT INTO [dbo].[FKCheck] (
TableName
,ChildTable
,QueryText
)
SELECT C.ParentTable,C.ChildTable
,'IF EXISTS (select 1 from inserted INNER JOIN ' + QUOTENAME(C.ChildTable) + ' ON ' + STUFF((
SELECT ' AND inserted.' + QUOTENAME(C2.MainTableColumn) + ' = ' + + QUOTENAME(C2.ChildTable) + '.' + QUOTENAME(C2.ChildColumnName)
FROM CTE_FKs C2
WHERE C2.ParentTable = C.ParentTable
AND C2.NAME = C.NAME
FOR XML PATH('')
,TYPE
).value('.', 'nvarchar(MAX)'), 1, 4, '') + ')
RAISERROR(''Relation with ' + QUOTENAME(C.ChildTable) +':'+ CASE C.DeleteAction
WHEN 'CASCADE'
THEN ' data will be deleted'
WHEN 'SET_NULL'
THEN ' set as NULL'
WHEN 'NO_ACTION'
THEN ' no default action'
ELSE 'Unknown'
END + ''')'
FROM [CTE_Tables] C
Your query in table will be looking like:
IF EXISTS (select 1 from inserted INNER JOIN [UserEmailPrefs] ON inserted.[UserId] = [UserEmailPrefs].[UserId])
RAISERROR('Relation with [UserEmailPrefs]: no default action')
IF EXISTS (select 1 from inserted INNER JOIN [UserEmail] ON inserted.[UserId] = [UserEmail].[UserId])
RAISERROR('Relation with [UserEmail]: set as NULL')
Then in trigger you can execute query to print message:
DECLARE #TableName SYSNAME = 'UserMasterTable';
DECLARE #sSQL NVARCHAR(MAX) = '';
SELECT #sSQL += F.QueryText + CHAR(10)
FROM FKCheck F
WHERE F.TableName = #TableName;
EXEC(#sSQL)
ROLLBACK
If you need to analyze more "distant" tables, you need to walk through hierarchy in FKCheck table.
I have to write a delete script to delete rows form a database table. However the table has a lot of children tables (foreign keys) and those children tables have children tables too.
There are foreign keys for all relationships and I'd like to use this info to get the list of tables where I'll have to deletes, in the correct order (leaf tables first and then up the dependency graph).
How can I get the list of child tables for a given table in the correct order?
try this on your database, this script will only give you the graph for one table at a time. I assume you have an Employee table but you would have to change line 2 to check a specific table of your database:
DECLARE #masterTableName varchar(1000)
SET #masterTableName = 'Employee'
DECLARE #ScannedTables TABLE( Level int, Name varchar(1000) collate Latin1_General_CI_AS )
DECLARE #currentTableCount INT
DECLARE #previousTableCount INT
DECLARE #level INT
SET #currentTableCount = 0
SET #previousTableCount = -1
SET #level = 0
INSERT INTO #ScannedTables VALUES ( #level, #masterTableName )
WHILE #previousTableCount <> #currentTableCount
BEGIN
SET #previousTableCount = #currentTableCount
INSERT INTO #ScannedTables
SELECT DISTINCT
#level + 1, TC.Table_Name COLLATE Latin1_General_CI_AS
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON TC.Constraint_Name = RC.Constraint_Name
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FTC ON RC.Unique_Constraint_Name = FTC.Constraint_Name
WHERE TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
AND FTC.TABLE_NAME COLLATE Latin1_General_CI_AS IN ( SELECT Name FROM #ScannedTables WHERE Level = #level )
AND TC.Table_Name COLLATE Latin1_General_CI_AS NOT IN ( SELECT Name FROM #ScannedTables )
SET #level = #level + 1
SELECT #currentTableCount = COUNT(*) FROM #ScannedTables
END
SELECT * FROM #ScannedTables
There is no simple generic answer for this, since tables can recursively depend on other tables including self relationships, etc. Your result could be more than simple tree.
Your best way should depend on your db model: if you have tree tables connected, then delete your data from third table first, than second, than third.
...or disable constraints, delete data, enable constraints.
...or change foreign keys to DELETE CASCADE.
It depends on your data model.
This article gives a good idea of how to do what you're asking.
EDIT: I've modified the original query given in the link to:
Make the script schema aware
Correct the bug noted in the
comments below
Not sure why the editor is doing such a poor job of formatting the code block.
with Fkeys as (
select distinct
OnTable = onTableSchema.name + '.' + OnTable.name
,AgainstTable = againstTableSchema.name + '.' + AgainstTable.name
from
sysforeignkeys fk
inner join sys.objects onTable
on fk.fkeyid = onTable.object_id
inner join sys.objects againstTable
on fk.rkeyid = againstTable.object_id
inner join sys.schemas onTableSchema
on onTable.schema_id = onTableSchema.schema_id
inner join sys.schemas againstTableSchema
on againstTable.schema_id = againstTableSchema.schema_id
where 1=1
AND AgainstTable.TYPE = 'U'
AND OnTable.TYPE = 'U'
-- ignore self joins; they cause an infinite recursion
and onTableSchema.name + '.' + OnTable.name <> againstTableSchema.name + '.' + AgainstTable.name
)
,MyData as (
select
OnTable = s.name + '.' + o.name
,AgainstTable = FKeys.againstTable
from
sys.objects o
inner join sys.schemas s
on o.schema_id = s.schema_id
left join FKeys
on s.name + '.' + o.name = FKeys.onTable
left join Fkeys fk2
on s.name + '.' + o.name = fk2.AgainstTable
and fk2.OnTable = Fkeys.AgainstTable
where 1=1
and o.type = 'U'
and o.name not like 'sys%'
and fk2.OnTable is null
)
,MyRecursion as (
-- base case
select
TableName = OnTable
,Lvl = 1
from
MyData
where 1=1
and AgainstTable is null
-- recursive case
union all select
TableName = OnTable
,Lvl = r.Lvl + 1
from
MyData d
inner join MyRecursion r
on d.AgainstTable = r.TableName
)
select
Lvl = max(Lvl)
,TableName
,strSql = 'delete from [' + tablename + ']'
from
MyRecursion
group by
TableName
order by
1 desc
,2 desc
Is there a query I can write against an INFORMATION_SCHEMA or against the system tables to determine if a column is an identity column in SQL CE version 3.5?
Use COLUMNPROPERTY . For ur reference
a)
COLUMNPROPERTY (Transact-SQL)
b)
Identity Columns
Try the following query:
;WITH PK_INFO AS (SELECT CON.TABLE_CATALOG, CON.TABLE_SCHEMA, CON.CONSTRAINT_NAME, USO.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS CON
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE USO ON CON.CONSTRAINT_NAME = USO.CONSTRAINT_NAME
AND CON.TABLE_CATALOG = USO.TABLE_CATALOG AND CON.TABLE_SCHEMA = USO.TABLE_SCHEMA
WHERE CON.TABLE_NAME = 'YOUR_TABLE'
AND CON.CONSTRAINT_TYPE = 'PRIMARY KEY'
) SELECT DISTINCT CASE WHEN (PK.COLUMN_NAME IS NULL) THEN 0 ELSE 1 END AS COLUMN_IS_PRIMARYKEY, *
FROM INFORMATION_SCHEMA.COLUMNS COL
LEFT JOIN PK_INFO PK on COL.COLUMN_NAME = PK.COLUMN_NAME WHERE COL.TABLE_NAME = 'YOUR_TABLE' ORDER BY COL.ORDINAL_POSITION ASC