April 24, 2006 at 11:25 am
How can i locate tables and their columns that are foreign keys and are not indexed. i have a large number of tables to process
I understand, that Indexes on Foreign Keys can largely improve the performance when there are few inserts or updates.
April 24, 2006 at 1:48 pm
Quick and easy open the database in Enterprise manager and open Query Analyzer make sure the Object browser is open then right click on the table and click on create to generate the create table statement you should see the Foreign keys as constraints. Run a search for constraints in SQL Server BOL (books online). Hope this helps.
Kind regards,
Gift Peddie
Kind regards,
Gift Peddie
April 24, 2006 at 2:58 pm
I have a set of scripts that writes the indexes and foreign keys to a table which could then be queried to get what you need on a mass scale......
Here:
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[DBA_OBJECTS]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[DBA_OBJECTS]
GO
CREATE TABLE [dbo].[DBA_OBJECTS] (
[Database] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[Object_Type] [varchar] (30) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[table_name] [varchar] (512) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[column_name] [varchar] (512) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[column_id] [smallint] NULL ,
[data_type] [varchar] (64) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[length] [smallint] NULL ,
[prec] [smallint] NULL ,
[scale] [int] NULL ,
[default_value] [varchar] (2000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[default_cons_name] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[is_nullable] [varchar] (1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[is_identity] [varchar] (1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[constraint_name] [varchar] (512) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[condition] [varchar] (3000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[pos] [smallint] NULL ,
[child_table] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[child_column] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[parent_table] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[parent_column] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[index_name] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[is_unique] [varchar] (1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[Filegroup] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[DBA_Scripts]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[DBA_Scripts]
GO
CREATE TABLE [dbo].[DBA_Scripts] (
[ID] [bigint] IDENTITY (1, 1) NOT NULL ,
[Database] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[Object_Type] [varchar] (30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[CMD_TYPE] [varchar] (20) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[Object_name] [varchar] (512) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[pos] [smallint] NOT NULL ,
[Text] [varchar] (7000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[DBA_GetIndexes]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[DBA_GetIndexes]
GO
--DBA_GetIndexes 'NorthWind'
CREATE Proc DBA_GetIndexes
@dbname Varchar(100)
As
Set Nocount On
Declare @Object_type Varchar(512),
@Object_name Varchar(512),
@table_name varchar(512) ,
@Column_id smallint,
@MaxID int,
@column_name varchar(512),
@index_name varchar(512),
@data_type varchar(64),
@length smallint,
@prec smallint,
@scale int,
@is_nullable char(1),
@is_identity char(1),
@Filegroup varchar(50),
@TypeString Varchar(100),
@Child_Table varchar(128),
@Child_column varchar(128),
@Parent_Table varchar(128),
@Parent_column varchar(128),
@SQLTxt Varchar(7000),
@SQLTxt1 Varchar(7000),
@SQLTxt2 Varchar(7000),
@Pos int,
@Header Varchar(1000),
@List1 Varchar(1000),
@Mid Varchar(1000),
@List2 Varchar(1000),
@Trailer Varchar(1000),
@exec_stmt nvarchar(625),
@showdev bit,
@allstatopts int,
@allrelstatopts int,
@allcatopts int,
@name sysname,
@curdbid int,
@cmd varchar(8000),
@bitdesc varchar(35) /* db option English description */
set nocount on
Set @cmd = 'USE ' + @dbName + ' '
Set @cmd =@cmd + 'SELECT ''' + @dbName + ''', ''Index'', tab.name table_name, ind.name index_name, INDEX_COL(tab.name, ind.indid, idk.keyno) column_name
, CASE WHEN ind.status & 0x2 = 0x2 THEN ''Y'' ELSE ''N'' END is_unique, idk.keyno FROM sysindexes ind INNER JOIN sysindexkeys idk ON idk.indid = ind.indid
INNER JOIN sysobjects tab ON idk.id = tab.id AND tab.id = ind.id WHERE NOT (ind.status & 0x800 = 0x800) AND NOT (ind.status & 0x1000 = 0x1000)
AND tab.xtype = ''U'' AND ind.name not like ''_WA%'' ORDER BY 1, 2'
Insert Into DBA_OBJECTS ([Database], Object_Type, table_name , index_name, column_name, is_unique, pos)
Exec (@cmd)
declare IndexList cursor Read_Only for
select distinct table_name ,index_name, pos, column_name,data_type,length,prec,scale,is_nullable,is_identity,[Filegroup]
from DBA_OBJECTS
Where Object_Type = 'Index'
order by table_name, index_name, pos
open IndexList
fetch next
from IndexList
into @table_name , @index_name, @Column_id, @column_name, @data_type,@length,@prec,@scale,@is_nullable,@is_identity,@Filegroup
while @@fetch_status <> -1
begin
If @column_id = 1
Begin
Set @SQLTxt = 'USE ' + @dbName + ' If Not Exists(Select [Name] From sysindexes Where [Name] = ''' + @index_name + ''') '
Select @MaxId = Max(pos) from DBA_OBJECTS
Where index_name = @index_name
Group By index_name
End
Set @SQLTxt = @SQLTxt + Case
When (@column_id = 1) then ' Create Index [' + @index_name + '] ON ' + @dbName + '( [' + @column_name + ']'
When (@column_id > 1 ) then ',[' + @column_name + '] '
END
If (@column_id = @MaxID)
Begin
INSERT INTO [dbo].[DBA_Scripts]([Database],[CMD_TYPE], [Object_Type], [Object_name], [pos], [Text])
VALUES( @DBname,'Drop', 'Index', @table_name, 2, 'USE ' + @dbName + ' If Exists(Select [Name] From sysindexes Where [Name] = ''' + @index_name + ''') Drop Index [' + @table_name + '].[' + @index_name + ']' )
INSERT INTO [dbo].[DBA_Scripts]([Database],[CMD_TYPE], [Object_Type], [Object_name], [pos], [Text])
VALUES( @DBname, 'Add', 'Index', @table_name, 2, @SQLTxt)
End
fetch next
from IndexList
into @table_name , @index_name, @Column_id, @column_name,@data_type,@length,@prec,@scale,@is_nullable,@is_identity,@Filegroup
end
close IndexList
deallocate IndexList
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[DBA_GetForeignKeys]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[DBA_GetForeignKeys]
GO
--DBA_GetForeignKeys 'NorthWind'
CREATE Proc DBA_GetForeignKeys
@dbname Varchar(100)
As
Set Nocount On
Declare @Object_type Varchar(512),
@Object_name Varchar(512),
@table_name varchar(512) ,
@Column_id smallint,
@MaxID int,
@column_name varchar(512),
@constraint_name varchar(512),
@data_type varchar(64),
@length smallint,
@prec smallint,
@scale int,
@is_nullable char(1),
@is_identity char(1),
@Filegroup varchar(50),
@TypeString Varchar(100),
@Child_Table varchar(128),
@Child_column varchar(128),
@Parent_Table varchar(128),
@Parent_column varchar(128),
@SQLTxt Varchar(7000),
@AFK_CMND Varchar(7000),
@DFK_CMND Varchar(7000),
@Pos int,
@Header Varchar(1000),
@List1 Varchar(1000),
@Mid Varchar(1000),
@List2 Varchar(1000),
@Trailer Varchar(1000),
@exec_stmt nvarchar(625),
@showdev bit,
@allstatopts int,
@allrelstatopts int,
@allcatopts int,
@name sysname,
@curdbid int,
@cmd varchar(8000),
@bitdesc varchar(35) /* db option English description */
set nocount on
Set @cmd = 'USE ' + @dbName + ' '
Set @cmd =@cmd + 'SELECT ''' + @dbName + ''', ''ForKey'', foreignkeycols.child_table,obj.name,foreignkeycols.child_column,foreignkeycols.child_pos,foreignkeycols.parent_table,foreignkeycols.parent_column FROM (
SELECT tab1.name child_table,col1.name child_column,CASE col1.colid WHEN ref.fkey1 THEN 1 WHEN ref.fkey2 THEN 2 WHEN ref.fkey3 THEN 3
WHEN ref.fkey4 THEN 4 WHEN ref.fkey5 THEN 5 WHEN ref.fkey6 THEN 6 WHEN ref.fkey7 THEN 7 WHEN ref.fkey8 THEN 8 WHEN ref.fkey9 THEN 9
WHEN ref.fkey10 THEN 10 WHEN ref.fkey11 THEN 11 WHEN ref.fkey12 THEN 12 WHEN ref.fkey13 THEN 13 WHEN ref.fkey14 THEN 14 WHEN ref.fkey15 THEN 15
WHEN ref.fkey16 THEN 16 END child_pos,tab2.name parent_table,col2.name parent_column,ref.constid constraint_id,CASE col2.colid WHEN ref.rkey1 THEN 1
WHEN ref.rkey2 THEN 2 WHEN ref.rkey3 THEN 3 WHEN ref.rkey4 THEN 4 WHEN ref.rkey5 THEN 5 WHEN ref.rkey6 THEN 6 WHEN ref.rkey7 THEN 7 WHEN ref.rkey8 THEN 8
WHEN ref.rkey9 THEN 9 WHEN ref.rkey10 THEN 10 WHEN ref.rkey11 THEN 11 WHEN ref.rkey12 THEN 12 WHEN ref.rkey13 THEN 13 WHEN ref.rkey14 THEN 14 WHEN ref.rkey15 THEN 15
WHEN ref.rkey16 THEN 16 END parent_pos FROM syscolumns col1 INNER JOIN sysreferences ref ON col1.id = ref.fkeyid INNER JOIN sysobjects tab1 ON
tab1.id = col1.id INNER JOIN syscolumns col2 ON col2.id = ref.rkeyid INNER JOIN sysobjects tab2 ON tab2.id = col2.id
WHERE col1.colid IN (ref.fkey1, ref.fkey2, ref.fkey3, ref.fkey4, ref.fkey5, ref.fkey6, ref.fkey7, ref.fkey8, ref.fkey9, ref.fkey10, ref.fkey11,
ref.fkey12, ref.fkey13, ref.fkey14, ref.fkey15,ref.fkey16) AND col2.colid IN (ref.rkey1, ref.rkey2, ref.rkey3, ref.rkey4, ref.rkey5, ref.rkey6, ref.rkey7,
ref.rkey8, ref.rkey9, ref.rkey10, ref.rkey11, ref.rkey12, ref.rkey13, ref.rkey14, ref.rkey15, ref.rkey16))
foreignkeycols INNER JOIN sysobjects obj ON obj.id = foreignkeycols.constraint_id
WHERE foreignkeycols.child_pos = foreignkeycols.parent_pos ORDER BY 1, 2, 4'
Insert Into DBA_OBJECTS ([Database],[Object_Type],[child_table],[constraint_name],[child_column],[pos],[parent_table],[parent_column])
Exec (@cmd)
declare IndexList cursor scroll for
select distinct Parent_Table , constraint_name, Child_Table, Child_column, Parent_Table, Parent_column, Pos
from DBA_OBJECTS
Where Object_Type = 'ForKey'
order by constraint_name, pos
open IndexList
fetch first
from IndexList
into @table_name , @constraint_name, @Child_Table, @Child_column, @Parent_Table, @Parent_column, @Pos
while @@fetch_status <> -1
begin
Select @MaxId = Max(pos) from DBA_OBJECTS
Where constraint_name = @constraint_name
Group By constraint_name
If @Pos = 1
Begin
Set @Header = 'USE ' + @dbName + ' ALTER TABLE ' + @Child_Table + ' WITH NOCHECK ADD CONSTRAINT '
Set @List1 = ' ' + @Child_column + ''
Set @Mid = ') REFERENCES ' + @Parent_Table
Set @List2 = ' ' + @Parent_column + ''
Set @Trailer = ') '
end
If @Pos > 1
Begin
Set @List1 = @List1 + ', ' + @Child_column + ''
Set @List2 = @List2 + ', ' + @Parent_column + ''
End
If @Pos = @MaxId
Begin
Set @AFK_CMND = @Header + @constraint_name + ' FOREIGN KEY ' + '(' + @List1 + @Mid + '(' + @List2 + @Trailer
Set @DFK_CMND = 'USE ' + @dbName + ' ALTER TABLE ' + @Child_Table + ' DROP CONSTRAINT ' + @constraint_name
INSERT INTO [dbo].[DBA_Scripts]([Database],[CMD_TYPE], [Object_Type], [Object_name], [pos], [Text])
VALUES( @dbName,'DROP', 'ForKey', @constraint_name, 2, @DFK_CMND)
INSERT INTO [dbo].[DBA_Scripts]([Database],[CMD_TYPE], [Object_Type], [Object_name], [pos], [Text])
VALUES( @dbName,'ADD', 'ForKey', @constraint_name, 2, @AFK_CMND)
End
fetch next
from IndexList
into @table_name , @constraint_name, @Child_Table, @Child_column, @Parent_Table, @Parent_column, @Pos
end
Close IndexList
deallocate IndexList
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
April 25, 2006 at 8:24 am
This is a must-have for any DBA, so I'am attempting to join the tables
sysindexes
sysindexkeys
sysforeignkeys
sysobjects
to arrive at a elegant and efficient solution.
April 25, 2006 at 1:45 pm
Here is the problem, the previous solution uses too much resources for a simple task and your solution is using Microsoft property the System tables which changes with service packs and versions. The other solution is to use the database Diagramming wizard in Enterprise manager you will see the constraints. Hope this helps.
Kind regards,
Gift Peddie
Kind regards,
Gift Peddie
April 25, 2006 at 2:02 pm
If all I needed was a picture of them, I am sure I would use what you suggest. However, I need a list of fields and tables as my solution gives, along with drop and rebuild scripts for each object, which I can store, and refer to as builds go through, do compares against, etc. If the origionator of this thread doesn't need this, it can be pared down to fit his needs, or perhaps your solution is better.......I simply offer one solution to be used or discarded as his needs indicate....
And as far as the changing schema's, I've been dealing with that since version 2.0 of Sql Server.......I don't figure it's too much of a bother for what I want to accomplish.....so while I agree that a novice, or someone who doesn't want to be bothered by schema changes may not need something like this, or care to change as server versions change, it's a simple thing for me to accomodate changes as they come along....
April 26, 2006 at 5:51 am
I am not a novice, Database schema changes one word ALTER and save as .sql. If database diagramming is simple DDL (data definition langauge) writers will not earn very good living doing just that, and Rational Rose, Erwin studio and Visio will not cost more than most software development IDE(integrated development environment).
Kind regards,
Gift Peddie
Kind regards,
Gift Peddie
April 26, 2006 at 12:16 pm
I did not mean to imply that you were a novice......
Once you create your .sql files for the foreign keys, and another for your indexes......how do you determine the keys which have no index? I would love to know, so that I do not have to generate the actual tables holding this relationally....
and in all the years I've used the modeling tools you mention, along with many others, I've yet to discover the "show me foreign keys with no corresponding index" command....
April 26, 2006 at 1:22 pm
All information about indexes are in the sysindex table in every database, so you have some options Index tuning wizard, DBCC SHOW_STATISTICS (table_name , index_name), or just plain UPDATE STATISTICS query. The key is database documentation and updating the document with major changes like ALTER table or column that will change your indexing strategies. Today most databases are very busy mine uses little or no resources.
Kind regards,
Gift Peddie
Kind regards,
Gift Peddie
April 26, 2006 at 3:38 pm
I can't help but still think your missing the point here......which is to identify foreign key fields within a database of maybe hundreds of tables and possibilities which have no index on them currently. No alternative you have shown is capable of doing that. I absolutely agree with the statements you have made:
1. The process I use is resource intensive.
2. Changes to schema will cause me to have to update my scripts.
However, with those things aside, my solution works. Period. None of your statements have answered the actual question of how to do what was asked. No Index tuning wizard, DBCC SHOW_STATISTICS (table_name , index_name), or just plain UPDATE STATISTICS query will ever be able to answer the question.
DO YOU OR DO YOU NOT HAVE AN ALTERNATIVE SOLUTION WHICH ACTUALLY DOES WHAT WAS ASKED?
April 27, 2006 at 5:59 am
I guess we have to disagree because I gave you those solutions for schema change, long resource using code is usually not needed just get the create table statement and the create index statements for the table, I wrote a tutorial on this topic in 2000. There is a reason for show results in a grid which Oracle still doesnot have and the sysindex table in every database. I have been in a shop with 480 databases if you document everything you find you don't need to run most of those discovery code. Now have a nice day.
Kind regards,
Gift Peddie
Kind regards,
Gift Peddie
April 27, 2006 at 1:43 pm
QUOTE : "long resource using code is usually not needed just get the create table statement and the create index statements for the table?"
Of course, you are absolutely right......and of course, if that was all that was needing to be done, again you would be right.....however, the code does much more than that, it allows a list of fields which can be queried on to give you the fields from indexes and fields from foreign keys which do not match...the script generation is simply an afterthought, added in for another purpose altogether, which can be stripped out completely if it is not needed...Obviously, you have missed the important part of the process which was shown, and should review the solution in more detail. The purpose of what was presented was NOT to generate schema code, it simply does that in addition. Take time to understand what you are criticizing, prior to doing that.
With the above code, the origional asker of the question can simply take the statements used to get indexes and foreign keys and join them as subqueries for what he needed.....which looks like exactly what he said he would do in fact......
April 28, 2006 at 6:42 am
Will the following do the job:
SELECT SCHEMA_NAME(o.schema_id) As SchemaName
, o.Name As TableName
, c.Name As ColumnName
FROM sys.objects o INNER JOIN sys.columns c ON o.object_id = c.object_id
INNER JOIN sysforeignkeys fk ON c.object_id = fk.fkeyid And c.column_id = fk.fkey
LEFT OUTER JOIN sysindexkeys ik ON fk.fkeyid = ik.id And fk.fkey = ik.colid
WHERE o.Name <> 'sysdiagrams'
And SCHEMA_NAME(o.schema_id) <> 'sys'
And ik.id Is Null
ORDER BY SchemaName
, TableName
, ColumnName
April 28, 2006 at 11:01 am
Most efficiently......a very good solution overall....
April 28, 2006 at 5:00 pm
When I try to run this query I get the following error:
Server: Msg 195, Level 15, State 10, Line 1
'SCHEMA_NAME' is not a recognized function name.
Any ideas?
Thanks,
Harley
Viewing 15 posts - 1 through 15 (of 16 total)
You must be logged in to reply to this topic. Login to reply