Query to find out if foreign key is referenced anywhere else in the database - sql

I have table Animal.
I want to return everything from this table + one column which denotes if record is referenced anywhere else as a foreign key.
I.E.:
Animal_Id Name
1 Cat
2 Dog
3 Parrot
I want to return this:
AnimalId Name Referenced
1 Cat true
2 Dog false
3 Parrot true
by 'referenced' I mean if a specific Animal_Id was referenced in any other table in the database as a foreign key. I think I first can query information_schema and find out what tables contain this foreign key and then use loop and dynamic sql to execute
select count(*) from eachTable where AnimalID = 1
Does anyone have a snippet on how to do that?

This should do it:
SELECT OO.Animal_ID, OO.Name, CASE WHEN XX.REFERENCED IS NULL THEN 'false' ELSE 'true' END Referenced
FROM Animal OO
OUTER APPLY (SELECT SUM(1) REFERENCED
FROM (SELECT FkAnimal_ID FROM AnimalRef1 RR WHERE RR.FkAnimal_ID = OO.Animal_ID UNION ALL
SELECT FkAnimal_ID FROM AnimalRef2 RR WHERE RR.FkAnimal_ID = OO.Animal_ID UNION ALL
SELECT FkAnimal_ID FROM AnimalRef3 RR WHERE RR.FkAnimal_ID = OO.Animal_ID) II) XX
If you don't know all the FK tables, you can use system meta-data tables to generate that collection of UNION ALL queries into a table, which you could then copy & paste into your query:
WITH AKT AS ( SELECT f.name AS ForeignKey
,OBJECT_NAME(f.parent_object_id) AS TableName
,COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName
,OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName
,COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS ReferenceColumnName
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc ON f.OBJECT_ID = fc.constraint_object_id
WHERE f.referenced_object_id = object_id('Animal'))
SELECT 'SELECT ' + ColumnName + ' FROM ' + TableName + ' WHERE RR.' + ColumnName + ' = OO.' + ReferenceColumnName + ' UNION ALL'
FROM AKT
And for the whole thing in a single query using a recursive CTE:
DECLARE #QUERY NVARCHAR(MAX)
WITH AKT AS ( SELECT ROW_NUMBER() OVER (ORDER BY f.name) RN, f.name AS ForeignKey
,OBJECT_NAME(f.parent_object_id) AS TableName
,COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName
,SCHEMA_NAME(oo.schema_id) SchemaName
,OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName
,COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS ReferenceColumnName
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc ON f.OBJECT_ID = fc.constraint_object_id
INNER JOIN sys.objects oo ON oo.object_id = fc.referenced_object_id
WHERE f.referenced_object_id = object_id('Animal'))
,bs AS (SELECT AKT.RN
,'SELECT ' + ColumnName + ' FROM ' + SchemaName + '.' + TableName + ' WHERE ' + ColumnName + ' = OO.' + ReferenceColumnName SubQuery
FROM AKT)
,re AS (SELECT bs.RN, CAST(RTRIM(bs.SubQuery) AS VARCHAR(MAX)) Joined
FROM bs
WHERE bs.RN = 1
UNION ALL
SELECT bs2.RN, CAST(re.Joined + ' UNION ALL ' + ISNULL(RTRIM(bs2.SubQuery), '') AS VARCHAR(MAX)) Joined
FROM re, bs bs2
WHERE re.RN = bs2.RN - 1 )
,fi AS (SELECT ROW_NUMBER() OVER (ORDER BY RN DESC) RNK, Joined
FROM re)
SELECT #QUERY = 'SELECT OO.Animal_ID, OO.Name, CASE WHEN XX.REFERENCED IS NULL THEN ''No'' ELSE ''Yes'' END Referenced
FROM Animal OO
OUTER APPLY (SELECT SUM(1) REFERENCED
FROM (' + Joined + ') II) XX'
FROM fi
WHERE RNK = 1
EXEC (#QUERY)

Related

SQL get dynamic query result into another query

I would like to get, for each line, the result of my ReqCount query directly into my main SELECT
(the main query is simplified on purpose).
I've tried using the EXEC sp_executesql but it was unsuccessful.
How can I manage to do this?
BEGIN
SELECT DISTINCT
TBL.name AS TableName
, IDX.type_desc
, COL.name
, 'SELECT MAX(occurs) FROM (SELECT ' + COL.name + ', count(*) as occurs FROM ' + TBL.name
+ ' GROUP BY ' + COL.name + ') a' as ReqCount
FROM sys.tables AS TBL
INNER JOIN sys.schemas SCH ON TBL.schema_id = SCH.schema_id AND TBL.name <> 'sysdiagrams'
INNER JOIN sys.indexes IDX ON TBL.object_id = IDX.object_id AND IDX.type = 0
LEFT JOIN sys.columns COL ON COL.object_id = TBL.object_id AND (COL.name LIKE '%id' OR COL.name LIKE '%code')
END
TableName type_desc name ReqCount
t_intervention_instruction HEAP NULL NULL
t_constat HEAP con_code SELECT MAX(occurs) FROM (SELECT con_code, count(*) as occurs FROM t_constat GROUP BY con_code) a
t_cri_clientele HEAP NULL NULL
t_ope_sur_branchement HEAP osb_id SELECT MAX(occurs) FROM (SELECT osb_id, count(*) as occurs FROM t_ope_sur_branchement GROUP BY osb_id) a

Update table with matching primary keys

I have two tables TABLE1 and TABLE2.
I need to update TABLE1 with the matching primary keys from TABLE2.
Here is my code to get the primary key from TABLE1:
SELECT C.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS T
JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE C ON C.CONSTRAINT_NAME = T.CONSTRAINT_NAME
WHERE C.TABLE_NAME = 'TABLE1'
AND T.CONSTRAINT_TYPE = 'PRIMARY KEY'
This is the code to get primary key for TABLE2:
SELECT C.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS T
JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE C ON C.CONSTRAINT_NAME = T.CONSTRAINT_NAME
WHERE C.TABLE_NAME = 'TABLE2'
AND T.CONSTRAINT_TYPE = 'PRIMARY KEY'
And this code is used to update TABLE1:
MERGE INTO <TABLE1>
USING <TABLE2> ON (TABLE1.COLUMN_NAME = TABLE2.COLUMN_NAME)
WHEN MATCHED THEN
UPDATE
SET <TABLE1.COLUMN_NAME> = <TABLE1.COLUMN_NAME>
I need to merge all snippets of code to update the TABLE1, please help me how to get the primary keys and update with a single code.
As #GuidoG recommended, you will probably need to use dynamic SQL to perform this feat. Furthermore, from your question it is hard to figure out how to determine that Table1 and Table2 are related to one another.
Let us suppose that you have a table with table pairs available, and Table1 and Table 2 are one of those pairs. We'll need to figure out what the primary keys of both tables are, and then build the dynamic SQL using this information.
DECLARE #tableCombinations TABLE ([MainTable] NVARCHAR(255), [SubTable] NVARCHAR(255))
INSERT INTO #tableCombinations ([MainTable], [SubTable]) VALUES ('Table1','Table2')
DECLARE #q NVARCHAR(MAX) = ''
;WITH PrimaryKeys AS (
SELECT [Schema_Name] = sch.[name]
, [Table_Name] = tbl.[name]
, [Column_Name] = cols.[name]
FROM [sys].[indexes] i
JOIN [sys].[index_columns] ic ON i.[index_id] = ic.[index_id] AND i.[object_id] = ic.[object_id]
JOIN [sys].[columns] cols ON cols.[object_id] = ic.[object_id] AND cols.[column_id] = ic.[column_id]
JOIN [sys].[tables] tbl ON ic.[object_id] = tbl.[object_id]
JOIN [sys].[schemas] sch ON tbl.[schema_Id] = sch.[schema_id]
WHERE i.[is_primary_key] = 1
)
SELECT #q += 'MERGE INTO ' + QUOTENAME(pkMain.[Schema_Name]) + '.' + QUOTENAME(pkMain.[Table_Name]) +' mt
USING ' + QUOTENAME(pkSub.[Schema_Name]) + '.' + QUOTENAME(pkSub.[Table_Name]) + 'st ON (
mt.' + QUOTENAME(pkMain.[Column_Name]) + ' = st.' + pkSub.[Column_Name] + ')
WHEN MATCHED THEN UPDATE SET mt.' + QUOTENAME(pkMain.[Column_Name]) + ' = st.' + pkSub.[Column_Name] + ';' + CHAR(13) + CHAR(10)
FROM #tableCombinations tc
JOIN [PrimaryKeys] pkMain ON tc.[MainTable] = pkMain.[Table_Name]
JOIN [PrimaryKeys] pkSub ON tc.[SubTable] = pkSub.[Table_Name]
SET #q = REPLACE(#q, ' ', '')
SELECT #q
Of course, if this yields the correct results, you can directly execute this query using [sys].[sp_executesql]. For more reliable results, be sure to include the schema names of the table combinations.

Create Sql Server view using table name from sysobjects table

I have around 200 tables. I want to create a view from all these tables. I feel it is inefficient to hardcode all the table names and do an UNION ALL in the view definition.
Instead I am planning to retrieve the table name from sysobjects table like
Select name from sysobjects where name like 'Warehouse_Inventory%'
How can I use these table names and create a view out of it?
Note: I am selecting only 10 columns which are common. If any column is not present in a table, I want to display NULL for it.
This Query may help you..
SELECT 'CREATE VIEW VIEW_NAME AS'
UNION ALL
SELECT 'SELECT * FROM ['+NAME+']
UNION ALL' FROM SYS.TABLES where name like 'Warehouse_Inventory%'
I am not sure why you want to use sys.sysojects instead of other sys views. Also now sure why when you want to union all tables you would want to search by a table name..... I would probably recommend a cursor on tables and temp tables to hold your results if you have 200 tables just do to size of the query but if you really really want to do it via union all here is a way...
Build a list of the 10 columns you want. Then run the query. you may need to tweak and add some cast/convert function to ensure everything is the right datatypes this can be done dynamically with sys.types and sys.columns or just make sure everything is a NVARCHAR(???) by altering my dynamic sql below and move forward.
DECLARE #ListOfColumns AS TABLE (ColumnName VARCHAR(100))
INSERT INTO #ListOfColumns (ColumnName) VALUES ('col1'),('col2'),('col3')
DECLARE #SQLStatement NVARCHAR(MAX)
;WITH cteColumnsTableCross AS (
SELECT
SchemaName = s.name
,t.schema_id
,TableName = t.name
,l.ColumnName
FROm
#ListOfColumns l
CROSS JOIN sys.tables t
INNER JOIN sys.schemas s
ON t.schema_id = s.schema_id
)
, cteColumns AS (
SELECT
x.SchemaName
,x.TableName
,x.ColumnName
,ColumnExists = IIF(c.name IS NOT NULL,1,0)
,RowNum = ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY x.TableName DESC)
--you can add data type by getting from sys.columns and sys.types if desired
FROM
cteColumnsTableCross x
LEFT JOIN sys.tables t
ON x.TableName = t.name
AND x.schema_id = t.schema_id
LEFT JOIN sys.columns c
ON t.object_id = c.object_id
AND x.ColumnName = c.name
)
, cteSelectStatements AS (
SELECT
TableName = t.name
,TableSelect = 'SELECT TableName = ''' + t.name + ''', ' +
STUFF(
(SELECT ', ' + c.ColumnName + ' = ' + IIF(c.ColumnExists = 0,'NULL',c.ColumnName)
FROM
cteColumns c
WHERE t.name = c.Tablename
FOR XML PATH(''))
,1,1,'')
+ ' FROM ' + t.name +
IIF((ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY t.name DESC)) > 1,' UNION ALL ','')
FROM
sys.tables t
)
SELECT #SQLStatement = STUFF(
(SELECT ' ' + TableSelect
FROM
cteSelectStatements
ORDER BY
TableName
FOR XML PATH(''))
,1,1,'')
PRINT #SQLStatement
--EXECUTE #SQLStatement

SQL - Query only the first 25 columns in that table

75 columns in a table - I want to query only the first 25 columns in that table without naming each column name.... can you assist with a SQL query....
I been playing with the following:
Select Table_Name, Count(*) As ColumnCount
From Information_Schema.Columns
Group By Table_Name
Order By Table_Name
Doesn't meet my output........
If a Table has 75 columns, How can I see the first 25 columns without naming each column name? Don't want to delete Columns Only want to see the first 25 columns out of 75 columns in the same table.....TOP is not enable need another work around....
First 25 columns in a table query built into #query and then executed. Substitute correct #target_table value.
DECLARE
#target_table sysname
, #query nvarchar(max)
SET
#target_table = '_dimAreaOverlay'
; with of_interest as
(
SELECT
SS.name AS schemaname
, T.name AS tablename
, SC.name AS columname
FROM
sys.schemas SS
inner join
sys.tables T
ON T.schema_id = SS.schema_id
inner join
sys.columns SC
ON SC.object_id = T.object_id
WHERE
T.name = #target_table
AND SC.column_id < 26
)
, c AS
(
SELECT
STUFF((
SELECT
',' + QUOTENAME(I.columname)
FROM
of_interest I
FOR XML PATH('')), 1,1, '') AS column_list
, OI.tablename
, OI.schemaname
FROM
of_interest OI
GROUP BY
OI.schemaname
, OI.tablename
)
SELECT
#query = 'SELECT '
+ C.column_list
+ ' FROM '
+ QUOTENAME(C.schemaname)
+ '.'
+ QUOTENAME(C.tablename)
FROM C
EXECUTE(#query)
Find the table in Management Studio Object Explorer.
Right click it and choose Script Table As -> Select To -> New Query Editor Window
Delete unwanted columns.

SQLServer: How to sort table names ordered by their foreign key dependency

The following SQL separates tables according to their relationship. The problem is with the tables that sort under the 3000 series. Tables that are part of foreign keys and that use foreign keys. Anyone got some clever recursive CTE preferably or a stored procedure to do the necessary sorting?? Programs connectiong to the database are not considered a solution.
Edit: I posted the answer in the "answers" based on the first solution
Free "right answer" to be had for anyone reposting my own "right" answer!
WITH
AllTables(TableName) AS
(
SELECT OBJECT_SCHEMA_NAME(so.id) +'.'+ OBJECT_NAME(so.id)
FROM dbo.sysobjects so
INNER JOIN sys.all_columns ac ON
so.ID = ac.object_id
WHERE
so.type = 'U'
AND
ac.is_rowguidcol = 1
),
Relationships(ReferenceTableName, ReferenceColumnName, TableName, ColumnName) AS
(
SELECT
OBJECT_SCHEMA_NAME (fkey.referenced_object_id) + '.' +
OBJECT_NAME (fkey.referenced_object_id) AS ReferenceTableName
,COL_NAME(fcol.referenced_object_id,
fcol.referenced_column_id) AS ReferenceColumnName
,OBJECT_SCHEMA_NAME (fkey.parent_object_id) + '.' +
OBJECT_NAME(fkey.parent_object_id) AS TableName
,COL_NAME(fcol.parent_object_id, fcol.parent_column_id) AS ColumnName
FROM sys.foreign_keys AS fkey
INNER JOIN sys.foreign_key_columns AS fcol ON
fkey.OBJECT_ID = fcol.constraint_object_id
),
NotReferencedOrReferencing(TableName) AS
(
SELECT TableName FROM AllTables
EXCEPT
SELECT TableName FROM Relationships
EXCEPT
SELECT ReferenceTableName FROM Relationships
),
OnlyReferenced(Tablename) AS
(
SELECT ReferenceTableName FROM Relationships
EXCEPT
SELECT TableName FROM Relationships
),
-- These need to be sorted based on theire internal relationships
ReferencedAndReferencing(TableName, ReferenceTableName) AS
(
SELECT r1.Tablename, r2.ReferenceTableName FROM Relationships r1
INNER JOIN Relationships r2
ON r1.TableName = r2.ReferenceTableName
),
OnlyReferencing(TableName) AS
(
SELECT Tablename FROM Relationships
EXCEPT
SELECT ReferenceTablename FROM Relationships
)
SELECT TableName, 1000 AS Sorting FROM NotReferencedOrReferencing
UNION
SELECT TableName, 2000 AS Sorting FROM OnlyReferenced
UNION
SELECT TableName, 3000 AS Sorting FROM ReferencedAndReferencing
UNION
SELECT TableName, 4000 AS Sorting FROM OnlyReferencing
ORDER BY Sorting
My rendition with moderate tweaks: This one is SQL-2005+ and works on databases without the "rowguidcol":
WITH TablesCTE(SchemaName, TableName, TableID, Ordinal) AS
(
SELECT
OBJECT_SCHEMA_NAME(so.object_id) AS SchemaName,
OBJECT_NAME(so.object_id) AS TableName,
so.object_id AS TableID,
0 AS Ordinal
FROM
sys.objects AS so
WHERE
so.type = 'U'
AND so.is_ms_Shipped = 0
UNION ALL
SELECT
OBJECT_SCHEMA_NAME(so.object_id) AS SchemaName,
OBJECT_NAME(so.object_id) AS TableName,
so.object_id AS TableID,
tt.Ordinal + 1 AS Ordinal
FROM
sys.objects AS so
INNER JOIN sys.foreign_keys AS f
ON f.parent_object_id = so.object_id
AND f.parent_object_id != f.referenced_object_id
INNER JOIN TablesCTE AS tt
ON f.referenced_object_id = tt.TableID
WHERE
so.type = 'U'
AND so.is_ms_Shipped = 0
)
SELECT DISTINCT
t.Ordinal,
t.SchemaName,
t.TableName,
t.TableID
FROM
TablesCTE AS t
INNER JOIN
(
SELECT
itt.SchemaName as SchemaName,
itt.TableName as TableName,
itt.TableID as TableID,
Max(itt.Ordinal) as Ordinal
FROM
TablesCTE AS itt
GROUP BY
itt.SchemaName,
itt.TableName,
itt.TableID
) AS tt
ON t.TableID = tt.TableID
AND t.Ordinal = tt.Ordinal
ORDER BY
t.Ordinal,
t.TableName
Thank you for a working solution NXC. You put me on the right track to solve the problem using a recursive CTE.
WITH
TablesCTE(TableName, TableID, Ordinal) AS
(
SELECT
OBJECT_SCHEMA_NAME(so.id) +'.'+ OBJECT_NAME(so.id) AS TableName,
so.id AS TableID,
0 AS Ordinal
FROM dbo.sysobjects so INNER JOIN sys.all_columns ac ON so.ID = ac.object_id
WHERE
so.type = 'U'
AND
ac.is_rowguidcol = 1
UNION ALL
SELECT
OBJECT_SCHEMA_NAME(so.id) +'.'+ OBJECT_NAME(so.id) AS TableName,
so.id AS TableID,
tt.Ordinal + 1 AS Ordinal
FROM
dbo.sysobjects so
INNER JOIN sys.all_columns ac ON so.ID = ac.object_id
INNER JOIN sys.foreign_keys f
ON (f.parent_object_id = so.id AND f.parent_object_id != f.referenced_object_id)
INNER JOIN TablesCTE tt ON f.referenced_object_id = tt.TableID
WHERE
so.type = 'U'
AND
ac.is_rowguidcol = 1
)
SELECT DISTINCT
t.Ordinal,
t.TableName
FROM TablesCTE t
INNER JOIN
(
SELECT
TableName as TableName,
Max (Ordinal) as Ordinal
FROM TablesCTE
GROUP BY TableName
) tt ON (t.TableName = tt.TableName AND t.Ordinal = tt.Ordinal)
ORDER BY t.Ordinal, t.TableName
For thoose wondering what this is useable for: I will use it to safely empty a database without violating any foreign key relations. (By truncating in descending order)
I will also be able to safely fill the tables with data from another database by filling the tables in ascending order.
You can use an iterative algorithm, which is probably less convoluted than a CTE. Here's an example that sorts according to depth:
declare #level int -- Current depth
,#count int
-- Step 1: Start with tables that have no FK dependencies
--
if object_id ('tempdb..#Tables') is not null
drop table #Tables
select s.name + '.' + t.name as TableName
,t.object_id as TableID
,0 as Ordinal
into #Tables
from sys.tables t
join sys.schemas s
on t.schema_id = s.schema_id
where not exists
(select 1
from sys.foreign_keys f
where f.parent_object_id = t.object_id)
set #count = ##rowcount
set #level = 0
-- Step 2: For a given depth this finds tables joined to
-- tables at this given depth. A table can live at multiple
-- depths if it has more than one join path into it, so we
-- filter these out in step 3 at the end.
--
while #count > 0 begin
insert #Tables (
TableName
,TableID
,Ordinal
)
select s.name + '.' + t.name as TableName
,t.object_id as TableID
,#level + 1 as Ordinal
from sys.tables t
join sys.schemas s
on s.schema_id = t.schema_id
where exists
(select 1
from sys.foreign_keys f
join #Tables tt
on f.referenced_object_id = tt.TableID
and tt.Ordinal = #level
and f.parent_object_id = t.object_id
and f.parent_object_id != f.referenced_object_id)
-- The last line ignores self-joins. You'll
-- need to deal with these separately
set #count = ##rowcount
set #level = #level + 1
end
-- Step 3: This filters out the maximum depth an object occurs at
-- and displays the deepest first.
--
select t.Ordinal
,t.TableID
,t.TableName
from #Tables t
join (select TableName as TableName
,Max (Ordinal) as Ordinal
from #Tables
group by TableName) tt
on t.TableName = tt.TableName
and t.Ordinal = tt.Ordinal
order by t.Ordinal desc
This causes problems with self-referencing tables. You'll need to manually exclude any foreign keys that point to self referencing tables.
INNER JOIN sys.foreign_keys f
ON (f.parent_object_id = so.id AND f.parent_object_id != f.referenced_object_id)
/* Manually exclude self-referencing tables - they cause recursion problems*/
and f.object_id not in /*Below are IDs of foreign keys*/
( 1847729685,
1863729742,
1879729799
)
INNER JOIN TablesCTE tt