Tara Kizer Blog

Tara Kizer

DBCC DBREINDEX Stored Procedure - new version #2

EDIT: fixed problems found by gam.

I have made yet another change to the DBCC DBREINDEX stored procedure. I modified it so that when a clustered index gets rebuilt, we don't rebuild the other indexes since that happens automatically for us. I made this change a little while ago, but I hadn't posted it yet. It's a good thing since I had to change it again. It turns out that only when a non-unique clustered index gets rebuilt, that the non-clustered ones get rebuilt as well. So when a unique clustered index gets rebuilt, we still need to rebuild the non-clustered ones. For more information, check this out. Hemanth Gorijala pointed this out to me in this thread.

Here's the new version:

----------------------------------------------------------------------------------------------------
-- OBJECT NAME         : isp_DBCC_DBREINDEX
--
-- AUTHOR               : Tara Duggan
-- DATE     : May 11, 2004
--
-- INPUTS    : @dbName - name of the database
-- OUTPUTS    : None
-- DEPENDENCIES         : None
--
-- DESCRIPTION         : This stored procedure runs DBCC DBREINDEX for each of the indexes in the database.
--
-- EXAMPLES (optional)  : EXEC isp_DBCC_DBREINDEX @dbName = 'GT'
--
-- MODIFICATION HISTORY :
----------------------------------------------------------------------------------------------------
-- 12/22/2004 - Tara Duggan
-- If table has a clustered index, just rebuild that index; otherwise rebuild all.
--
-- 12/30/2004 - Tara Duggan
-- If table has a non-unique clustered index, just rebuild that index; otherwise rebuild all.
----------------------------------------------------------------------------------------------------
ALTER             PROC isp_DBCC_DBREINDEX
(@dbName SYSNAME)
AS
SET NOCOUNT ON

DECLARE @objName SYSNAME
DECLARE @idxName SYSNAME
DECLARE @SQL NVARCHAR(4000)
DECLARE @ID INT
DECLARE @RowCnt INT

CREATE TABLE ##Indexes
(
 Indexes_ID INT IDENTITY(1, 1) NOT NULL,
 IndexName SYSNAME NOT NULL,
 ObjectName SYSNAME NOT NULL,
 Status INT NOT NULL
)

-- non-unique clustered indexes
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes (IndexName, ObjectName, Status) '
SET @SQL = @SQL + 'SELECT i.[name], o.[name], i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.[id] = o.[id] '
SET @SQL = @SQL + 'WHERE i.indid = 1 AND '
SET @SQL = @SQL + 'o.type = ''U'' AND (i.status & 2) = 0'

EXEC sp_executesql @statement = @SQL

-- non-clustered indexes except for those tables that have clustered non-unique indexes; these will be rebuilt automatically
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes (IndexName, ObjectName, Status) '
SET @SQL = @SQL + 'SELECT i.[name], o.[name], i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.[id] = o.[id] '
SET @SQL = @SQL + 'WHERE i.indid > 1 AND i.indid < 255 AND '
SET @SQL = @SQL + 'o.type = ''U'' AND '
SET @SQL = @SQL + '(i.status & (64 | 8388608)) <= 0 AND '
SET @SQL = @SQL + 'o.[name] NOT IN (SELECT ObjectName FROM ##Indexes)'

EXEC sp_executesql @statement = @SQL

-- unique clustered indexes
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes (IndexName, ObjectName, Status) '
SET @SQL = @SQL + 'SELECT i.[name], o.[name], i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.[id] = o.[id] '
SET @SQL = @SQL + 'WHERE i.indid = 1 AND '
SET @SQL = @SQL + 'o.type = ''U'' AND (i.status & 2) <> 0'

EXEC sp_executesql @statement = @SQL

SELECT TOP 1 @ID = Indexes_ID, @idxName = IndexName, @objName = ObjectName
FROM ##Indexes
ORDER BY Indexes_ID

SET @RowCnt = @@ROWCOUNT

