Index rebuild on sql server - sql

I am doing the index rebuilding on database.
I need to verify if it is done or not.
Can somebody please guide me.
I am using SQL Server 2008 R2

If you are looking for details on all indexes and tables in your database you can use.
SELECT OBJECT_NAME(object_id),*
FROM sys.dm_db_index_physical_stats(DB_ID(),NULL,NULL,NULL,'SAMPLED')
It just occurred to me that you might also be asking how to know the progress of the reindexing. For this you can use
SELECT percent_complete
from sys.dm_exec_requests
where session_id= <spid of interest>

A key thing would be to run the "Index Physical Statistics" report and "Disk Usage by Top Tables" reports before and after you rebuild the indexes.
On the "Index Physical Statistics" report, you can see how fragmented each index is.
To see these reports...
* Right Click on database in Sql Server Management Studio
* Mouse over "Reports", then "Standard Reports", then select the report you want.
For a script you can set up to identify fragmented indexes and rebuild them (and for more info), check this out:
http://www.foliotek.com/devblog/sql-server-optimization-with-index-rebuilding/

If you have successfully re-indexed your tables, then the index fragmentation will be zero (or close to if you have hot tables). You can use this script to check the fragmentation level
DECLARE
#IndexID int,
#TableID int,
#IndexName varchar(256)
--Enter index name here
SELECT #IndexName = '<index name>'
--Enter table name here
SET #TableID = OBJECT_ID('<table name>')
SELECT #IndexID = IndID
FROM sysindexes
WHERE
id = #TableID
AND name = #IndexName
DBCC SHOWCONTIG (#id, #IndexID)
What you are looking for in the output is the property called Scan Density. This should be close to 100%. If not, then your re-indexing is not complete/successful
If you have lots of tables/indices, this can get tedious, so short-circuit it by auto-generating the script like this:
SELECT 'DBCC SHOWCONTIG ' +
'('
+ CONVERT(varchar(32), si.id) + ','
+ CONVERT(varchar(32), si.indid) +
')--' + so.name
FROM sysobjects so
INNER JOIN sysindexes si
ON (so.id = si.id)
WHERE (
so.type = 'U' AND
si.indid < 2 AND
si.id = object_id(so.name)
)

You can try this procedure. It will rebuild the index of all the tables in the database and print the result as it progresses to the message pane of your Management Studio:
CREATE PROCEDURE [dbo].[ReIndexDatabase]
AS
DECLARE #MyTable VARCHAR(255)
DECLARE myCursor
CURSOR FOR
SELECT table_name
FROM information_schema.tables
WHERE table_type = 'base table'
OPEN myCursor
FETCH NEXT
FROM myCursor INTO #MyTable
WHILE ##FETCH_STATUS = 0 BEGIN
PRINT 'Reindexing Table: ' + #MyTable
EXEC('ALTER INDEX ALL ON '+#MyTable+'
REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = OFF,
STATISTICS_NORECOMPUTE = ON)');
FETCH NEXT FROM myCursor INTO #MyTable
END
CLOSE myCursor
DEALLOCATE myCursor
EXEC sp_updatestats
You can see this link for more on re-indexing or this link.
Note the information at the top of the page.

Related

Is there a way add auto increment in all tables from a specific database at once?

I am trying to add auto increment in all existing tables in a specific database, and I can do that going through the table design, flagging the identity option, but in this case I have to do it table per table, and there is a lot of tables. Is there a way to do that automatically?
Copied from my comments per request:
I don't believe you're going to find an automated option for doing this for multiple tables. The change script that SSMS creates when you do this in table designer is already doing a ton of work you'd have to recreate for any other solution. Frankly, I wouldn't trust myself to do it as correctly as SSMS.
However, if it were a large enough number of tables, I would create a completely new database with the corrected schema. Ensure that everything in the new database is present and correct. Then, set identity insert to on all tables in the new db, copy the data over, set all the identity inserts off, and then move the new db to the old db with DETACH/ATTACH or BACKUP/RESTORE. In other words, I'd literally rebuild the database from the ground up because old schema had been completely trashed. It would take a lot for me to decide to do that in a production system, however.
I'd only do the DETACH/ATTACH or BACKUP/RESTORE if I absolutely needed to change the database file names or database names. I'd actually prefer to just use the new database as a new database for the application. That would also mean I could swap back to the old database pretty quickly if I ran into trouble.
It can be done by using a 'cursor', but you need to have all the columns that you need to add auto increment to in the same name as ID
Declare #Table nvarchar(50), #script nvarchar(100)
DECLARE cur CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR
SELECT TABLE_SCHEMA + '.' + TABLE_NAME as 'Table' FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME not in ('sysdiagrams') -- You can exclude any table from this process by adding it on the where statement
OPEN cur
FETCH NEXT FROM cur INTO #Table
WHILE ##FETCH_STATUS = 0 BEGIN
-- The sql command to alter a Table and add Identity to it, you can change ID by any column in your tables
set #script = 'Alter Table '+ #Table +' ADD ID INT IDENTITY'
EXEC sp_executesql #script
FETCH NEXT FROM cur INTO #Table
END
CLOSE cur
DEALLOCATE cur
Edit 1 : According to what you asked for in the comment
Declare #Table nvarchar(50), #script nvarchar(100), #primarykey_name nvarchar(20)
DECLARE cur CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR
SELECT TABLE_SCHEMA + '.' + TABLE_NAME as 'Table' FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME not in ('sysdiagrams') -- You can exclude any table from this process by adding it here
OPEN cur
FETCH NEXT FROM cur INTO #Table
WHILE ##FETCH_STATUS = 0 BEGIN
-- Find Primary key for the current Table and set it to #primarykey_name
Set #primarykey_name = (SELECT c.NAME FROM sys.key_constraints kc INNER JOIN sys.index_columns ic ON kc.parent_object_id = ic.object_id and kc.unique_index_id = ic.index_id
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
WHERE kc.name='PK_'+ substring(#Table, 5, LEN(#Table)-4) and kc.type = 'PK')
-- The sql command to alter a Table and add Identity to the primarykey of each table
set #script = 'Alter Table '+ #Table +' ADD ' + #primarykey_name + ' INT IDENTITY'
print #script
--EXEC sp_executesql #script
FETCH NEXT FROM cur INTO #Table
END
CLOSE cur
DEALLOCATE cur

Query to select all databases then run another query on the result

*******EDITED*****
I have multiple sql servers with around 200-300 dbs in them and want to save space on my server.
I would like to run a shrink routine on all databases in my sql server by running this script through a task scheduler. I have the queries but I do not know how to connect the two together.
For selecting all databases I use this
select * from sys.sysdatabases
Where name <> 'master' and name <> 'tempdb' and name <> 'model' and name <> 'msdb'
For my shrink routeen I use this
USE [single_database_name]
GO
DBCC SHRINKFILE ('single_database_name', 10)
GO
How can I connect the two queries so that the "single_database_name" is coming from the list of all database names from the first query.
Thanks for your help
Use a cursor with dynamic SQL. This will shrink each file individually on each database.
Declare #dataFiles Table (databaseName Varchar(256), datafile Varchar(256))
Declare #SQL Nvarchar(Max), #databaseName Varchar(256), #dbfile Varchar(256)
Insert #dataFiles
select sd.name, smf.name
from sys.sysdatabases sd
join sys.master_files smf
On sd.[dbid] = smf.database_id
Where sd.name not in ('master','tempdb','model','msdb')
Declare cur Cursor For
Select databaseName,
datafile
From #dataFiles
Open cur
Fetch Next
From cur
Into #databaseName,
#dbfile
While ##Fetch_Status = 0
Begin
Set #SQL = 'USE [' + #databasename + ']
DBCC SHRINKFILE (''' + #dbfile + ''', 10) WITH NO_INFOMSGS'
Exec sp_executeSQL #SQL
Fetch Next
From cur
Into #databaseName,
#dbfile
End
Close cur
Deallocate cur
select sd.name As DatabaseName, smf.name DBFileName, (size*8)/1024 SizeMB
from sys.sysdatabases sd
join sys.master_files smf
On sd.[dbid] = smf.database_id
Where sd.name not in ('master','tempdb','model','msdb')
select
'USE '+ quotename([name]) + '
GO
DBCC SHRINKFILE (''' + [name] + ''', 10)
GO
'
from sys.databases
where name not in ('master', 'tempdb', 'model', 'msdb')
But please don't do this: shrinking is bad.
Using undocumented stored procedure sp_MSForEachDB and DBCC SHRINKDATABASE command you can shrink all databases on an SQL Server in one line of code
EXEC sp_MSForEachDB
'if ''?'' not in (select name from sys.databases
where name <> ''master'' and name <> ''tempdb'' and name <> ''model'' and name <> ''msdb'')
DBCC SHRINKDATABASE([?], 10)'
More info about sp_MSForEachDB
Shrinking a database should be avoided except for in very specific situations, like where you delete a large amount of data and the database is never going to grow back to the original size and reclaim the space. Shrinking the database will fragment the data and can cause performance issues.
for operating on a particular database, you will be have atleast knowledge of the name of the databse, if it is the case then use the following query
if (exists(Select name from sys.databases where name like 'single_database_name'))
begin
DBCC SHRINKFILE ('single_database_name', 10)
end

Set IDENTITY_INSERT OFF for all tables

I have a script which creates an entire database and inserts all records to a few dozen tables.
It works great, unless there is some issue during the processing, and a table gets left with IDENTITY_INSERT ON, when the script fails during insertion and before it can be set to OFF again.
When this happens, the script automatically fails when attempting to run it again, with the error "IDENTITY_INSERT is already ON for table xx" as we go into the insertion for the first table.
As a failsafe I would like to make sure that IDENTITY_INSERT is set to OFF for all tables, before running the rest of the processing in the setup script.
As an alternative, we could perhaps close the MS SQL connection and open it again, which, as I understand it, would clear all IDENTITY_INSERT values for the connection session.
What's the best way to do this, and prevent the "already on" errors?
Dynamic SQL:
select 'set identity_insert ['+s.name+'].['+o.name+'] off'
from sys.objects o
inner join sys.schemas s on s.schema_id=o.schema_id
where o.[type]='U'
and exists(select 1 from sys.columns where object_id=o.object_id and is_identity=1)
Then copy & paste the resulting SQL into another query window and run
EXEC sp_MSforeachtable #command1="SET IDENTITY_INSERT ? OFF"
EXEC sp_MSforeachtable #command1="PRINT '?'; SET IDENTITY_INSERT ? OFF",
#whereand = ' AND EXISTS (SELECT 1 FROM sys.columns WHERE object_id = o.id AND is_identity = 1)'
Building on Lynn's answer, in case you're too lazy to perform this in more than one step - this should run on all tables where there is an identity column.
Caveat is only tested in 2012 and sp_MSforeachtable is of course entirely unsupported...
Building on #KevD's answer - It was working fine for disabling but here is more for enabling as well.
To disable all identity inserts where they need to be disabled, use -
EXEC sp_MSforeachtable #command1="PRINT '?'; SET IDENTITY_INSERT ? OFF",
#whereand = ' AND EXISTS (SELECT 1 FROM sys.columns WHERE object_id = o.id
AND is_identity = 1) and o.type = ''U'''
To enable all identity inserts where they need to be enabled, use -
EXEC sp_MSforeachtable #command1="PRINT '?'; SET IDENTITY_INSERT ? ON",
#whereand = ' AND EXISTS (SELECT 1 FROM sys.columns WHERE object_id = o.id
AND is_identity = 1) and o.type = ''U'''
Tested on Sql server 2014 and 2016
I had a similar issue but I'd rather not use undocumented stored procedures in production. To automate this I built on to #John Dewey's answer and put it into a cursor. This iterates over 699 tables in 407 ms.
DECLARE #sql NVARCHAR(500) -- SQL command to execute
DECLARE sql_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT 'SET identity_insert ['+s.name+'].['+o.name+'] OFF'
FROM sys.objects o
INNER JOIN sys.schemas s on s.schema_id=o.schema_id
WHERE o.[type]='U'
AND EXISTS(SELECT 1 FROM sys.columns WHERE object_id=o.object_id AND is_identity=1)
OPEN sql_cursor
FETCH NEXT FROM sql_cursor INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
EXECUTE sp_executesql #sql --> Comment this out to test
-- PRINT #sql --> Uncomment to test or if logging is desired
FETCH NEXT FROM sql_cursor INTO #sql
END
CLOSE sql_cursor
DEALLOCATE sql_cursor
If you are against cursors, it could also be easily transformed into a while loop.

