Automatically Drop and Recreate current indexes - sql

I'm working on providing a method to allow for bulk updating our tables ( greater than 1M new or updated rows per update) and was interested in dropping the current indexes and recreating them after the updates.
I was wondering if anyone has a script to provide loose coupling of these operations so that if the indexes change over time, the update process does not change.
It seems like this is one of those things that the community has already probably solved.

I have script that I use to query the system tables to capture all non-clustered indexes and disable then rebuild upon completion. The below is for use on standard edition, if you are on enterprise I would add the ONLINE option.
Disable
DECLARE #sql AS VARCHAR(MAX);
SET #sql = '';
SELECT
#sql = #sql + 'ALTER INDEX [' + i.name + '] ON [' + o.name + '] DISABLE; '
FROM sys.indexes AS i
JOIN sys.objects AS o ON i.object_id = o.object_id
WHERE i.type_desc = 'NONCLUSTERED'
AND o.type_desc = 'USER_TABLE'
EXEC (#sql)
Rebuild
DECLARE #sql AS VARCHAR(MAX);
SET #sql = '';
SELECT
#sql = #sql + 'ALTER INDEX [' + i.name + '] ON [' + o.name + '] REBUILD WITH (FILLFACTOR = 80); '
FROM sys.indexes AS i
JOIN sys.objects AS o ON i.object_id = o.object_id
WHERE i.type_desc = 'NONCLUSTERED'
AND o.type_desc = 'USER_TABLE'
EXEC (#sql);
I like this method as it is very customizable as you can exclude/include certain tables based on the conditions as well as avoiding a cursor. Also you can change the EXEC to a PRINT and see the code that will execute and manually run it.
Condition to exclude a table
AND o.name NOT IN ('tblTest','tblTest1');

EXEC sp_MSforEachTable 'ALTER INDEX ALL ON ? DISABLE'
and
EXEC sp_MSforEachTable 'ALTER INDEX ALL ON ? REBUILD'
is all you need if you want to do it for all tables and every index.

Related

DISABLE all triggers with a particular name on all tables using SQL

Id like to disable all triggers with the name beginning with TRG. I would like to loop through all my tables in my database.
So far I have this:
ALTER TABLE [dbo].[Customer] DISABLE TRIGGER TRG
The only thing I know is to use a query to build the alter table statements, and then copy/paste those:
SELECT
sysobjects.name AS trigger_name
,USER_NAME(sysobjects.uid) AS trigger_owner
,s.name AS table_schema
,OBJECT_NAME(parent_obj) AS table_name ,
'ALTER TABLE ' + OBJECT_NAME(parent_obj) + ' DISABLE TRIGGER ' + sysobjects.name + ';' as Stmt
FROM sysobjects
INNER JOIN sys.tables t
ON sysobjects.parent_obj = t.object_id
INNER JOIN sys.schemas s
ON t.schema_id = s.schema_id
WHERE sysobjects.type = 'TR'
and sysobjects.name like 'TRG%'.
No need to loop here. That is the wrong mindset. You just need to leverage sys.triggers and build dynamic sql. Once you are comfortable with the output of #SQL uncomment the exec line.
declare #SQL nvarchar(MAX) = ''
select #SQL = #SQL + 'alter table [' + OBJECT_NAME(parent_id) + '] DISABLE TRIGGER ' + name + ';'
from sys.triggers
where name like 'trg%'
select #SQL
--exec sp_executesql #SQL

Drop All User-Defined Types from SQL Server

I need to drop all User-Defined Types, User-Defined Data Types and User-Defined Tables (all in Types folder). Is it possible to do this using a T-SQL script, or must I use SSMS?
select 'drop type ' + quotename(schema_name(schema_id)) + '.' + quotename(name)
from sys.types
where is_user_defined = 1
You can try getting all you user defined type objects and create a script using this query or Generate Script Clicking Task under your database.
http://blog.falafel.com/t-sql-drop-all-objects-in-a-sql-server-database/
For the above link posted
T-SQL: Drop All Objects in a SQL Server Database
I added following code to replace check Constraint from Drop Constraint by Stefan Steiger to Drop All Constraints. I chose below code because it uses same approach
DECLARE #sql nvarchar(MAX)
SET #sql = ''
SELECT #sql = #sql + 'ALTER TABLE ' + QUOTENAME(RC.CONSTRAINT_SCHEMA)
+ '.' + QUOTENAME(KCU1.TABLE_NAME)
+ ' DROP CONSTRAINT ' + QUOTENAME(rc.CONSTRAINT_NAME) + '; '
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
Declare #sql NVARCHAR(MAX) = N'';
SELECT #sql = #sql + N' DROP type '
+ QUOTENAME(SCHEMA_NAME(schema_id))
+ N'.' + QUOTENAME(name)
FROM sys.types
WHERE is_user_defined = 1
Exec sp_executesql #sql

Diff / Delta script: ideas on streamlining it?

I'm pulling data from a remote DB into a local MS SQL Server DB, the criteria being whether the PK is higher than I have in my data warehouse or whether the edit date is within a range that I provide with an argument. That works super fast so I am happy with it. However, when I attempt to sync this delta table into my data warehouse it takes quite a long time.
Here's my SPROC:
ALTER PROCEDURE [dbo].[sp_Sync_Delta_Table]
#tableName varchar(max)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql as varchar(4000)
-- Delete rows in MIRROR database where ID exists in the DELTA database
SET #sql = 'Delete from [SERVER-WHS].[MIRROR].[dbo].[' + #tableName
+ '] Where [ID] in (Select [ID] from [SERVER-DELTA].[DELTAS].[dbo].[' + #tableName + '])'
EXEC(#sql)
-- Insert all deltas
SET #sql = 'Insert Into [SERVER-WHS].[MIRROR].[dbo].[' + #tableName
+ '] Select * from [SERVER-DELTA].[DELTAS].[dbo].[' + #tableName + ']'
EXEC(#sql)
END
It works, but I think it takes way too long. For example: inserting 3590 records from the DELTA table into the MIRROR table containing 3,600,761 took over 25 minutes.
Can anyone give me a hint on how I can make this job easier on SSMS? I'm using 2008 R2, btw.
Thanks again!
Nate
The issue is likely the time required to do a table scan on the 3,600,761 to see if the new records are unique.
First of all, let's confirm that the primary key (ID) on the target table is the clustered index and increasing.
SELECT s.name, o.name, i.name, i.type_desc, ic.key_ordinal, c.name
FROM sys.objects o
JOIN sys.columns c ON (c.object_id = o.object_id)
JOIN sys.schemas s ON (s.schema_id = o.schema_id)
JOIN sys.indexes i 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 AND ic.column_id = c.column_id)
WHERE o.name = '[table_name]'
If the index is not an ascending integer, it is possible that the inserts are causing lots of page splits.
Second, what other objects does that insert affect. Are there triggers, materialized views, or non-clustered indexes?
Third, do you have
My suggestion would be to stage the data on the mirror server in a local table. It can be as simple as as:
SET #sql = 'SELECT INTO * [MIRROR].[dbo].[' + #tableName + '_Staging] from [SERVER-DELTA].[DELTAS].[dbo].[' + #tableName + ']'
EXEC(#sql)
After that add a clustered primary key to the table.
SET #sql = 'ALTER TABLE [MIRROR].[dbo].[' + #tableName + '_Staging] ADD CONSTRAINT [PK_' + #tableName + '] PRIMARY KEY CLUSTERED (Id ASC)'
EXEC(#sql)
At this point, try inserting the data into the real table. The optimizer should be much more helpful this time.
Change the delete portion to:
SET #sql = 'Delete tbl1 from [SERVER-WHS].[MIRROR].[dbo].[' + #tableName
+ '] tbl1 inner join [SERVER-DELTA].[DELTAS].[dbo].[' + #tableName + '] tbl2 on tbl1.[ID] = tbl2.[ID]'
In future use INNER JOIN instead of IN with Sub Query.

Delete all indexes of specific database

How to delete all indexes from one database, whether clustered or nonclustered?
I need do that with script, not over GUI.
EDITED
Database has 7 tables, some of them are lookups, some are related over foreign keys. Every table has minimal one index, created in time the primary key was created, so automatically was created constraint. When deleting such indexes over an GUI, I got an error that indexes cannot be deleted because of dependency on other keys.
So, I need to first delete an indexes keys that are foreign keys, and then an indexes created over primary keys.
Slightly different variation of dynamic SQL, however it drops foreign keys first, then primary keys, then indexes (non-clustered indexes first so that you don't convert to a heap (which can affect all the non-clustered indexes)).
USE specific_database;
GO
First, delete all foreign keys:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'ALTER TABLE '
+ QUOTENAME(OBJECT_SCHEMA_NAME([parent_object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([parent_object_id]))
+ ' DROP CONSTRAINT ' + QUOTENAME(name) + ';
'
FROM sys.foreign_keys
WHERE OBJECTPROPERTY([parent_object_id], 'IsMsShipped') = 0;
EXEC sp_executesql #sql;
Now drop primary keys:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'ALTER TABLE '
+ QUOTENAME(OBJECT_SCHEMA_NAME([parent_object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([parent_object_id]))
+ ' DROP CONSTRAINT ' + QUOTENAME(name) + ';
'
FROM sys.key_constraints
WHERE [type] = 'PK'
AND OBJECTPROPERTY([parent_object_id], 'IsMsShipped') = 0;
EXEC sp_executesql #sql;
And finally, indexes, non-clustered first:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'DROP INDEX '
+ QUOTENAME(name) + ' ON '
+ QUOTENAME(OBJECT_SCHEMA_NAME([object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([object_id])) + ';
'
FROM sys.indexes
WHERE index_id > 0
AND OBJECTPROPERTY([object_id], 'IsMsShipped') = 0
ORDER BY [object_id], index_id DESC;
EXEC sp_executesql #sql;
Note that the database engine tuning advisor will recommend a bunch of these indexes (and depending on the workload you present it, may miss some, and may suggest redundant and nearly duplicate indexes). However it is not going to recommend any of the data integrity stuff you just deleted (PK, FK, unique constraints).
create a Dynamic SQL
DECLARE #qry nvarchar(max);
SELECT #qry = (SELECT 'DROP INDEX ' + ix.name + ' ON ' + OBJECT_NAME(ID) + '; '
FROM sysindexes ix
WHERE ix.Name IS NOT NULL AND
ix.Name LIKE '%prefix_%'
FOR XML PATH(''));
EXEC sp_executesql #qry
Drop All Indexes and Stats in one Script

How can I drop all indexes in a SQL database with one command?

So, how can I drop all indexes in a SQL database with one command? I have this command that will get me all the 20 or so drop statements, but how can I run all of those drop statements from this "result set"?
select * from vw_drop_idnex;
Another variation that gives me the same list is:
SELECT 'DROP INDEX ' + ix.Name + ' ON ' + OBJECT_NAME(ID) AS QUERYLIST
FROM sysindexes ix
WHERE ix.Name IS NOT null and ix.Name like '%pre_%'
I tried to do "exec(select cmd from vw_drop_idnex)" and it didn't work. I am looking for something that works like a for loop and runs the queries one by one.
-----------------------
With Rob Farleys help, final draft of the script is:
declare #ltr nvarchar(1024);
SELECT #ltr = ( select 'alter table '+o.name+' drop constraint '+i.name+';'
from sys.indexes i join sys.objects o on i.object_id=o.object_id
where o.type<>'S' and is_primary_key=1
FOR xml path('') );
exec sp_executesql #ltr;
declare #qry nvarchar(1024);
select #qry = (select 'drop index '+o.name+'.'+i.name+';'
from sys.indexes i join sys.objects o on i.object_id=o.object_id
where o.type<>'S' and is_primary_key<>1 and index_id>0
for xml path(''));
exec sp_executesql #qry
You're very close.
declare #qry nvarchar(max);
select #qry =
(SELECT 'DROP INDEX ' + quotename(ix.name) + ' ON ' + quotename(object_schema_name(object_id)) + '.' + quotename(OBJECT_NAME(object_id)) + '; '
FROM sys.indexes ix
WHERE ix.Name IS NOT null and ix.Name like '%prefix_%'
for xml path(''));
exec sp_executesql #qry
this worked for me
we skip sys indexes and for constraints
declare #qry nvarchar(max);
select #qry = (
select 'IF EXISTS(SELECT * FROM sys.indexes WHERE name='''+ i.name +''' AND object_id = OBJECT_ID(''['+s.name+'].['+o.name+']'')) drop index ['+i.name+'] ON ['+s.name+'].['+o.name+']; '
from sys.indexes i
inner join sys.objects o on i.object_id=o.object_id
inner join sys.schemas s on o.schema_id = s.schema_id
where o.type<>'S' and is_primary_key<>1 and index_id>0
and s.name!='sys' and s.name!='sys' and is_unique_constraint=0
for xml path(''));
exec sp_executesql #qry
From: Stephen Hill's Bloggie
DECLARE #indexName VARCHAR(128)
DECLARE #tableName VARCHAR(128)
DECLARE [indexes] CURSOR FOR
SELECT [sysindexes].[name] AS [Index],
[sysobjects].[name] AS [Table]
FROM [sysindexes]
INNER JOIN [sysobjects]
ON [sysindexes].[id] = [sysobjects].[id]
WHERE [sysindexes].[name] IS NOT NULL
AND [sysobjects].[type] = 'U'
--AND [sysindexes].[indid] > 1
OPEN [indexes]
FETCH NEXT FROM [indexes] INTO #indexName, #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT 'DROP INDEX [' + #indexName + '] ON [' + #tableName + ']'
Exec ('DROP INDEX [' + #indexName + '] ON [' + #tableName + ']')
FETCH NEXT FROM [indexes] INTO #indexName, #tableName
END
CLOSE [indexes]
DEALLOCATE [indexes]
GO
None of the answers quite suited my needs.
I needed one that will also drop indexes that backup unique or primary constraints (except if these can't be dropped as they back up a foreign key)
DECLARE #SqlScript NVARCHAR(MAX);
SELECT #SqlScript =
(
SELECT
'
BEGIN TRY
'+ CASE WHEN 1 IN (i.is_primary_key, i.is_unique_constraint) THEN
'
ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id)) + '.' + QUOTENAME(t.name) + ' DROP CONSTRAINT ' + QUOTENAME(i.name) + ';'
else
'
DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id)) + '.' + QUOTENAME(t.name)
END+'
END TRY
BEGIN CATCH
RAISERROR(''Could not drop %s on table %s'', 0,1, ' + QUOTENAME(i.name, '''') + ', ' + QUOTENAME(t.name, '''') + ')
END CATCH
'
FROM sys.indexes i
JOIN sys.tables t ON i.object_id = t.object_id
WHERE i.type_desc IN ('CLUSTERED', 'NONCLUSTERED' )
ORDER BY t.object_id, i.index_id DESC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)');
--Return script that will be run
SELECT #SqlScript AS [processing-instruction(x)]
FOR XML PATH('');
EXEC (#SqlScript);
Minor improvements to the accepted answer that I had to make in my own case, mostly to account for schemas:
declare #qry nvarchar(4000);
select #qry = (select 'drop index ['+s.name+'].['+o.name+'].['+i.name+'];'
from sys.indexes i join sys.objects o on i.object_id=o.object_id join sys.schemas s on o.schema_id=s.schema_id
where o.type<>'S' and is_primary_key<>1 and index_id>0 and s.name<>'sys'
for xml path(''));
exec sp_executesql #qry
Also: In my case it couldn't run in one go because the script becomes longer than 4000 characters. The only way I could think of to deal with that was to put a "top 20" on the inner select and execute it multiple times.
The "Final Draft" that OP posts as part of his question errors out if there are already zero indexes on any table in the DB. I needed to fix that.
Also, I wanted more control over the process than dropping all indexes on all tables, so I wrote the following stored proc to do it one table at a time:
CREATE PROCEDURE [dbo].[sp_DropAllIndexesOnTable]
#TableName varchar(1000)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #DropCommand1 nvarchar(4000)
DECLARE #DropCommand2 nvarchar(4000)
--Create Temp Table to hold the names and table names of all Clustered Indexes
SELECT o.name AS TableName, i.name AS IndexName
INTO #AllClustered
FROM sys.indexes i
INNER JOIN sys.objects o ON i.object_id=o.object_id
WHERE o.type <> 'S'
AND is_primary_key = 1
--Create Temp Table to hold the names and table names of all NonClustered Indexes
SELECT o.name AS TableName, i.name AS IndexName
INTO #AllNonClustered
FROM sys.indexes i
INNER JOIN sys.objects o ON i.object_id=o.object_id
WHERE o.type <> 'S'
AND is_primary_key <> 1
AND index_id > 0
--Create DROP CONSTRAINT command for the Primary Key Constraint if there is one
SELECT #DropCommand1 = ( SELECT 'ALTER TABLE dbo.['+ TableName +'] DROP CONSTRAINT ['+ IndexName +'];'
FROM #AllClustered
WHERE TableName = #TableName
FOR xml path('') );
--Create DROP INDEX command for the indexes on the table if there are any
SELECT #DropCommand2 = ( SELECT 'DROP INDEX [' + IndexName + '] ON dbo.['+ TableName +'];'
FROM #AllNonClustered
WHERE TableName = #TableName
FOR xml path('') );
IF (#DropCommand1 IS NULL AND #DropCommand2 IS NULL) PRINT 'No action taken, zero indexes found on table ' + #TableName
IF #DropCommand1 IS NOT NULL EXEC sp_executesql #DropCommand1
IF #DropCommand2 IS NOT NULL EXEC sp_executesql #DropCommand2
DROP TABLE IF EXISTS #AllClustered
DROP TABLE IF EXISTS #AllNonClustered
END
GO
I cycle through the specific tables in my DB which I want to drop indexes on using a loop, and I drop the indexes by calling this proc with the table name, and recreate better ones right after. This way, only one table at a time has no indexes.
The reason I do this and the number of tables I do it on would make your head spin, but I definitely needed a proc like this!
SELECT 'DROP INDEX [' + IX.NAME + '] ON ' + OBJECT_NAME(IX.OBJECT_ID) + '; '
FROM SYS.INDEXES IX
JOIN SYS.OBJECTS O ON IX.OBJECT_ID = O.OBJECT_ID
INNER JOIN SYS.SCHEMAS S ON O.SCHEMA_ID = S.SCHEMA_ID
WHERE
IX.NAME IS NOT NULL
AND O.TYPE <> 'S'
AND IS_PRIMARY_KEY <> 1
AND INDEX_ID > 0
AND S.NAME != 'SYS' AND S.NAME!= 'SYS' AND IS_UNIQUE_CONSTRAINT = 0
Modify conditions according to your needs
If u want to delete PK constraints, you will get this message if you try to drop index:
An explicit DROP INDEX is not allowed on index... It is being used for PRIMARY KEY constraint enforcement.
Then, use this...
SELECT 'ALTER TABLE [' + O.NAME + '] DROP CONSTRAINT ' + IX.NAME + '; '
FROM SYS.INDEXES IX
JOIN SYS.OBJECTS O ON IX.OBJECT_ID = O.OBJECT_ID
INNER JOIN SYS.SCHEMAS S ON O.SCHEMA_ID = S.SCHEMA_ID
WHERE
IX.NAME IS NOT NULL
AND O.TYPE <> 'S'
AND INDEX_ID > 0
AND S.NAME != 'SYS' AND S.NAME!= 'SYS'