WHILE @RowCnt <> 0
BEGIN

 SET @SQL = 'DBCC DBREINDEX(''' + @dbName + '.dbo.' + @objName + ''', ' + @idxName + ', 0) WITH NO_INFOMSGS'

 EXEC sp_executesql @statement = @SQL

 SELECT TOP 1 @ID = Indexes_ID, @idxName = IndexName, @objName = ObjectName
 FROM ##Indexes
 WHERE Indexes_ID > @ID
 ORDER BY Indexes_ID
 
 SET @RowCnt = @@ROWCOUNT

END

DROP TABLE ##Indexes

RETURN 0

GO

Legacy Comments


gam
2005-01-06
re: DBCC DBREINDEX Stored Procedure - new version #2
I seem to be having inconsistent results(missing indexes) when running this procedure. I created it in an admin database and I think that OBJECTPROPERTY may be the culprit. I created a new proc which gives me all the indexes that I think need rebuilding.
CREATE PROC isp_DBCC_DBREINDEX2
(@dbName SYSNAME, @Debug int = 0)
AS
SET NOCOUNT ON

DECLARE @objName SYSNAME
DECLARE @idxName SYSNAME
DECLARE @SQL NVARCHAR(4000)
--DECLARE @ID INT
DECLARE @Indid INT
DECLARE @RowCnt INT

CREATE TABLE ##Indexes
(
ObjectName SYSNAME NOT NULL,
IndexName SYSNAME NOT NULL,
Id INT NOT NULL,
Indid SMALLINT NOT NULL,
Status INT NOT NULL
)
-- get all clustered indexes
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes '
SET @SQL = @SQL + 'SELECT o.name,i.name,i.id,indid,i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.id = o.id '
SET @SQL = @SQL + 'WHERE '
SET @SQL = @SQL + 'indid =1 '
SET @SQL = @SQL + 'AND type = ''U'''

EXEC sp_executesql @statement = @SQL

IF(@Debug = 1)
SELECT * from ##Indexes
ORDER BY objectname,indid

-- get nonclustered indexes except for those tables that have clustered nonunique indexes; these will be rebuilt automatically
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes '
SET @SQL = @SQL + 'SELECT o.name,i.name,i.id,indid,i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.id = o.id '
SET @SQL = @SQL + 'WHERE '
SET @SQL = @SQL + 'indid > 1 AND indid < 255 '
SET @SQL = @SQL + 'AND type = ''U'''
SET @SQL = @SQL + 'AND (i.status & 64) = 0 '-- statistics
SET @SQL = @SQL + 'AND o.name NOT IN (SELECT objectname FROM ##indexes WHERE (status & 2) = 0) '--2 unique so return nonunique clustered

EXEC sp_executesql @statement = @SQL

IF(@Debug = 1)
SELECT * from ##Indexes
ORDER BY objectname,indid

SELECT TOP 1 @Indid = Indid, @idxName = IndexName, @objName = ObjectName
FROM ##Indexes
ORDER BY ObjectName,indid

SET @RowCnt = @@ROWCOUNT

WHILE @RowCnt <> 0
BEGIN