How to ALTER INDEX for every Table in my DataBase

I use SQL Server 2008 R2, I need to rebuild the index for every table in a database
Using this script, I receive an error
USE myDb
GO
EXEC sp_MSForEachTable 'ALTER INDEX ALL ON ? REBUILD'
Error:
ALTER INDEX failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are
correct for use with indexed views and/or indexes on computed columns
and/or filtered indexes and/or query notifications and/or XML data
type methods and/or spatial index operations.
Any idea how to fix it? thanks
SQL Fool (Michelle Ufford) has a great script to do this for you - all done and well tested by many users.
It's a great piece of work - it allows you to define fragmentation levels for which you
do nothing
reorganize an index
rebuild an index
Don't reinvent the wheel - just go see and use the script!
A simple approch I decide to use for my questions. Code from: http://blog.sqlauthority.com/2009/01/30/sql-server-2008-2005-rebuild-every-index-of-all-tables-of-database-rebuild-index-with-fillfactor/
-- Rebuild eve Index for every Table in the Database.
-- Resource: http://blog.sqlauthority.com/2009/01/30/sql-server-2008-2005-rebuild-every-index-of-all-tables-of-database-rebuild-index-with-fillfactor/
USE [YourDbName]
GO
-- Show Fragmentation sample on YourTable Index.
select avg_fragmentation_in_percent, avg_fragment_size_in_pages, fragment_count, avg_page_space_used_in_percent
from sys.dm_db_index_physical_stats (DB_ID(), object_id('[dbo].[YourTableName]'), NULL, NULL, 'DETAILED')
-- Cursor going over each table and rebuilding every index of database.
DECLARE #TableName VARCHAR(255)
DECLARE #sql NVARCHAR(500)
DECLARE #fillfactor INT
SET #fillfactor = 80
DECLARE TableCursor CURSOR FOR
SELECT OBJECT_SCHEMA_NAME([object_id])+'.'+name AS TableName
FROM sys.tables
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'ALTER INDEX ALL ON ' + #TableName + ' REBUILD WITH (FILLFACTOR = ' + CONVERT(VARCHAR(3),#fillfactor) + ')'
EXEC (#sql)
FETCH NEXT FROM TableCursor INTO #TableName
END
CLOSE TableCursor
DEALLOCATE TableCursor
GO

Best Approach for Reindexing

I am trying to reduce fragmentation in all of the indexes for a database running on SQL Server 2005.
Currently I am trying to use ALTER INDEX in conjunction with sp_MSforeachtable, to apply it to all of the indexes for all of the tables:
sp_MSforeachtable "ALTER INDEX ALL ON ? REBUILD;"
But for some reason this doesn’t always seem to work?
If I try it for a single index, or all of the indexes for a single table then the fragmentation is cleaned up, it just seems to be when I apply it to the whole database that I get problems.
Previously I might have used DBCC DBREINDEX but BOL states it will be removed in the next version of SQL Server, so I don’t want to use it.
Can anyone give me any advice on the best way to tackle cleaning up all of the indexes in a database?
Thanks
If you want to fully automate your SQL Server Index maintenance then I seriously recommend that you check out Michelle Ufford's stored procedure for this.
Index Defrag Script V4.1
It is what I consider to be the best index maintenance script I have ever read.
One of the best features about this script are that you can customize the threshold values that you use in order to determine whether or not to REBUILD or REORGANIZE a given index strucutre.
It also provides the option to limit the number of CPU cores that are utilized by the procedure. An excellent option if you intend to run the script on a busy live production database.
Warning: As with all internet available code, be sure you test it thoroughly before using in a production environment. You will also most likely want to incorporate your own customisation and features too.
Check out the article and accompanying sample script to handle this task at SQL Fool (Michelle Ufford's website):
http://sqlfool.com/2009/06/index-defrag-script-v30/
This is quite a nice solution to handle this once and for all!
The best practice recommendation is to reorganize your index if you have 5-30% of fragmentation, and only rebuild it if it has more than 30% fragmentation. You can easily use these thresholds or specify your own using this script.
Marc
Or you can use Microsoft's index rebuilding script found here http://msdn.microsoft.com/en-us/library/ms188917.aspx
-- Ensure a USE <databasename> statement has been executed first.
SET NOCOUNT ON;
DECLARE #objectid int;
DECLARE #indexid int;
DECLARE #partitioncount bigint;
DECLARE #schemaname nvarchar(130);
DECLARE #objectname nvarchar(130);
DECLARE #indexname nvarchar(130);
DECLARE #partitionnum bigint;
DECLARE #partitions bigint;
DECLARE #frag float;
DECLARE #command nvarchar(4000);
-- Conditionally select tables and indexes from the sys.dm_db_index_physical_stats function
-- and convert object and index IDs to names.
SELECT
object_id AS objectid,
index_id AS indexid,
partition_number AS partitionnum,
avg_fragmentation_in_percent AS frag
INTO #work_to_do
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'LIMITED')
WHERE avg_fragmentation_in_percent > 10.0 AND index_id > 0;
-- Declare the cursor for the list of partitions to be processed.
DECLARE partitions CURSOR FOR 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, #partitionnum, #frag;
IF ##FETCH_STATUS < 0 BREAK;
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;
SELECT #partitioncount = count (*)
FROM sys.partitions
WHERE object_id = #objectid AND index_id = #indexid;
-- 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
SET #command = N'ALTER INDEX ' + #indexname + N' ON ' + #schemaname + N'.' + #objectname + N' REBUILD';
IF #partitioncount > 1
SET #command = #command + N' PARTITION=' + CAST(#partitionnum AS nvarchar(10));
EXEC (#command);
PRINT N'Executed: ' + #command;
END;
-- Close and deallocate the cursor.
CLOSE partitions;
DEALLOCATE partitions;
-- Drop the temporary table.
DROP TABLE #work_to_do;
GO
I use this script together with SQL Server Agent to automate the task. Hope this helps.
The safest and most portable way is to drop the indices and to re-add them.