Listing all indexes - sql-server

I'm wondering what the simplest way to list all indexes for all tables in a database is.
Should I call sp_helpindex for each table and store the results in a temp table, or is there an easier way?
Can anyone explain why constraints are stored in sysobjects but indexes are not?

Here's an example of the kind of query you need:
select
i.name as IndexName,
o.name as TableName,
ic.key_ordinal as ColumnOrder,
ic.is_included_column as IsIncluded,
co.[name] as ColumnName
from sys.indexes i
join sys.objects o on i.object_id = o.object_id
join sys.index_columns ic on ic.object_id = i.object_id
and ic.index_id = i.index_id
join sys.columns co on co.object_id = i.object_id
and co.column_id = ic.column_id
where i.[type] = 2
and i.is_unique = 0
and i.is_primary_key = 0
and o.[type] = 'U'
--and ic.is_included_column = 0
order by o.[name], i.[name], ic.is_included_column, ic.key_ordinal
;
This one is somewhat specific to a certain purpose (I use it in a little C# app to find duplicate indexes and format the output so it's actually readable by a human). But you could easily adapt it to your needs.

You could reference sysindexes
Another trick is to look at the text of sp_helpindex to see how it reconstructs information from the underlying tables.
sp_helptext 'sp_helpindex'
I don't have a reference for this, but I believe constraints are not stored in sysobjects because they are a different kind of thing; sysindexes contains meta-data about objects in sysobjects.

If you need more information, here is a nice SQL script, which I use from time to time:
DECLARE #TabName varchar(100)
CREATE TABLE #temp (
TabName varchar(200), IndexName varchar(200), IndexDescr varchar(200),
IndexKeys varchar(200), IndexSize int
)
DECLARE cur CURSOR FAST_FORWARD LOCAL FOR
SELECT name FROM sysobjects WHERE xtype = 'U'
OPEN cur
FETCH NEXT FROM cur INTO #TabName
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #temp (IndexName, IndexDescr, IndexKeys)
EXEC sp_helpindex #TabName
UPDATE #temp SET TabName = #TabName WHERE TabName IS NULL
FETCH NEXT FROM cur INTO #TabName
END
CLOSE cur
DEALLOCATE cur
DECLARE #ValueCoef int
SELECT #ValueCoef = low FROM Master.dbo.spt_values WHERE number = 1 AND type = N'E'
UPDATE #temp SET IndexSize =
((CAST(sysindexes.used AS bigint) * #ValueCoef)/1024)/1024
FROM sysobjects INNER JOIN sysindexes ON sysobjects.id = sysindexes.id
INNER JOIN #temp T ON T.TabName = sysobjects.name AND T.IndexName = sysindexes.name
SELECT * FROM #temp
ORDER BY TabName, IndexName
DROP TABLE #temp

Here is a script that will return SQL statements to recreate all the indexes in a database.
SELECT ' CREATE ' +
CASE
WHEN I.is_unique = 1 THEN ' UNIQUE '
ELSE ''
END +
I.type_desc COLLATE DATABASE_DEFAULT + ' INDEX ' +
I.name + ' ON ' +
SCHEMA_NAME(T.schema_id) + '.' + T.name + ' ( ' +
KeyColumns + ' ) ' +
ISNULL(' INCLUDE (' + IncludedColumns + ' ) ', '') +
ISNULL(' WHERE ' + I.filter_definition, '') + ' WITH ( ' +
CASE
WHEN I.is_padded = 1 THEN ' PAD_INDEX = ON '
ELSE ' PAD_INDEX = OFF '
END + ',' +
'FILLFACTOR = ' + CONVERT(
CHAR(5),
CASE
WHEN I.fill_factor = 0 THEN 100
ELSE I.fill_factor
END
) + ',' +
-- default value
'SORT_IN_TEMPDB = OFF ' + ',' +
CASE
WHEN I.ignore_dup_key = 1 THEN ' IGNORE_DUP_KEY = ON '
ELSE ' IGNORE_DUP_KEY = OFF '
END + ',' +
CASE
WHEN ST.no_recompute = 0 THEN ' STATISTICS_NORECOMPUTE = OFF '
ELSE ' STATISTICS_NORECOMPUTE = ON '
END + ',' +
' ONLINE = OFF ' + ',' +
CASE
WHEN I.allow_row_locks = 1 THEN ' ALLOW_ROW_LOCKS = ON '
ELSE ' ALLOW_ROW_LOCKS = OFF '
END + ',' +
CASE
WHEN I.allow_page_locks = 1 THEN ' ALLOW_PAGE_LOCKS = ON '
ELSE ' ALLOW_PAGE_LOCKS = OFF '
END + ' ) ON [' +
DS.name + ' ] ' + CHAR(13) + CHAR(10) + ' GO' [CreateIndexScript]
FROM sys.indexes I
JOIN sys.tables T
ON T.object_id = I.object_id
JOIN sys.sysindexes SI
ON I.object_id = SI.id
AND I.index_id = SI.indid
JOIN (
SELECT *
FROM (
SELECT IC2.object_id,
IC2.index_id,
STUFF(
(
SELECT ' , ' + C.name + CASE
WHEN MAX(CONVERT(INT, IC1.is_descending_key))
= 1 THEN
' DESC '
ELSE
' ASC '
END
FROM sys.index_columns IC1
JOIN sys.columns C
ON C.object_id = IC1.object_id
AND C.column_id = IC1.column_id
AND IC1.is_included_column =
0
WHERE IC1.object_id = IC2.object_id
AND IC1.index_id = IC2.index_id
GROUP BY
IC1.object_id,
C.name,
index_id
ORDER BY
MAX(IC1.key_ordinal)
FOR XML PATH('')
),
1,
2,
''
) KeyColumns
FROM sys.index_columns IC2
--WHERE IC2.Object_id = object_id('Person.Address') --Comment for all tables
GROUP BY
IC2.object_id,
IC2.index_id
) tmp3
)tmp4
ON I.object_id = tmp4.object_id
AND I.Index_id = tmp4.index_id
JOIN sys.stats ST
ON ST.object_id = I.object_id
AND ST.stats_id = I.index_id
JOIN sys.data_spaces DS
ON I.data_space_id = DS.data_space_id
JOIN sys.filegroups FG
ON I.data_space_id = FG.data_space_id
LEFT JOIN (
SELECT *
FROM (
SELECT IC2.object_id,
IC2.index_id,
STUFF(
(
SELECT ' , ' + C.name
FROM sys.index_columns IC1
JOIN sys.columns C
ON C.object_id = IC1.object_id
AND C.column_id = IC1.column_id
AND IC1.is_included_column =
1
WHERE IC1.object_id = IC2.object_id
AND IC1.index_id = IC2.index_id
GROUP BY
IC1.object_id,
C.name,
index_id
FOR XML PATH('')
),
1,
2,
''
) IncludedColumns
FROM sys.index_columns IC2
--WHERE IC2.Object_id = object_id('Person.Address') --Comment for all tables
GROUP BY
IC2.object_id,
IC2.index_id
) tmp1
WHERE IncludedColumns IS NOT NULL
) tmp2
ON tmp2.object_id = I.object_id
AND tmp2.index_id = I.index_id
WHERE I.is_primary_key = 0
AND I.is_unique_constraint = 0
--AND T.name NOT LIKE 'mt_%'
--AND I.name NOT LIKE 'mt_%'
--AND I.Object_id = object_id('Person.Address') --Comment for all tables
--AND I.name = 'IX_Address_PostalCode' --comment for all indexes

I do not have a clear explanation why indexes are not stored in sys.objects. But I would like to contribute to find a simple way to list all indexes for all tables and views in a database. The following query retrieves all indexes including their type and also their object id and object type.
use /*Enter here your database*/
go
select A.Object_id,B.name,B.type,B.type_desc, A.index_id,A.type,A.type_desc
from sys.indexes A left join sys.objects B on A.object_id=B.object_id
where B.type = 'U' or B.type='V' /*filtering on U or V to retrieve tables and views only*/
order by B.name ASC /*Optional sorting*/

I've written this code to iterate through all the databases in your server and push it to a table in a database named Maintenance. You should create this database first and then create a table in that database with the following fields:
CREATE TABLE [dbo].[DBCC_Stats](
[ID] [int] IDENTITY(1,1) NOT NULL,
[DatabaseName] [varchar](50) NULL,
[SchemaName] [nvarchar](128) NULL,
[TableName] [sysname] NOT NULL,
[StatName] [nvarchar](128) NULL,
[modification_counter] [bigint] NULL,
[rows] [bigint] NULL,
[rows_sampled] [bigint] NULL,
[% Rows Sampled] [bigint] NULL,
[last_updated] [datetime2](7) NULL,
[DateEntered] [datetime] NULL,
CONSTRAINT [PK_DBCC_Stats] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[DBCC_Stats] ADD CONSTRAINT [DF_DBCC_Stats_DateEntered] DEFAULT (getdate()) FOR [DateEntered]
To use the stored procedure below you'd pass in the server name.
usp_Execute_Stats '[YourServerName]'
`CREATE PROCEDURE usp_Execute_Stats
#ServerName varchar(100)
AS
BEGIN
DECLARE #strSQL varchar(max)
SET #strSQL='USE ?
SELECT ''' + '?' + ''' AS DatabaseName,OBJECT_SCHEMA_NAME(obj.object_id) SchemaName, obj.name TableName,
stat.name StatName, modification_counter,
[rows], rows_sampled, rows_sampled* 100 / [rows] AS [% Rows Sampled],
last_updated
FROM ' + #ServerName + '.' + '?' + '.sys.objects AS obj
INNER JOIN ' + #ServerName + '.' + '?' + '.sys.stats AS stat ON stat.object_id = obj.object_id
CROSS APPLY sys.dm_db_stats_properties(stat.object_id, stat.stats_id) AS sp
WHERE obj.is_ms_shipped = 0
ORDER BY modification_counter DESC'
INSERT INTO Maintenance.dbo.vwDBCC_Stats
EXEC sp_MSforeachdb #strSQL
--Delete older logs
DELETE Maintenance.dbo.DBCC_Stats
--WHERE DatabaseName IN('Master','Model','MSDB','TempDB')
WHERE [DateEntered] < getdate()-14
END`

Related

SQL Server Find Index Definitions

SQL Server Find Index Definitions
I am tasked to migrate a set of tables schema, content, and table objects to an Azure SQLdb. I ran in to a problem where triggers and indexes. They didn't get copied over. Triggers were easy to get from the sys.modules table but no luck with indexes.
I had no luck finding anything here or on the interwebs so I wrote this. I hope this helps someone.
/*
========================WARNING!!!!!!!!================================
The follow has the following defaults that may not suite your needs.
PRIMARY KEY CLUSTERED
STATISTICS_NORECOMPUTE = OFF
SORT_IN_TEMPDB = OFF I leave this on Since my TempDB has the room,
bandwidth, and it's own IO channel.
It's up to you though.
ONLINE = OFF
Created ON [PRIMARY]
Created by: Frank Canapa
Date 2020-07-24
Based on the work of others on the interwebs
*/
;WITH MyCTE
AS
(
SELECT T = t.[name]
,A = 'ALTER TABLE [' + schema_name(t.schema_id) + '].[' + t.[name]+'] ADD CONSTRAINT [' + i.[name] + '] PRIMARY KEY CLUSTERED (' + substring(column_names, 1, len(column_names)-1)
, B =')WITH (PAD_INDEX = ' +
CASE WHEN i.[is_padded] = 0 THEN 'OFF'
WHEN i.[is_padded] = 1 THEN 'ON'
END
, C =', STATISTICS_NORECOMPUTE = OFF' + ', SORT_IN_TEMPDB = OFF' + ', IGNORE_DUP_KEY = ' +
CASE WHEN i.[IGNORE_DUP_KEY] = 0 THEN 'OFF'
WHEN i.[IGNORE_DUP_KEY] = 1 THEN 'ON'
END
, D = ', ONLINE = OFF' + ', ALLOW_ROW_LOCKS = ' +
CASE WHEN i.[ALLOW_ROW_LOCKS] = 0 THEN 'OFF'
WHEN i.[ALLOW_ROW_LOCKS] = 1 THEN 'ON'
END
, E = ', ALLOW_PAGE_LOCKS = ' +
CASE WHEN i.[ALLOW_PAGE_LOCKS] = 0 THEN 'OFF'
WHEN i.[ALLOW_PAGE_LOCKS] = 1 THEN 'ON'
END
, F = CASE WHEN i.[fill_factor] = 0 THEN ''
WHEN i.[fill_factor] > 0 THEN ', FILLFACTOR = ' + CAST(i.[fill_factor] AS varchar(3))
END
, G = ') ON [PRIMARY]'
FROM sys.objects t
INNER JOIN sys.indexes i
ON t.object_id = i.object_id
CROSS APPLY(SELECT col.[name] + ', '
FROM sys.index_columns ic
INNER JOIN sys.columns col
ON ic.object_id = col.object_id
AND ic.column_id = col.column_id
WHERE ic.object_id = t.object_id
AND ic.index_id = i.index_id
ORDER by key_ordinal
for xml path ('') ) D (column_names)
WHERE i.[is_primary_key] = 1
AND t.is_ms_shipped <> 1
AND index_id > 0
UNION
SELECT T = t.[name]
,A = 'CREATE ' +
CASE WHEN i.[type] = 1 THEN 'CLUSTERED'
WHEN i.[type] = 2 THEN 'NONCLUSTERED'
WHEN i.[type] = 3 THEN 'XML'
WHEN i.[type] = 4 THEN 'Spatial '
WHEN i.[type] = 5 THEN 'Clustered columnstore'
WHEN i.[type] = 6 THEN 'Nonclustered columnstore'
WHEN i.[type] = 7 THEN 'Nonclustered hash'
END
, B = ' INDEX [' + i.[name] + '] ON ['+ schema_name(t.schema_id) + '].[' + t.[name]+'] ('+substring(column_names, 1, len(column_names)-1) + ')WITH (PAD_INDEX = ' +
CASE WHEN i.[is_padded] = 0 THEN 'OFF'
WHEN i.[is_padded] = 1 THEN 'ON'
END
, C = ', STATISTICS_NORECOMPUTE = OFF' + ', SORT_IN_TEMPDB = OFF' + ', IGNORE_DUP_KEY = ' +
CASE WHEN i.[IGNORE_DUP_KEY] = 0 THEN 'OFF'
WHEN i.[IGNORE_DUP_KEY] = 1 THEN 'ON'
END
, D = ', ONLINE = OFF' + ', ALLOW_ROW_LOCKS = ' +
CASE WHEN i.[ALLOW_ROW_LOCKS] = 0 THEN 'OFF'
WHEN i.[ALLOW_ROW_LOCKS] = 1 THEN 'ON'
END
, E = ', ALLOW_PAGE_LOCKS = ' +
CASE WHEN i.[ALLOW_PAGE_LOCKS] = 0 THEN 'OFF'
WHEN i.[ALLOW_PAGE_LOCKS] = 1 THEN 'ON'
END
, F = CASE WHEN i.[fill_factor] = 0 THEN ''
WHEN i.[fill_factor] > 0 THEN ', FILLFACTOR = ' + CAST(i.[fill_factor] AS varchar(3))
END
, G = ') ON [PRIMARY]'
FROM sys.objects t
INNER JOIN sys.indexes i
ON t.object_id = i.object_id
CROSS APPLY(SELECT col.[name] + ', '
FROM sys.index_columns ic
INNER JOIN sys.columns col
ON ic.object_id = col.object_id
AND ic.column_id = col.column_id
WHERE ic.object_id = t.object_id
AND ic.index_id = i.index_id
ORDER by key_ordinal
for xml path ('') ) D (column_names)
WHERE i.[is_primary_key] = 0
AND substring(i.[name],1,1 ) != '_'
AND t.is_ms_shipped <> 1
AND index_id > 0
)
SELECT T, A + B + C + D + E + F + G
FROM MyCTE
ORDER BY T

T-SQL Cursor + Dynamic SQL (Number of rows that reference a specific primary key)

I've to count how many times a key is being used across tables. I have the following code that gets all the tables that reference the key:
SELECT s.SCHEMA_NAME,
OBJECT_NAME(f.parent_object_id) AS TableName,
COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
OUTER APPLY
(
SELECT i.SCHEMA_NAME
FROM INFORMATION_SCHEMA.SCHEMATA i
INNER JOIN SYS.TABLES s
ON i.SCHEMA_NAME = SCHEMA_NAME(s.SCHEMA_ID)
WHERE f.parent_object_id = s.object_id
) AS s
WHERE OBJECT_NAME (f.referenced_object_id) = 'Languages'
Result:
SchemaName, TableName, ColumnName
And this is the query I have to do for each returned row, and finally SUM all of them:
SELECT COUNT(*)
FROM SchemaName.TableName t
WHERE t.ColumnName = #LanguageId
I've been reading about CURSORs and dynamic SQL but I couldn't manage a way to make it work (never used any of them).
The use of cursors / dynamic SQL is not a requirement. If there are simpler ways I appreciate.
EDIT: I managed to make it work.
EDIT2: Some refactoring and full implementation of the actual requirement.
DECLARE #WantedDefaultLanguageId INT = 1;
--Internal Variables
DECLARE #DefaultLanguageId INT = (SELECT Id FROM i18n.Languages WHERE IsDefault = 1)
, #SqlCommand NVARCHAR(1000)
, #SchemaName SYSNAME
, #TableName SYSNAME
, #FieldName SYSNAME
, #CurrentValue INT
, #DefaultTotal INT = 0
, #WantedTotal INT = 0;
DECLARE relationships CURSOR
LOCAL FAST_FORWARD READ_ONLY
FOR SELECT schemaNames.SCHEMA_NAME,
OBJECT_NAME(foreignKeys.parent_object_id) AS TableName,
COL_NAME(foreignKeysColumns.parent_object_id, foreignKeysColumns.parent_column_id) AS ColumnName
FROM sys.foreign_keys AS foreignKeys
INNER JOIN sys.foreign_key_columns AS foreignKeysColumns
ON foreignKeys.OBJECT_ID = foreignKeysColumns.constraint_object_id
OUTER APPLY
(
SELECT metadata.SCHEMA_NAME
FROM INFORMATION_SCHEMA.SCHEMATA metadata
INNER JOIN SYS.TABLES AS sysTables
ON metadata.SCHEMA_NAME = SCHEMA_NAME(sysTables.SCHEMA_ID)
WHERE foreignKeys.parent_object_id = sysTables.object_id
) AS schemaNames
WHERE OBJECT_NAME (foreignKeys.referenced_object_id) = 'Languages';
IF #DefaultLanguageId = #WantedDefaultLanguageId
SELECT 1;
ELSE
BEGIN
OPEN relationships
FETCH NEXT FROM relationships
INTO #SchemaName, #TableName, #FieldName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SqlCommand = '
SELECT #CurrentValue = COUNT(*)
FROM ' + #SchemaName + '.' + #TableName + ' tableName
WHERE tableName.' + #FieldName + ' = ' + CAST(#DefaultLanguageId AS nvarchar(1000))
EXEC sp_executesql #SqlCommand, N'#CurrentValue INT OUTPUT', #CurrentValue OUTPUT
SET #DefaultTotal += #CurrentValue
--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
SET #SqlCommand = '
SELECT #CurrentValue = COUNT(*)
FROM ' + #SchemaName + '.' + #TableName + ' tableName
WHERE tableName.' + #FieldName + ' = ' + CAST(#WantedDefaultLanguageId AS nvarchar(1000))
EXEC sp_executesql #SqlCommand, N'#CurrentValue INT OUTPUT', #CurrentValue OUTPUT
SET #WantedTotal += #CurrentValue
--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FETCH NEXT FROM relationships
INTO #SchemaName, #TableName, #FieldName;
END
CLOSE relationships
DEALLOCATE relationships
SELECT CASE
WHEN #WantedTotal = #DefaultTotal THEN
1
ELSE 0
END
END;
It's taking a very long time to run. This query won't be ran very often but any help to improve it / better ways to implement this functionality is appreciated.
(I'll close the question later on).
Edit3: Ok so here is a example of what I need:
Table: Language
Id Language
1 English
RelationalTable1
... LanguageId
1
1
2
AllOtherRelationalTables
...
I need to do a COUNT of how many times the LanguageId = 1 (for all tables that reference the Language Table). The code is working, but would like to know if there is a simpler way of implementing this / improving it.
I'm not exactly sure what precisely you're asking for, but this MAY help you...
This SQL will get you all the FK column information:
SELECT fkeys.[name] AS FKName,
OBJECT_NAME(fkeys.parent_object_id) AS TableName,
(SELECT STUFF((SELECT ',' + c.[name]
FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.parent_object_id = t.object_id
INNER JOIN sys.columns as c ON t.object_id = c.object_id
INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.parent_column_id
AND fc.constraint_object_id = fk.object_id
AND fc.parent_object_id = fk.parent_object_id
WHERE fk.[name] = fkeys.[name]
FOR XML PATH ('')), 1, 1, '')) AS FKFolumns,
OBJECT_NAME(fkeys.referenced_object_id) AS ReferencedTableName,
(SELECT STUFF((SELECT ',' + c.[name]
FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.referenced_object_id = t.object_id
INNER JOIN sys.columns as c ON t.object_id = c.object_id
INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.referenced_column_id
AND fc.constraint_object_id = fk.object_id
AND fc.referenced_object_id = fk.referenced_object_id
WHERE fk.[name] = fkeys.[name]
FOR XML PATH ('')), 1, 1, '')) AS ReferencedFKColumns
FROM sys.foreign_keys fkeys
You can use this to extract the count of things that reference a given table and column:
WITH AllFKInfo AS (
SELECT fkeys.[name] AS FKName,
OBJECT_NAME(fkeys.parent_object_id) AS TableName,
(SELECT STUFF((SELECT ',' + c.[name]
FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.parent_object_id = t.object_id
INNER JOIN sys.columns as c ON t.object_id = c.object_id
INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.parent_column_id
AND fc.constraint_object_id = fk.object_id
AND fc.parent_object_id = fk.parent_object_id
WHERE fk.[name] = fkeys.[name]
FOR XML PATH ('')), 1, 1, '')) AS FKFolumns,
OBJECT_NAME(fkeys.referenced_object_id) AS ReferencedTableName,
(SELECT STUFF((SELECT ',' + c.[name]
FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.referenced_object_id = t.object_id
INNER JOIN sys.columns as c ON t.object_id = c.object_id
INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.referenced_column_id
AND fc.constraint_object_id = fk.object_id
AND fc.referenced_object_id = fk.referenced_object_id
WHERE fk.[name] = fkeys.[name]
FOR XML PATH ('')), 1, 1, '')) AS ReferencedFKColumns
FROM sys.foreign_keys fkeys
)
SELECT ReferencedTableName, ReferencedFKColumns, COUNT(ReferencedFKColumns) AS CountOfReferences
FROM AllFKInfo
GROUP BY ReferencedTableName, ReferencedFKColumns
ORDER BY ReferencedTableName, ReferencedFKColumns
The answer in the post works, probably not the best solution but does the job.
DECLARE #WantedDefaultLanguageId INT = 1;
--Internal Variables
DECLARE #DefaultLanguageId INT = (SELECT Id FROM i18n.Languages WHERE IsDefault = 1)
, #SqlCommand NVARCHAR(1000)
, #SchemaName SYSNAME
, #TableName SYSNAME
, #FieldName SYSNAME
, #CurrentValue INT
, #DefaultTotal INT = 0
, #WantedTotal INT = 0;
DECLARE relationships CURSOR
LOCAL FAST_FORWARD READ_ONLY
FOR SELECT schemaNames.SCHEMA_NAME,
OBJECT_NAME(foreignKeys.parent_object_id) AS TableName,
COL_NAME(foreignKeysColumns.parent_object_id, foreignKeysColumns.parent_column_id) AS ColumnName
FROM sys.foreign_keys AS foreignKeys
INNER JOIN sys.foreign_key_columns AS foreignKeysColumns
ON foreignKeys.OBJECT_ID = foreignKeysColumns.constraint_object_id
OUTER APPLY
(
SELECT metadata.SCHEMA_NAME
FROM INFORMATION_SCHEMA.SCHEMATA metadata
INNER JOIN SYS.TABLES AS sysTables
ON metadata.SCHEMA_NAME = SCHEMA_NAME(sysTables.SCHEMA_ID)
WHERE foreignKeys.parent_object_id = sysTables.object_id
) AS schemaNames
WHERE OBJECT_NAME (foreignKeys.referenced_object_id) = 'Languages';
IF #DefaultLanguageId = #WantedDefaultLanguageId
SELECT 1;
ELSE
BEGIN
OPEN relationships
FETCH NEXT FROM relationships
INTO #SchemaName, #TableName, #FieldName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SqlCommand = '
SELECT #CurrentValue = COUNT(*)
FROM ' + #SchemaName + '.' + #TableName + ' tableName
WHERE tableName.' + #FieldName + ' = ' + CAST(#DefaultLanguageId AS nvarchar(1000))
EXEC sp_executesql #SqlCommand, N'#CurrentValue INT OUTPUT', #CurrentValue OUTPUT
SET #DefaultTotal += #CurrentValue
--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
SET #SqlCommand = '
SELECT #CurrentValue = COUNT(*)
FROM ' + #SchemaName + '.' + #TableName + ' tableName
WHERE tableName.' + #FieldName + ' = ' + CAST(#WantedDefaultLanguageId AS nvarchar(1000))
EXEC sp_executesql #SqlCommand, N'#CurrentValue INT OUTPUT', #CurrentValue OUTPUT
SET #WantedTotal += #CurrentValue
--■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FETCH NEXT FROM relationships
INTO #SchemaName, #TableName, #FieldName;
END
CLOSE relationships
DEALLOCATE relationships
SELECT CASE
WHEN #WantedTotal = #DefaultTotal THEN
1
ELSE 0
END
END;

How to cascade-delete temporarily or on-demand?

Sometimes I'm trying to delete just ONE row in MSSQL and I fall into countless deletes up the hierarchy because of references due to foreign-key constraints.
Is there any quick way to automatically cascade-delete without having to setup the foreign-key constraints with cascade delete? It's just this one time that I need the cascade-delete... on-demand -- not always.
Any chance? Any equivalents?
If you want a point and shoot dynamic sql solution, this uses a recursive query to build a table hierarchy for foreign keys that descends from a particlar key. With that it generates the delete statements that need to be executed, in order (hopefully), to delete a particular row from a table.
use AdventureWorks2012
declare #tablename sysname = N'Production.Product';
declare #primarykeycolumn sysname = N'ProductId';
declare #value nvarchar(128) = '2';
declare #sql nvarchar(max);
;with tableHierarchy as (
select
object_id = p.object_id
, parent_id = cast(null as int)
, schemaName = schema_name(p.schema_id)
, tableName = object_name(p.object_id)
, parentObjectName = cast(null as sysname)
, parentToChild = cast(object_name(p.object_id) as varchar(max))
, childToParent = cast(object_name(p.object_id) as varchar(max))
, treelevel = 0
, keyName = p.name
, columnName = c.name
, columnId = c.column_id
, parentColumnName = c.name
from sys.objects as p
inner join sys.columns c
on p.object_id = c.object_id
where p.object_id = object_id(#tablename)
and c.name = #primarykeycolumn
union all
select
object_id = fk.parent_object_id
, parent_id = fk.referenced_object_id
, schemaName = schema_name(fk.schema_id)
, tableName = object_name(fk.parent_object_id)
, parentObjectName = object_name(fk.referenced_object_id)
, parentToChild = parentToChild + ' \ ' + cast(object_name(fk.parent_object_id) as varchar(128))
, childToParent = cast(object_name(fk.parent_object_id) as varchar(128)) + ' \ ' + childToParent
, treelevel = th.treelevel + 1
, keyName = fk.name
, columnName = c.name
, columnId = c.column_id
, parentColumnName = rc.name
from tableHierarchy as th
inner join sys.foreign_keys as fk
on fk.referenced_object_id = th.object_id
and fk.referenced_object_id != fk.parent_object_id
inner join sys.foreign_key_columns fkc
on fk.object_id = fkc.constraint_object_id
and fkc.referenced_column_id = th.columnId
inner join sys.columns c
on fkc.parent_object_id = c.object_id
and fkc.parent_column_id = c.column_id
inner join sys.columns rc
on fkc.referenced_object_id = rc.object_id
and fkc.referenced_column_id = rc.column_id
)
select #sql = stuff((
select
char(10)
--+'/* treelevel: '+convert(nvarchar(10),treelevel)
--+' | ' + childtoparent +' */'+char(10)
+'delete from '+quotename(schemaName)+'.'+quotename(tableName)
+' where '+quotename(columnName)+' = '+#value+';'
from tableHierarchy
group by treelevel, childtoparent, schemaName, tableName, columnName
order by treelevel desc, childtoparent
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
option ( maxrecursion 100 );
select #sql as CodeGenerated;
--exec sp_executesql #sql;
Code generated:
delete from [Sales].[SalesOrderDetail] where [ProductID] = 2;
delete from [Production].[BillOfMaterials] where [ComponentID] = 2;
delete from [Production].[BillOfMaterials] where [ProductAssemblyID] = 2;
delete from [Production].[ProductCostHistory] where [ProductID] = 2;
delete from [Production].[ProductDocument] where [ProductID] = 2;
delete from [Production].[ProductInventory] where [ProductID] = 2;
delete from [Production].[ProductListPriceHistory] where [ProductID] = 2;
delete from [Production].[ProductProductPhoto] where [ProductID] = 2;
delete from [Production].[ProductReview] where [ProductID] = 2;
delete from [Purchasing].[ProductVendor] where [ProductID] = 2;
delete from [Purchasing].[PurchaseOrderDetail] where [ProductID] = 2;
delete from [Sales].[ShoppingCartItem] where [ProductID] = 2;
delete from [Sales].[SpecialOfferProduct] where [ProductID] = 2;
delete from [Production].[TransactionHistory] where [ProductID] = 2;
delete from [Production].[WorkOrder] where [ProductID] = 2;
delete from [Production].[Product] where [ProductID] = 2;
Since you need it for one-off purposes, but you don't want it always present, I think your best bet it to write a stored proc.
Just delete from the table furthest away first, and work your way back up the tree
Here's an example:
Create Proc CascaseDeleteMyTable
#MyTableId Int
As
Delete From ChildTable33 Where ChildParent33Id In (Select ChildParent33Id From ChildParent33 Where MyTableId = #MyTableId)
Delete From ChildTable2 Where MyTableId = #MyTableId
GO

How do I list (or export) the code for all triggers in a database?

I'm changing from local time to UTC-time in our database.
There are alot of triggers that copies information to history tables that currently uses GETDATE().
I would like to find every trigger that uses GETDATE() (instead of GETUTCDATE()) in the database, is there any way to do this automatic?
I've listed them by select * from sys.triggers but I also need to see the actual code to be able to find the use of GETDATE().
Your could try the following:
SELECT o.[name],
c.[text]
FROM sys.objects AS o
INNER JOIN sys.syscomments AS c
ON o.object_id = c.id
WHERE o.[type] = 'TR'
Here's the script I used to export triggers:
DECLARE #t VARCHAR (MAX)
SET #t = ''
SELECT #t = #t + 'IF EXISTS (SELECT 1 FROM sys.triggers WHERE object_id = OBJECT_ID(N''' + s.name + '.' + o.name +'''))
DROP TRIGGER ' + s.name + '.' + o.name + '
GO
' + OBJECT_DEFINITION (OBJECT_ID( s.name + '.' + o.name )) +'
GO
'
FROM sys.objects o
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
INNER JOIN sys.objects o2 ON o.parent_object_id = o2.object_id
WHERE o. [type] = 'TR'
AND (
OBJECTPROPERTY ( o.object_id , 'ExecIsInsertTrigger' ) = 1
OR
OBJECTPROPERTY ( o.object_id , 'ExecIsUpdateTrigger' ) = 1
OR
OBJECTPROPERTY ( o.object_id , 'ExecIsDeleteTrigger' ) = 1
)
SELECT #t AS [processing-instruction(x)] FOR XML PATH ('')
More details here if it doesn't make sense to anyone:
http://paulfentonsql.co.uk/2015/09/01/generate-createdrop-statements-for-all-triggers-of-a-given-type/
If you want to export all triggers from the database... here's some code:
DECLARE #vchServerName VARCHAR(500)
DECLARE #vchDBName VARCHAR(500)
DECLARE #intLoop INTEGER
DECLARE #intTotalRows INTEGER
DECLARE #intId INTEGER
DECLARE #vchName VARCHAR(500)
DECLARE #vchSQL VARCHAR(4000)
-- supress count (just so log looks nicer!)
SET NOCOUNT ON
-- get current DB and server
SET #vchDBName = DB_NAME()
SET #vchServerName = ##servername
-- get list of XXX
SELECT ROW_NUMBER() OVER (ORDER BY o.object_id ) fldRowNum
,o.object_id fldId
,s.name + '.' + o.name fldName
INTO #tblFound
FROM sys.objects o
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE [type] = 'TR'
SET #intTotalRows = ##ROWCOUNT
SET #intLoop = 1
-- loop thru list
WHILE #intLoop <= #intTotalRows
BEGIN
SELECT #intID = fldId
,#vchName = fldName
FROM #tblFound
WHERE fldRowNum = #intLoop
PRINT 'Exporting ' + #vchName + '...'
-- NOTE: I'm using a version of bcp that doens't have -D parameter so I need to use DB name here
SET #vchSQL = 'SELECT c.[text] FROM ' + #vchDBName + '.sys.syscomments AS c WHERE c.id = ' + CONVERT(VARCHAR,#intID)
SET #vchSQL = 'bcp "' + #vchSQL + '" queryout "c:\temp\' + #vchName + '.sql" -c -t -T -S ' + #vchServerName
EXEC master..XP_CMDSHELL #vchSQL
SET #intLoop = #intLoop + 1
END
DROP TABLE #tblFound
PRINT 'Done'

Tool for Scripting Table Data

Are there any free tools for scripting MSSQL table data? I'll gladly write one, but am hoping it's been done, and the app matured a bit before/
Here are some scripts I wrote for reverse engineering SQL server schemas. They may be of some use. Also, as a general interest they give some examples of how to get various bits of information out of the data dictionary. I've added an MIT license below to make permission-to-use explicit and for some basic no-implicit-warranty CYA. Enjoy.
-- ====================================================================
-- === reverse_engineer_2005.sql ======================================
-- ====================================================================
--
-- Script to generate table, index, pk, view and fk definitions from
-- a SQL Server 2005 database. Adapted from one I originally wrote
-- for SQL Server 2000. It's not comprehensive (doesn't extract
-- partition schemes) but it does do defaults and computed columns
--
-- Run the script with 'results to text' and cut/paste the output into
-- the editor window. Set the schema as described below.
--
-- Copyright (c) 2004-2008 Concerned of Tunbridge Wells
--
-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:
--
-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.
--
-- ====================================================================
--
set nocount on
-- This does a specific schema. Set the schema here
--
declare #schema varchar (max)
select #schema = 'dbo'
if object_id ('tempdb..#objects') is not null begin
drop table #objects
end
if object_id ('tempdb..#views') is not null begin
drop table #views
end
if object_id ('tempdb..#types') is not null begin
drop table #types
end
-- Gets lists of tables and views belonging to the schema
--
select o.name
,o.object_id
into #objects
from sys.objects o
join sys.schemas s
on s.schema_id = o.schema_id
where o.type in ('U')
and s.name = #schema
select o.name
,o.object_id
into #views
from sys.objects o
join sys.schemas s
on s.schema_id = o.schema_id
where o.type in ('V')
and s.name = #schema
-- Some metadata for rendering types
--
select a.*
into #types
from ((select 'decimal' as typename, 6 as format) union all
(select 'numeric', 6) union all
(select 'varbinary', 1) union all
(select 'varchar', 1) union all
(select 'char', 1) union all
(select 'nvarchar', 1) union all
(select 'nchar', 1)) a
-- This generates 'drop table' and 'drop view' statements
--
select 'if exists (select 1' + char(10) +
' from sys.objects o' + char(10) +
' join sys.schemas s' + char(10) +
' on o.schema_id = s.schema_id' + char(10) +
' where o.name = ''' + o.name + '''' + char(10) +
' and s.name = ''' + #schema +'''' + char(10) +
' and o.type = ''U'') begin' + char(10) +
' drop table [' + #schema + '].[' + o.name + ']' + char(10) +
'end' + char(10) +
'go' + char(10)
from sys.objects o
join #objects o2
on o.object_id = o2.object_id
where o.type = 'U'
select 'if exists (select 1' + char(10) +
' from sys.objects o' + char(10) +
' join sys.schemas s' + char(10) +
' on o.schema_id = s.schema_id' + char(10) +
' where o.name = ''' + o.name + '''' + char(10) +
' and s.name = ''' + #schema + '''' + char(10) +
' and o.type = ''V'') begin' + char(10) +
' drop view [' + #schema + '].[' + o.name + ']' + char(10) +
'end' + char(10) +
'go' + char(10)
from sys.objects o
join #objects o2
on o.object_id = o2.object_id
where o.type = 'V'
-- This generates table definitions
--
select case when c.column_id =
(select min(c2.column_id)
from sys.columns c2
where c2.object_id = o.object_id)
then 'create table [' + #schema + '].[' + isnull(o.name, 'XYZZY') + '] (' + char(10)
else ''
end +
left(' [' +rtrim(c.name) + '] ' +
' ', 48) +
isnull(calc.text,
t.name +
case when tc.format & 2 = 2
then ' (' +convert (varchar, c.precision) +
case when tc.format & 2 = 2
then ', ' + convert (varchar, c.scale)
else ''
end + ')'
when tc.format & 1 = 1
then ' (' + convert (varchar, c.max_length) + ')'
else ''
end + ' ' +
case when c.is_nullable <> 0 then 'null'
else 'not null'
end + isnull(ident.text, isnull(con.text, ''))) +
case when c.column_id =
(select max(c2.column_id)
from sys.columns c2
where c2.object_id = o.object_id)
then char(10) + ')' + char(10) + 'go' + char(10)
else ','
end
from sys.objects o
join #objects o2
on o.object_id = o2.object_id
join sys.columns c
on c.object_id = o.object_id
join sys.types t
on c.user_type_id = t.user_type_id
left join
(select object_id,
column_id,
'as ' + definition as text
from sys.computed_columns) calc
on calc.object_id = o.object_id
and calc.column_id = c.column_id
left join
(select parent_object_id,
parent_column_id,
' default ' + definition as text
from sys.default_constraints) con
on con.parent_object_id = o.object_id
and con.parent_column_id = c.column_id
left join
(select o.object_id,
col.column_id,
' identity (' + convert(varchar, ident_seed(o.name)) + ', ' +
convert(varchar, ident_incr(o.name)) + ')' as text
from sys.objects o
join sys.columns col
on o.object_id = col.object_id
where columnproperty (o.object_id, col.name, 'IsIdentity') = 1) as ident
on ident.object_id = o.object_id
and ident.column_id = c.column_id
left join #types tc
on tc.typename = t.name
where o.type = 'U'
order by o.name,
c.column_id
-- This generates view definitions
--
select definition + char(10) + 'go' + char(10)
from sys.sql_modules c
join sys.objects o
on c.object_id = o.object_id
join #views o2
on o.object_id = o2.object_id
-- This generates PK and unique constraints
--
select case when ik.key_ordinal =
(select min(ik2.key_ordinal)
from sys.index_columns ik2
where ik2.object_id = ik.object_id
and ik2.index_id = ik.index_id)
then 'alter table [' + rtrim (s.name) + '].[' + rtrim(t.name) + ']' + char(10) +
' add constraint [' + rtrim (pk.name) + '] ' +
case when pk.type = 'PK' then 'primary key'
when pk.type = 'UQ' then 'unique'
else 'foobar'
end + char(10) +
' ('
else ' ,'
end +
'[' + rtrim(c.name) + ']' +
case when ik.key_ordinal =
(select max(ik2.key_ordinal)
from sys.index_columns ik2
where ik2.object_id = ik.object_id
and ik2.index_id = ik.index_id)
then ')' + char(10) + 'go' + char(10)
else ''
end
from sys.objects t -- table
join #objects o
on t.object_id = o.object_id
join sys.schemas s
on s.schema_id = t.schema_id
join sys.objects pk -- key
on pk.parent_object_id = t.object_id
join sys.columns c -- columns
on c.object_id = t.object_id
join sys.indexes i -- get index for constraint
on i.object_id = t.object_id
and i.name = pk.name
join sys.index_columns ik -- index column and name
on ik.object_id = i.object_id
and ik.index_id = i.index_id
and ik.column_id = c.column_id -- vvv Get the right index
where c.name = index_col('[' + s.name + '].[' + t.name + ']', i.index_id, ik.key_ordinal)
and pk.type in ('PK', 'UQ') --probably redundant
order by t.object_id,
pk.object_id,
ik.key_ordinal
-- This generates indexes
--
select case when ik.key_ordinal =
(select min(ik2.key_ordinal)
from sys.index_columns ik2
where ik2.object_id = ik.object_id
and ik2.index_id = ik.index_id)
then 'create ' +
case when is_unique_constraint = 1 then 'unique '
else ''
end +
'index [' + rtrim(i.name) + ']' + char (10) +
' on [' + rtrim(t.name) + ']' + char (10) +
' ('
else ' ,'
end +
'[' + c.name + ']' +
case when ik.key_ordinal =
(select max(ik2.key_ordinal)
from sys.index_columns ik2
where ik2.object_id = ik.object_id
and ik2.index_id = ik.index_id)
then ')' + char(10) + 'go' + char(10)
else ''
end
from sys.objects t -- table
join #objects o
on o.object_id = t.object_id
join sys.columns c -- columns
on c.object_id = t.object_id
join sys.indexes i -- get index for constraint
on i.object_id = t.object_id
join sys.index_columns ik -- index column and name
on ik.object_id = i.object_id
and ik.index_id = i.index_id
and ik.column_id = c.column_id -- vvv Get the right index
where c.name = index_col(t.name, i.index_id, ik.key_ordinal)
and t.type = 'U'
and i.name <> t.name
and i.name not in
(select c2.name
from sys.objects c2
where c2.parent_object_id = t.object_id
and c2.type in ('PK', 'UQ'))
order by t.name,
i.name,
ik.key_ordinal
-- This generates foreign keys
--
select con.constraint_text as [--constraint_text]
from ((select case when kc.constraint_column_id =
(select min(k2.constraint_column_id)
from sys.foreign_key_columns k2
where k2.constraint_object_id = k.object_id)
then 'alter table [' + #schema + '].[' + rtrim(t.name) + ']' + char(10) +
' add constraint [' + rtrim (k.name) + '] ' + char(10) +
' foreign key ('
else ' ,'
end +
'[' + tc.name + ']' +
case when kc.constraint_column_id =
(select max(k2.constraint_column_id)
from sys.foreign_key_columns k2
where k2.constraint_object_id = k.object_id)
then ')'
else ''
end as constraint_text,
t.name as table_name,
k.name as constraint_name,
kc.constraint_column_id as row_order,
t.object_id
from sys.foreign_keys k
join sys.objects t
on t.object_id = k.parent_object_id
join sys.columns tc
on tc.object_id = t.object_id
join sys.foreign_key_columns kc
on kc.constraint_object_id = k.object_id
and kc.parent_object_id = t.object_id
and kc.parent_column_id = tc.column_id
join sys.objects r
on r.object_id = kc.referenced_object_id
join sys.columns rc
on kc.referenced_object_id = rc.object_id
and kc.referenced_column_id = rc.column_id)
union all
(select case when kc.constraint_column_id =
(select min(k2.constraint_column_id)
from sys.foreign_key_columns k2
where k2.constraint_object_id = k.object_id)
then ' references [' + rtrim(r.name) + ']' + char(10) +
' ('
else ' ,'
end +
'[' + rc.name + ']' +
case when kc.constraint_column_id =
(select max(k2.constraint_column_id)
from sys.foreign_key_columns k2
where k2.constraint_object_id = k.object_id)
then ')' + char(10) + 'go' + char(10)
else ''
end as constraint_text,
t.name as table_name,
k.name as constraint_name,
kc.constraint_column_id + 100 as row_order,
t.object_id
from sys.foreign_keys k
join sys.objects t
on t.object_id = k.parent_object_id
join sys.columns tc
on tc.object_id = t.object_id
join sys.foreign_key_columns kc
on kc.constraint_object_id = k.object_id
and kc.parent_object_id = t.object_id
and kc.parent_column_id = tc.column_id
join sys.objects r
on r.object_id = kc.referenced_object_id
join sys.columns rc
on kc.referenced_object_id = rc.object_id
and kc.referenced_column_id = rc.column_id)) con
join #objects o
on con.object_id = o.object_id
order by con.table_name,
con.constraint_name,
con.row_order
Microsoft SQL Server Database Publishing Wizard is a nice GUI that can script out the structure / data / Procs of a SQL Server 2000/2005 db.
http://www.microsoft.com/downloads/details.aspx?FamilyId=56E5B1C5-BF17-42E0-A410-371A838E570A&displaylang=en
Hope this helps
A quick google and hop pointed me to a Stored proc that should be able to help you. Look at My code library more specifically the file generate_inserts.txt to see if it can help you.
Not really a tool, but the start of one! :)
For loading and unloading table data you could also use bcp, Integration Services or BULK INSERT (for the loading portion). This article describes a method to invoke bcp from within a stored procedure.
TOAD for Oracle can do it, so I suspect TOAD for SQL Server will be able to too.

Resources