SET @SQL = 'DBCC DBREINDEX(''' + @dbName + '.dbo.' + @objName + ''', ' + @idxName + ', 0) WITH NO_INFOMSGS'

IF(@Debug = 1)
PRINT @SQL

EXEC sp_executesql @statement = @SQL

SELECT TOP 1 @Indid = Indid, @idxName = IndexName, @objName = ObjectName
FROM ##Indexes
WHERE (ObjectName = @objName and Indid > @Indid) or
(ObjectName > @objName)
ORDER BY ObjectName,indid

SET @RowCnt = @@ROWCOUNT

END
DROP TABLE ##Indexes

RETURN 0
GO

Tara
2005-01-06
re: DBCC DBREINDEX Stored Procedure - new version #2
I haven't seen an example where this sproc didn't work. I tested it out on a few different databases.

gam
2005-01-06
re: DBCC DBREINDEX Stored Procedure - new version #2
I get different results for a database depending on where I create the sproc. So if I create it in database A and run it for database B I process fewer indexes then if I create in B and run it for B. Even in the latter situation I appear to be bypassing some indexes. I think the intent is to process every index except those nonclustered indexes in tables that have a clustered nonunique index. Is this correct?

Tara
2005-01-06
re: DBCC DBREINDEX Stored Procedure - new version #2
No. The intent is for tables that have a non-unique clustered index, to only process that index and none of the others. This is because SQL Server automatically rebuilding the others for us, so we don't want to duplicate the work. For all other tables that do not have a non-unique clustered index, process all indexes including the clustered one.

gam
2005-01-07
re: DBCC DBREINDEX Stored Procedure - new version #2
I think we're saying the same thing. "the intent is to process every index except those nonclustered indexes in tables that have a clustered nonunique index." vs. "The intent is for tables that have a non-unique clustered index, to only process that index and none of the others." The "others" in your statement would be the nonclustered indexes in my statement; clustered nonunique in mine is the same as non-unique clustered in yours.

So I believe we're trying to do the same thing, but the results are different. I'm processing indexes with my sproc that I'm missing with yours. How can we determine what's correct? Can you try running mine and see if you get different results?

Tara
2005-01-07
re: DBCC DBREINDEX Stored Procedure - new version #2
Could you send me or post the DDL of an example table with indexes? Then I can run both versions on my machine and test it out. I didn't fully test my solution out on lots of different tables, so there could be bugs in it. I'd like to look into this further, but I'll need to see an example of where it's broken.

gam
2005-01-07
re: DBCC DBREINDEX Stored Procedure - new version #2
CREATE TABLE dbo.gam
(
c1 char(1) NOT NULL,
c2 char(1) NOT NULL,
c3 char(1) NULL
)
go
CREATE CLUSTERED INDEX clusterednonunique
ON dbo.gam(c1)
ON [PRIMARY]
go
CREATE INDEX noncluster
ON dbo.gam(c2)
ON [PRIMARY]
go

CREATE TABLE dbo.Tbl_Cntrl
(
Tbl_Cntrl_Id numeric(5,0) IDENTITY,
Cntrl_Typ_Id numeric(5,0) NULL,
Cntrl_Nm varchar(20) NOT NULL,
Cntrl_Deflt varchar(20) NULL,
CONSTRAINT PK_Tbl_Cntrl
PRIMARY KEY CLUSTERED (Tbl_Cntrl_Id)
ON [PRIMARY],
)
CREATE UNIQUE INDEX Tbl_Cntrl_idx1
ON dbo.Tbl_Cntrl(Cntrl_Nm)
ON [PRIMARY]
go
Should only get clusternonunique on first and get PK_Tbl_Cntrl and Tbl_Cntrl_idx1 on second, if I understand the issue.

Tara
2005-01-10
re: DBCC DBREINDEX Stored Procedure - new version #2
I see what you mean. I wonder why OBJECTPROPERTY isn't working here. I think mine may need to check IsPrimaryKey as well. Oh well.

So status & 2 = 0 means nonunique?

Tara
2005-01-10
re: DBCC DBREINDEX Stored Procedure - new version #2
I made my changes to the original weblog. Let me know if it looks good. When I ran it on one of my databases, I produced the same indexes to rebuild that yours did.

gam
2005-01-10
re: DBCC DBREINDEX Stored Procedure - new version #2
I was having some problems with OBJECTPROPERTY as well. It seems to return TRUE when a unique constraint is defined in a table; not when a unique index is created.

I took code from sp_helpindex. (status & 2) <> 0 means unique. Unique indexes seem to have a 2 status and unique constraints have 4098(unique(2) and unique key(4096)). The bitwise & determines if there's a hit.

gam
2005-01-11
re: DBCC DBREINDEX Stored Procedure - new version #2
Looks good.

Cord thomas
2005-01-18
re: DBCC DBREINDEX Stored Procedure - new version #2
Tara - good work.

One issue i note with this procedure is that it assumes all the tables/databases are using the default dbo schema - you need to get the sysuser from the sysobject uid and add to the list of fields in ##Indexes - otherwise, this looks great.

Add to ##Indexes

ownername SYSNAME

Add to processing of each one
u.name in the list of fields to insert and

join ' + @dbName + '.dbo.sysusers u on o.uid = u.uid '

to the query specs

Cord thomas
2005-01-18
re: DBCC DBREINDEX Stored Procedure - new version #2
When i say need, i simply mean it would then serve my purposes ;) - i have done this if you want whole code.

Terry Duffy
2005-06-21
re: DBCC DBREINDEX Stored Procedure - new version #2
Can anyone tell me where the knowledge leading to the effect of dbcc dbreindex on the clustered index has on non-clustered? I have not been able to find this from Microsoft.
RE:
"It turns out that only when a non-unique clustered index gets rebuilt, that the non-clustered ones get rebuilt as well."

Terry Duffy
2005-06-21
re: DBCC DBREINDEX Stored Procedure - new version #2
Can anyone tell me where the knowledge leading to the effect of dbcc dbreindex on the clustered index has on non-clustered? I have not been able to find this from Microsoft.
RE:
"It turns out that only when a non-unique clustered index gets rebuilt, that the non-clustered ones get rebuilt as well."

Tara
2005-06-21
re: DBCC DBREINDEX Stored Procedure - new version #2
Here you go:

http://support.microsoft.com/default.aspx?scid=kb;en-us;304519

benamis
2005-07-12
re: DBCC DBREINDEX Stored Procedure - new version #2
Hi

hi i am new to sql so i need some help

my tables are (navision style):

CRONUS aaaaa bbbb$160$0
the error:
Server: Msg 170, Level 15, State 1, Line 1
Line 1: Incorrect syntax near 'aaaaa'.

i assume that the error occurs because of the spaces in the table names, but i can't fix it :(

plz help :)

Also this procedure is the same as optimize table in mysql? or maybe you have some other proc for such tasks?

thanx

Tara
2005-07-12
re: DBCC DBREINDEX Stored Procedure - new version #2
Table names should never have spaces in them. You'll need to add brackets to this stored procedure for it to work.

And no this won't work on MySql. I've never used that dbms.

Tim S
2005-07-21
re: DBCC DBREINDEX Stored Procedure - new version #2
SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON
GO

IF OBJECT_ID('dbo.isp_DBCC_DBREINDEX') IS NULL
EXEC ('CREATE PROC dbo.isp_DBCC_DBREINDEX
AS RAISERROR ( ''Dummy Proc Called'', 16, 62)')
GO

ALTER PROC dbo.isp_DBCC_DBREINDEX
(@dbName sysname)
AS
----------------------------------------------------------------------------------------------------
-- OBJECT NAME : isp_DBCC_DBREINDEX
--
-- AUTHOR : Tara Duggan
-- DATE : May 11, 2004
--
-- INPUTS : @dbName - name of the database
-- OUTPUTS : None
-- DEPENDENCIES : None
--
-- DESCRIPTION : This stored procedure runs DBCC DBREINDEX for each of the indexes in the database.
--
-- EXAMPLES (optional) : EXEC isp_DBCC_DBREINDEX @dbName = 'GT'
--
-- MODIFICATION HISTORY :
----------------------------------------------------------------------------------------------------
-- 12/22/2004 - Tara Duggan
-- If table has a clustered index, just rebuild that index; otherwise rebuild all.
--
-- 12/30/2004 - Tara Duggan
-- If table has a non-unique clustered index, just rebuild that index; otherwise rebuild all.
--
-- 07/21/2005 - Tim S
-- Added SchemaName
----------------------------------------------------------------------------------------------------

SET NOCOUNT ON

DECLARE @objName sysname
DECLARE @objSchema sysname
DECLARE @idxName sysname
DECLARE @SQL NVARCHAR(4000)
DECLARE @ID INT
DECLARE @RowCnt INT

CREATE TABLE ##Indexes
(
Indexes_ID int IDENTITY(1, 1) NOT NULL,
IndexName sysname NOT NULL,
SchemaName sysname NOT NULL,
ObjectName sysname NOT NULL,
Status int NOT NULL
)

-- non-unique clustered indexes
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes (IndexName, SchemaName, ObjectName, Status) '
SET @SQL = @SQL + 'SELECT i.[name], u.[name], o.[name], i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.[id] = o.[id] '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysusers u '
SET @SQL = @SQL + 'ON o.[uid] = u.[uid] '
SET @SQL = @SQL + 'WHERE i.indid = 1 AND '
SET @SQL = @SQL + 'o.type = ''U'' AND (i.status & 2) = 0'

EXEC sp_executesql @statement = @SQL

-- non-clustered indexes except for those tables that have clustered non-unique indexes; these will be rebuilt automatically
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes (IndexName, SchemaName, ObjectName, Status) '
SET @SQL = @SQL + 'SELECT i.[name], u.[name], o.[name], i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.[id] = o.[id] '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysusers u '
SET @SQL = @SQL + 'ON o.[uid] = u.[uid] '
SET @SQL = @SQL + 'WHERE i.indid > 1 AND i.indid < 255 AND '
SET @SQL = @SQL + 'o.type = ''U'' AND '
SET @SQL = @SQL + '(i.status & (64 | 8388608)) <= 0 AND '
SET @SQL = @SQL + 'o.[name] NOT IN (SELECT ObjectName FROM ##Indexes)'

EXEC sp_executesql @statement = @SQL

-- unique clustered indexes
SET @SQL = ''
SET @SQL = @SQL + 'INSERT INTO ##Indexes (IndexName, SchemaName, ObjectName, Status) '
SET @SQL = @SQL + 'SELECT i.[name], u.[name], o.[name], i.status '
SET @SQL = @SQL + 'FROM ' + @dbName + '.dbo.sysindexes i '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysobjects o '
SET @SQL = @SQL + 'ON i.[id] = o.[id] '
SET @SQL = @SQL + 'INNER JOIN ' + @dbName + '.dbo.sysusers u '
SET @SQL = @SQL + 'ON o.[uid] = u.[uid] '
SET @SQL = @SQL + 'WHERE i.indid = 1 AND '
SET @SQL = @SQL + 'o.type = ''U'' AND (i.status & 2) <> 0'

EXEC sp_executesql @statement = @SQL

SELECT TOP 1 @ID = Indexes_ID, @idxName = IndexName, @objName = ObjectName, @objSchema = SchemaName
FROM ##Indexes
ORDER BY Indexes_ID

SET @RowCnt = @@ROWCOUNT

WHILE @RowCnt <> 0
BEGIN

SET @SQL = 'DBCC DBREINDEX(''' + @dbName + '.' + @objSchema + '.' + @objName + ''', ' + @idxName + ', 0) WITH NO_INFOMSGS'
-- PRINT @SQL
EXEC sp_executesql @statement = @SQL

SELECT TOP 1 @ID = Indexes_ID, @idxName = IndexName, @objName = ObjectName, @objSchema = SchemaName
FROM ##Indexes
WHERE Indexes_ID > @ID
ORDER BY Indexes_ID

SET @RowCnt = @@ROWCOUNT

END

DROP TABLE ##Indexes

RETURN 0

GO

SamCKayak
2005-10-31
re: DBCC DBREINDEX Stored Procedure - new version #2
isp_DBCC_DBREINDEX runs without any error in query analyzer. I set it up as a scheduled job and get the following error message in job history:

Executed as user: S61564\sqlservice. DBCC failed because the following SET options have incorrect settings: 'ARITHABORT'. [SQLSTATE 42000] (Error 1934). The step failed.

Why is the JOB context creating this error when query analyzer does not?

Sam

Tara
2005-11-02
re: DBCC DBREINDEX Stored Procedure - new version #2
I do not know why you are getting that error as a job and not inside Query Analyzer.

SamCKayak
2005-11-03
re: DBCC DBREINDEX Stored Procedure - new version #2
I found out why. Query Analyzer's Tools, Options, Connection Properties Tab has "SET ARITHABORT" checked.

Queueing a job in EM does no such thing.

Then there's this note from BOL on SET ARITHABORT:

SET ARITHABORT must be ON when creating or manipulating indexes on computed columns or indexed views.

I added SET ARITHABORT ON right after SET NOCOUNT in this procedure which seems to have solved the problem.

Thanks for the proc!

Sam

pizza
2006-03-10
re: DBCC DBREINDEX Stored Procedure - new version #2
Hi !
I face a problem with my database.
All the fill factor of primary key and index in the table are set to 90% and slow down the performance of store procedure.

I had manually change the fill factor to 0 ( this process take quite sometime) of some table and I see the store procedure performance increase significially.

There are just too many table involve, how can i write a script to change the fill factor of index for every table ?




Tara
2006-03-13
re: DBCC DBREINDEX Stored Procedure - new version #2
Just use the stored procedure that I posted and modify @SQL to do what you want when it runs DBCC DBREINDEX.

How do you know that the 90% fill factor is what is slowing your performance?

sachin
2006-04-30
re: DBCC DBREINDEX Stored Procedure - new version #2
The stored procedure you posted rebuilds both clusterd as well non clusted indexs.

I tried this.

Rich
2006-05-21
re: DBCC DBREINDEX Stored Procedure - new version #2
Is there a way to view the output of this stored procedure?