List all indexes with included columns(nonkeys) - sql-server

I had tried sp_helpindex but it shows the columns that have index not the included columns. Please tell me how to list all indexes with included columns(nonkeys)?

Try this T-SQL query against the catalog views:
SELECT
IndexName = i.Name,
ColName = c.Name
FROM
sys.indexes i
INNER JOIN
sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
INNER JOIN
sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
WHERE
ic.is_included_column = 1
ORDER BY
i.Name
It basically checks all indexes (sys.indexes) and then links to their columns and checks to see which columns are designed as included columns (ic.is_included_column = 1) and then lists out all those indexes and all those columns.

Copied from http://www.midnightdba.com/Jen/2009/12/get-index-included-column-info/
SELECT
OBJECT_NAME(i.[object_id]) TableName ,
i.[name] IndexName ,
c.[name] ColumnName ,
ic.is_included_column ,
i.index_id ,
i.type_desc ,
i.is_unique ,
i.data_space_id ,
i.ignore_dup_key ,
i.is_primary_key ,
i.is_unique_constraint
FROM
sys.indexes i
JOIN sys.index_columns ic ON ic.object_id = i.object_id and i.index_id = ic.index_id
JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
ORDER BY
tableName ,
ic.index_id ,
ic.index_column_id

If you want a globally system stored procedure available to get index keys/nonkeys(include) columns then run the following code
USE master
GO
CREATE PROCEDURE sp_helpcolindex
#objname NVARCHAR(776)
AS
BEGIN
SELECT
c.Name,
CASE ic.is_included_column WHEN 0 THEN 'Key' ELSE 'Include' END AS [Type]
FROM sys.indexes i
JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
WHERE i.name = #objname
ORDER BY [Type] DESC
END
GO
EXEC sp_ms_marksystemobject 'sp_helpcolindex'
GO

I know this is quite old, but I've created a modified version of sp_helpindex that outputs the include columns.
use master;
if object_id('dbo.sp_helpindex2') is not null
drop procedure dbo.sp_helpindex2;
go
create procedure dbo.sp_helpindex2(
#objname nvarchar(776)-- the table to check for indexes
)
as
begin
-- PRELIM
set nocount on;
declare
#objid int -- the object id of the table
,#indid smallint -- the index id of an index
,#groupid int -- the filegroup id of an index
,#indname sysname
,#groupname sysname
,#status int
,#keys nvarchar(2126) -- Length (16*max_identifierLength)+(15*2)+(16*3)
,#includes nvarchar(2126) -- Length (16*max_identifierLength)+(15*2)+(16*3)
,#dbname sysname
,#ignore_dup_key bit
,#is_unique bit
,#is_hypothetical bit
,#is_primary_key bit
,#is_unique_key bit
,#is_columnstore bit
,#auto_created bit
,#no_recompute bit
,#memory_optimized bit -- For hekaton tables
,#hash_index bit; -- The index is a hash index
-- Check to see that the object names are local to the current database.
select #dbname = parsename(#objname,3);
if #dbname is null
select #dbname = db_name();
else if #dbname <> db_name()
begin
raiserror(15250,-1,-1);
return (1)
end;
-- Check to see that the table exists and initialize #objid.
select #objid = object_id(#objname);
if #objid is null
begin
raiserror(15009,-1,-1,#objname,#dbname);
return (1)
end;
-- OPEN CURSOR OVER INDEXES (skip stats: bug shiloh_51196)
declare ms_crs_ind cursor local static
for select
i.index_id
,i.data_space_id
,i.name
,i.ignore_dup_key
,i.is_unique
,i.is_hypothetical
,i.is_primary_key
,i.is_unique_constraint
,case
when type = 5 or type = 6 then 1
else 0
end
,case
when type = 5 or type = 6 then 0
else s.auto_created
end
,case
when type = 5 or type = 6 then 0
else s.no_recompute
end
,case
when type = 7 then 1
else 0
end
from sys.indexes as i
left join sys.stats as s
on i.object_id = s.object_id
and i.index_id = s.stats_id
where i.object_id = #objid
and type in ( 1,2,5,6,7 );
open ms_crs_ind;
fetch ms_crs_ind into
#indid
,#groupid
,#indname
,#ignore_dup_key
,#is_unique
,#is_hypothetical
,#is_primary_key
,#is_unique_key
,#is_columnstore
,#auto_created
,#no_recompute
,#hash_index;
-- IF NO INDEX, QUIT
if ##fetch_status < 0
begin
deallocate ms_crs_ind;
raiserror(15472,-1,-1,#objname); -- Object does not have any indexes.
return (0)
end;
if (select object_id('tempdb..#spindtab')) is not null
drop table #spindtab
create table #spindtab
(
index_name sysname collate catalog_default not null
,index_id int
,ignore_dup_key bit
,is_unique bit
,is_hypothetical bit
,is_primary_key bit
,is_unique_key bit
,is_columnstore bit
,auto_created bit
,no_recompute bit
,memory_optimized bit
,hash_index bit
,groupname sysname collate catalog_default null
,index_keys nvarchar(2126) collate catalog_default null -- see #keys above for length descr
,include_cols nvarchar(2126) collate catalog_default null -- see #keys above for length descr
);
-- Now check out each index, figure out its type and keys and
-- save the info in a temporary table that we'll print out at the end.
while ##fetch_status >= 0
begin
-- First we'll figure out what the keys are.
declare
#i int
,#thiskey nvarchar(131) -- 128+3
,#is_include_column bit;
select
#keys = ''
,#includes = '';
declare jbo_crs_ind_cols cursor local static
for select
c.name
,ic.is_included_column
from sys.indexes as i
inner join sys.index_columns as ic
on ic.index_id = i.index_id
and ic.object_id = i.object_id
inner join sys.columns as c
on c.column_id = ic.column_id
and c.object_id = ic.object_id
where i.object_id = #objid
and i.index_id = #indid
order by ic.is_included_column, ic.index_column_id;
open jbo_crs_ind_cols;
fetch jbo_crs_ind_cols
into #thiskey, #is_include_column
if ##fetch_status < 0
begin
deallocate jbo_crs_ind_cols;
continue;
end;
while ##fetch_status >= 0
begin
if(#is_include_column = 0)
select #keys = #keys + iif(len(#keys) > 0, ', ', '') + #thiskey
else
select #includes = #includes + iif(len(#includes) > 0, ', ', '') + #thiskey
fetch jbo_crs_ind_cols
into #thiskey, #is_include_column
end
deallocate jbo_crs_ind_cols;
select #groupname = null;
if serverproperty('EngineEdition') != 5
select #groupname = name
from sys.data_spaces
where data_space_id = #groupid;
select #memory_optimized = is_memory_optimized
from sys.tables
where object_id = #objid;
-- INSERT ROW FOR INDEX
insert into #spindtab
values
(
#indname
,#indid
,#ignore_dup_key
,#is_unique
,#is_hypothetical
,#is_primary_key
,#is_unique_key
,#is_columnstore
,#auto_created
,#no_recompute
,#memory_optimized
,#hash_index
,#groupname
,#keys
,#includes
);
-- Next index
fetch ms_crs_ind into
#indid
,#groupid
,#indname
,#ignore_dup_key
,#is_unique
,#is_hypothetical
,#is_primary_key
,#is_unique_key
,#is_columnstore
,#auto_created
,#no_recompute
,#hash_index;
end;
deallocate ms_crs_ind;
-- DISPLAY THE RESULTS
select
'index_name' = index_name
,'index_description' = convert(varchar(210), --bits 16 off, 1, 2, 16777216 on, located on group
case
when index_id = 1 then 'clustered'
else 'nonclustered'
end +
case
when hash_index = 1 then ' hash'
else ''
end +
case
when ignore_dup_key <> 0 then ', ignore duplicate keys'
else ''
end +
case
when is_unique <> 0 then ', unique'
else ''
end +
case
when is_hypothetical <> 0 then ', hypothetical'
else ''
end +
case
when is_primary_key <> 0 then ', primary key'
else ''
end +
case
when is_unique_key <> 0 then ', unique key'
else ''
end +
case
when is_columnstore <> 0 then ', columnstore'
else ''
end +
case
when auto_created <> 0 then ', auto create'
else ''
end +
case
when no_recompute <> 0 then ', stats no recompute'
else ''
end +
case
when memory_optimized = 1 then ' located in MEMORY '
else ''
end +
case
when groupname is not null
and (memory_optimized = 0
or memory_optimized is null) then ' located on ' + groupname
else ''
end)
,'index_keys' = index_keys
,'include_cols' = include_cols
from #spindtab
order by
index_name;
return (0) -- sp_helpindex2
end
execute sp_ms_marksystemobject 'dbo.sp_helpindex2';

Related

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;

Count all rows from all tables in two databases on different servers

I would like my query to return the table name, and rowcount for all of the tables on our two reporting servers. They both have the same tables. Also, I already added the linked server the other day between these two.
Query so far for one server, not sure how to add a third column connected with our other server though:
SELECT
t.NAME AS TableName,
p.[Rows]
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name, p.[Rows]
ORDER BY
object_name(i.object_id)
Desired output:
TableName DB1_rows DB2_Rows
----------+-----------+-----------
Account | 20,000 | 19,388
Contacts | 1,234 | 1,390
Bla | 2,330 | 2,430
This would be a great use for Common Table Expressions (CTE's) as you can run multiple queries, then join those query results together and analyze/manipulate them in different ways:
/* Use the WITH keyword to start your first expression */
WITH SERVER_A AS (
SELECT
t.NAME AS TableName,
p.[Rows] AS NumRows
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name, p.[Rows]
),
/* Then chain additional expressions (this time adding the linked server into the table name) */
SERVER_B AS (
SELECT
t.NAME AS TableName,
p.[Rows] AS NumRows
FROM
LINKED_SERVER_NAME.sys.tables t
INNER JOIN
LINKED_SERVER_NAME.sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
LINKED_SERVER_NAME.sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
LINKED_SERVER_NAME.sys.allocation_units a ON p.partition_id = a.container_id
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name, p.[Rows]
)
/* Then join the two together on a common column */
SELECT
A.TableName,
A.NumRows AS DB1_Rows,
B.NumRows AS DB2_Rows
FROM SERVER_A A
LEFT JOIN SERVER_B B ON
A.TableName = B.TableName
ORDER BY
A.TableName ASC
You could also accomplish this with APPLY statements or correlated sub-queries, but the advantage to using a CTE is that you're not running the sub-query for every single row that the parent query returns. Using a CTE you can run a query and then simply treat that query result as if it were a another table.
Obviously you'll want to test this. I don't have access to a SQL Server at the moment, so there may be a typo here or there.
DECLARE #RESULT TABLE (TableName VARCHAR(MAX), DB1_rows INT, DB2_Rows INT)
DECLARE #TABLENAME VARCHAR(MAX), #SQL VARCHAR(MAX)
DECLARE cCursor CURSOR FOR
SELECT name FROM sys.tables
OPEN cCursor
FETCH NEXT FROM cCursor INTO #TABLENAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'SELECT ''' + #TABLENAME + ''' , COUNT(*) FROM ' + #TABLENAME
DECLARE #FirstColumn VARCHAR(MAX) = (SELECT TOP 1 c.name FROM sys.columns c JOIN sys.tables t ON t.object_id = c.object_id WHERE t.name = #TABLENAME ORDER BY column_id)
SET #SQL = 'SELECT ''' + #TABLENAME + ''' , SUM(CASE WHEN A.' + #FirstColumn + ' IS NOT NULL THEN 1 ELSE 0 END), SUM(CASE WHEN B.' + #FirstColumn + ' IS NOT NULL THEN 1 ELSE 0 END) '
+'FROM LIVE.dbo.' + #TABLENAME + ' AS A FULL JOIN TEST.dbo.' + #TABLENAME + ' AS B on 1=0'
INSERT INTO #RESULT EXEC (#SQL)
FETCH NEXT FROM cCursor INTO #TABLENAME
END
CLOSE cCURSOR
DEALLOCATE cCURSOR
SELECT * FROM #RESULT
Just change the LIVE and TEST and the 'dbo' schema name on the second line of the 'SET #SQL' statement to the names of the 2 databases.
EDIT: Also you can add one of the database names.schema names to the 'SELECT name FROM sys.tables' statement at the top, plus any table name filtering you wanted to do.
If you are able to get the result from one server, then you can get the same from other server if the linked server is setup.
You can do it by using the 4 part name. For example:
Select ...
From ServerName.DBName.schema.TableName
...

Generate **bare** CREATE TABLE and CREATE PROC statemnts in SQL Server

The deployment procedures in my company require that SQL scripts for creation of procedures and tables, etc. be bare.
Generating create scripts in SQL Server Management Studio gives you significantly more than a bare script, which is unfortunate in this case. For example, for a test table this is what SSMS generated for me:
USE [DemoDB]
GO
/****** Object: Table [dbo].[example] Script Date: 08/07/2012 15:46:32 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[example](
[fake_row] [int] NULL
) ON [PRIMARY]
GO
So, what I'd like to know is:
Can I somehow run a query on SQL Server in SSMS that would give me a script that was literally just the following?
create table example (
fake_row int
)
You can create your own "create table" statements, using information_schema (Aaron . . . I know, INFORMATION_SCHEMA ;).
I have written some code for this purpose, but it doesn't use INFORMATION_SCHEMA. I know, if uses older system tables that are probably scheduled to be removed. It also adds in some additional information (which you can remove, since you don't need it). Just put the list of tables in the #INCLUSIONLIST and run this in the database where the tables reside.
SET #INCLUSIONLIST = '|table1|table2|';
SELECT (CASE WHEN rownum = 1 THEN 'CREATE TABLE ['+a.t_name+'] ('
WHEN c.column_id IS NOT NULL
THEN ' ['+c.c_name+'] ' +
(CASE WHEN CHARINDEX('CHAR', datatype) > 0 THEN datatype+'('+(case when length < 0 then 'max' else cast(length as varchar) end)+')'
WHEN CHARINDEX('BINARY', datatype) > 0 THEN datatype+'('+cast(length as varchar)+')'
WHEN datatype = 'float' AND precision <> 24 THEN datatype+'('+cast(precision as varchar)+')'
WHEN datatype IN ('numeric', 'decimal') AND scale = 0 THEN datatype+'('+cast(precision as varchar)+')'
WHEN datatype IN ('numeric', 'decimal') AND scale > 0 THEN datatype+'('+cast(precision as varchar)+','+cast(scale as varchar)+')'
ELSE datatype END)+' '+
(CASE WHEN c.identity_seed IS NOT NULL
THEN 'IDENTITY(' + CAST(identity_seed AS VARCHAR) + ',' + CAST(identity_increment AS VARCHAR) + ') '
ELSE '' END) +
(CASE WHEN c.is_nullable = 0 THEN 'NOT NULL ' ELSE '' END) +
(CASE WHEN c.default_definition IS NOT NULL
THEN 'DEFAULT '+default_definition ELSE '' END) +
(CASE WHEN max_column_id = column_id AND pk.pk_name IS NULL THEN '' ELSE ',' END)
WHEN rownum = max_column_id + 2 and pk.pk_name IS NOT NULL
THEN ' PRIMARY KEY ('+pk.pk_columns+')'
WHEN rownum = max_column_id + 3 THEN ') /* CREATE TABLE '+a.t_name+' */'
WHEN rownum = max_column_id + 4 THEN 'GO'
WHEN rownum = max_column_id + 5 THEN ''
END)
FROM (SELECT t.t_name, rownum, max_column_id
FROM (SELECT t.name as t_name, MAX(c.column_id) as max_column_id
FROM sys.columns c join
(SELECT * FROM sys.tables WHERE CHARINDEX('|'+name+'|', #INCLUSIONLIST) > 0 ) t
ON c.object_id = t.object_id
GROUP BY t.name) t join
(SELECT ROW_NUMBER() OVER (ORDER BY object_id) as rownum FROM sys.columns c) ctr
ON ctr.rownum <= t.max_column_id + 5
) a LEFT OUTER JOIN
(SELECT t.name as t_name, c.column_id, c.name AS c_name, u.name as datatype,
ISNULL(baset.name, N'') AS systemtype,
CAST(CASE WHEN baset.name IN (N'nchar', N'nvarchar') AND c.max_length <> -1
THEN c.max_length/2 ELSE c.max_length END AS INT) AS length,
c.precision AS precision,
c.scale as scale,
c.is_nullable,
dc.definition as default_definition,
idc.seed_value as identity_seed, idc.increment_value as identity_increment
FROM sys.tables t JOIN
sys.all_columns AS c
ON c.object_id = t.object_id LEFT OUTER JOIN
sys.types u
ON u.user_type_id = c.user_type_id LEFT OUTER JOIN
sys.types baset
ON baset.user_type_id = c.system_type_id AND
baset.user_type_id = baset.system_type_id LEFT OUTER JOIN
sys.default_constraints dc
ON c.object_id = dc.parent_object_id AND
c.column_id = dc.parent_column_id LEFT OUTER JOIN
sys.identity_columns idc
ON c.object_id = idc.object_id AND
c.column_id = idc.column_id
) c
ON a.t_name = c.t_name AND
c.column_id + 1 = a.rownum LEFT OUTER JOIN
(SELECT t.name as t_name, kc.name as pk_name,
(MAX(CASE WHEN index_column_id = 1 THEN '['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 2 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 3 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 4 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 5 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 6 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 7 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 8 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 9 THEN ','+'['+c.name+']' ELSE '' END) +
MAX(CASE WHEN index_column_id = 10 THEN ','+'['+c.name+']' ELSE '' END)
) as pk_columns
FROM sys.indexes i JOIN
sys.key_constraints kc
ON i.name = kc.name AND
kc.type = 'PK' JOIN
sys.tables t
ON i.object_id = t.object_id JOIN
sys.index_columns ic
ON i.object_id = ic.object_id AND
i.index_id = ic.index_id JOIN
sys.columns c
ON ic.index_column_id = c.column_id AND
ic.object_id = c.object_id
GROUP BY t.name, kc.name
) pk
ON pk.t_name = a.t_name
ORDER BY a.t_name, rownum
Apologies for the self-answer, marking other good answers +1.
It turns out you can get pretty much all of the way there with utility in SSMS.
Right click on the database in the object explorer
Click "Tasks"
Click "Generate Scripts"
Select "Script Specific Objects" and choose tables, or whatever else
Select "Save To File" & "Single File Per Object" (or just spool to query window)
Click "Advanced" and you can enable/disable the generation of virtually every part of the statements you're generating including constraints, use statements, etc.
If further removal is required (e.g. removing GO), it's minimal
Optionally, you can control the general scripting behavior of SSMS like so: Options for scripting SQL Server database objects.
I would create stored procs that read from the INFORMATION_SCHEMA views.
Getting the stored proc text is just:
DECLARE #SQL VARCHAR(8000)
SET #SQL=' '
select #SQL = #SQL + ROUTINE_DEFINITION
from INFORMATION_SCHEMA.ROUTINES
where ROUTINE_TYPE='PROCEDURE'
AND SPECIFIC_NAME = 'updateComments'
PRINT #SQL
The one to script the table would looks something like:
DECLARE #tableName VARCHAR(50)
SET #tableName = 'Location'
-- Need to know how many columns
DECLARE #NumColumns int
SELECT #NumColumns = MAX(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tableName
DECLARE #SQL VARCHAR(8000)
SET #SQL=' '
DECLARE #Results TABLE
(LineNumber int,
Line VARCHAR(1000))
INSERT INTO #Results
SELECT 0 AS ORDINAL_POSITION,
'CREATE TABLE '+TABLE_SCHEMA+'.'+#tableName+' (' AS Line
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #tableName
UNION ALL
SELECT ORDINAL_POSITION,
'['+COLUMN_NAME+'] '+ DATA_TYPE +
CASE WHEN DATA_TYPE = 'varchar' OR DATA_TYPE = 'char'
THEN '('+ CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) +')'
ELSE '' END +
CASE WHEN IS_NULLABLE = 'NO'
THEN ' NULL '
ELSE ' NOT NULL' END +
CASE WHEN ORDINAL_POSITION < #NumColumns
THEN ', '
ELSE ' ' END
AS LINE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tableName
UNION ALL
SELECT 999, ' )'
SELECT Line
FROM #Results
ORDER BY LineNumber
SELECT #SQL = #SQL + Line
FROM #Results
ORDER BY LineNumber
SELECT #SQL
PRINT #SQL
This creates the simple table script that you want, but it is not complete - doesn't do numeric precision, primary key, etc. But this is enough to get you started.
I would just make each one of these into a stored proc that takes the object name as a parameter.

How to retrieve all indexes in a database along with their data types in SQL Server 2005?

I have a maintenance task that's failing because there are a few indexes on the database that have a type which does not allow online rebuilding of the index. Offline is not an option in my industry, so I have to create my own T-SQL task that does the index rebuilding on specific indexes. The database is large with many tables and indexes, so is there a way to query the system for all indexes and their data types?
This will show you all the columns for all indices, including the columns of 'base tables' like heaps and clustered index and all INCLUDE columns as well:
select o.name as [object_name],
ix.name as [index_name],
coalesce(c.name,cc.name) as [column_name],
t.name as [type_name],
coalesce(c.max_length, cc.max_length) as max_length
from sys.indexes ix
join sys.objects o
on ix.object_id = o.object_id
left join sys.index_columns ic
on ix.object_id = ic.object_id
and ix.index_id = ix.index_id
and ix.index_id > 1
left join sys.columns c
on ic.object_id = c.object_id
and ic.column_id = c.column_id
left join sys.columns cc
on ix.object_id = cc.object_id
and ix.index_id in (1,0)
join sys.types t
on t.system_type_id = coalesce(c.system_type_id,cc.system_type_id)
where o.type = 'U'
order by object_name, index_name, column_name;
You can then identify the ones unsafe for online index rebuild (the BLOB/XML/CLR types). Note that in SQL server 2012 the restriction on online index build with LOB columns was lifted, see Online Index Operations for indexes containing LOB columns.
we've made recently such procedure. it has "HasBlobs" flag for this purpose
ALTER PROCEDURE sp_rebuild_local_idexes
#RebuildClustered bit = 0
AS
BEGIN
DECLARE #objectid int
DECLARE #indexid int
DECLARE #schemaname nvarchar(130)
DECLARE #objectname nvarchar(130)
DECLARE #indexname nvarchar(130)
DECLARE #partitions bigint
DECLARE #frag float
DECLARE #command nvarchar(4000)
DECLARE #HasBlobs bit
DECLARE #index_type_desc nvarchar(255)
-- Conditionally select tables and indexes from the sys.dm_db_index_physical_stats function
-- and convert object and index IDs to names.
SELECT
--object_name(object_id),
object_id AS objectid,
index_id AS indexid,
avg_fragmentation_in_percent AS frag,
CASE WHEN (
SELECT st.object_id
from sys.tables st
inner join sys.columns sc
on st.object_id=sc.object_id
inner join sys.types styp
on sc.system_type_id=styp.system_type_id and sc.max_length=styp.max_length
inner join sys.schemas ss
on st.schema_id=ss.schema_id
where styp.schema_id=4
and styp.name<>'sysname'
and styp.name IN ('xml','nvarchar','varchar','image','text','ntext')
AND st.object_id = a.object_id
group by st.object_id
) IS NULL THEN 0 ELSE 1 END AS HasBlobs,
a.index_type_desc
INTO #work_to_do
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'LIMITED') AS a
WHERE avg_fragmentation_in_percent >= 5.0 AND index_id > 0
AND a.index_type_desc IN ('CLUSTERED INDEX','NONCLUSTERED INDEX')
ORDER BY a.index_type_desc
-- Declare the cursor for the list of partitions to be processed.
DECLARE partitions CURSOR FOR SELECT objectid,
indexid,
frag,
HasBlobs,
index_type_desc from #work_to_do
select * from #work_to_do
-- Open the cursor.
OPEN partitions
-- Loop through the partitions.
WHILE (1=1)
BEGIN
FETCH NEXT FROM partitions
INTO #objectid, #indexid, #frag,#HasBlobs,#index_type_desc
IF ##FETCH_STATUS < 0 BREAK
IF #RebuildClustered = 1 AND #index_type_desc != 'CLUSTERED INDEX'
CONTINUE;
SELECT #objectname = QUOTENAME(o.name), #schemaname = QUOTENAME(s.name)
FROM sys.objects AS o
JOIN sys.schemas as s ON s.schema_id = o.schema_id
WHERE o.object_id = #objectid
SELECT #indexname = QUOTENAME(name)
FROM sys.indexes
WHERE object_id = #objectid AND index_id = #indexid and type!=3
-- 30 is an arbitrary decision point at which to switch between reorganizing and rebuilding.
IF #frag < 30.0
SET #command = N'ALTER INDEX ' + #indexname + N' ON ' + #schemaname + N'.' + #objectname + N' REORGANIZE'
IF #frag >= 30.0
BEGIN
print #indexname+ #schemaname+#objectname
SET #command = N'ALTER INDEX ' + #indexname +
N' ON ' +
#schemaname +
N'.' + #objectname +
N' REBUILD' +
CASE WHEN #HasBlobs = 0 THEN ' WITH(ONLINE=ON)' ELSE '' END
print #command
END
EXEC (#command)
PRINT N'Executed: ' + #command
END
-- Close and deallocate the cursor.
CLOSE partitions
DEALLOCATE partitions
END

Listing all indexes

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`

Resources