Online operation cannot be performed for index - sql-server

I'm trying to REBUILD/REORGANIZE indexes on our SQL SERVER 2014 Enterprise db server. I've written the following stored procedure with help from the web:
USE [ManagementReporter]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[REORGANIZE_TOP_INDEXES_NO_PAGE]
AS
DECLARE #dbname SYSNAME = DB_NAME(),
#schema_name SYSNAME,
#page_count INT,
#table_name SYSNAME,
#index_name SYSNAME,
#avg_fragmentation FLOAT,
#sql NVARCHAR(MAX)
-- Do re-indexing
DECLARE INDEXFRAGMENTATION CURSOR FAST_FORWARD FOR
SELECT DISTINCT TOP 5000 DBSCHEMAS.[NAME] AS 'Schema',
DBTABLES.[NAME] AS 'Table',
DBINDEXES.[NAME] AS 'Index',
INDEXSTATS.AVG_FRAGMENTATION_IN_PERCENT AS 'Frag %',
PAGE_COUNT
FROM SYS.DM_DB_INDEX_PHYSICAL_STATS (DB_ID(), NULL, NULL, NULL, NULL) AS INDEXSTATS
INNER JOIN SYS.TABLES dbtables
ON DBTABLES.[OBJECT_ID] = INDEXSTATS.[OBJECT_ID]
INNER JOIN SYS.SCHEMAS dbschemas
ON DBTABLES.[SCHEMA_ID] = DBSCHEMAS.[SCHEMA_ID]
INNER JOIN SYS.INDEXES AS DBINDEXES
ON DBINDEXES.[OBJECT_ID] = INDEXSTATS.[OBJECT_ID]
AND INDEXSTATS.INDEX_ID = DBINDEXES.INDEX_ID
INNER JOIN SYS.INDEX_COLUMNS AS DBINDEXCOLUMNS
ON DBINDEXCOLUMNS.[OBJECT_ID] = DBINDEXES.[OBJECT_ID]
INNER JOIN SYS.COLUMNS AS DBSYSCOLUMNS
ON DBSYSCOLUMNS.[OBJECT_ID] = DBINDEXCOLUMNS.[OBJECT_ID]
AND DBSYSCOLUMNS.COLUMN_ID = DBINDEXCOLUMNS.COLUMN_ID
WHERE INDEXSTATS.DATABASE_ID = DB_ID()
AND DBINDEXES.NAME IS NOT NULL
AND NOT ( DBSYSCOLUMNS.SYSTEM_TYPE_ID IN ( 34, 35, 99, 173, 241 ) --IMAGE, TEXT, NTEXT, BINARY, XML
OR ( DBSYSCOLUMNS.SYSTEM_TYPE_ID IN ( 165, 167, 231 ) --VARBINARY, VARCHAR, NVARCHAR
AND DBSYSCOLUMNS.MAX_LENGTH = -1 ) )
ORDER BY INDEXSTATS.AVG_FRAGMENTATION_IN_PERCENT DESC
OPEN INDEXFRAGMENTATION
FETCH NEXT FROM INDEXFRAGMENTATION INTO #schema_name,
#table_name,
#index_name,
#avg_fragmentation,
#page_count
WHILE ##FETCH_STATUS = 0
BEGIN
IF #avg_fragmentation > 20 -- ReBuild index
SET #sql = 'ALTER INDEX [' + #index_name + '] ON [' + #schema_name + '].[' + #table_name + '] REBUILD WITH (ONLINE = ON (WAIT_AT_LOW_PRIORITY (MAX_DURATION= 20, ABORT_AFTER_WAIT=BLOCKERS)))';
ELSE -- ReOrganize index
SET #sql = 'ALTER INDEX [' + #index_name + '] ON [' + #schema_name + '].[' + #table_name + '] REORGANIZE;';
EXEC (#sql)
FETCH NEXT FROM INDEXFRAGMENTATION INTO #schema_name,
#table_name,
#index_name,
#avg_fragmentation,
#page_count
END
CLOSE INDEXFRAGMENTATION
DEALLOCATE INDEXFRAGMENTATION
From what I've read on the WEB, I thought if I ignored the indexes with the above SYSTEM_TYPE_ID's, I thought it would not bring these indexes back in my result set. However, the following Index is still getting selected and producing the following error:
Msg 2725, Level 16, State 2, Line 3 An online operation cannot be
performed for index 'PK_ReportOutput' because the index contains
column 'EmailMessage' of data type text, ntext, image or FILESTREAM.
For a non-clustered index, the column could be an include column of
the index. For a clustered index, the column could be any column of
the table. If DROP_EXISTING is used, the column could be part of a new
or old index. The operation must be performed offline.
The EmailMessage column is defined as (ntext, not null), so I'm not understanding why it's getting selected with my main SQL above? It looks like ntext has a system_type_id of 99, which I have excluded.
Here is the definition of the Table and Index that the error's complaining about:
Am I missing something with my SQL statement?

Related

Wipe out all data in a scheme, leave structure intact

We're going to be going through a period of testing on a product soon. This product is a web application with a SQL Server 2008R2 backend.
Our database has several schemas within it (Customer, DataEntry, and a few others).
I have found ways to wipe all data in a database without breaking referential integrity or the data structures, which is close to what we're looking to do. The problem I'm finding is that we actually need a bunch of the data from some of the tables. Essentially, we only want to wipe the Customers schema.
We have a script written which will load in the test data for customers, but is there a way to change the techniques in my linked article to target only a specific schema? Is there a better way to clear all data in a schema?
A common scenario for me as well. I usually write what I call a reset script, deleting all data form the target tables in the order necessary to prevent referential errors, and then reseed the primary keys.
DELETE FROM < table 1 >
DELETE FROM < table 2 >
... etc ...
DBCC CHECKIDENT (< table 1 >, RESEED, 0)
DBCC CHECKIDENT (< table 2 >, RESEED, 0)
... etc ...
EDIT
To more fully answer the original question. to leave data in specific tables you would need to modify the block of code that does the deleting / truncating, and also modify the code that reseeds the idents in a similar way.
EXEC sp_MSForEachTable '
IF object_id(''?'') != < table name > AND object_id(''?'') != < table name > AND ... etc ...
BEGIN
IF OBJECTPROPERTY(object_id(''?''), ''TableHasForeignRef'') = 1
DELETE FROM ?
ELSE
TRUNCATE TABLE ?
END
'
GO
Just set the #schemaID to the name of the schema you wish to blow away and it should do the rest. If you end up with a FK dependency loop it will break and tell you what to do...
Declare #schemaID Nvarchar(256)
Set #schemaID = 'Schema' -- Set this to the name of the schema you wish to blow away
If Object_ID('tempdb..#tables') Is Not Null Drop Table #tables
Create Table #tables (tID Int, SchemaName Nvarchar(256), TableName Nvarchar(256))
Insert #tables
Select Row_Number() Over (Order By s.name, so.name), s.name, so.name
From sysobjects so
Join sys.schemas s
On so.uid = s.schema_id
Where so.xtype = 'u'
And s.name = #schemaID
Declare #SQL Nvarchar(Max),
#schema Nvarchar(256),
#table Nvarchar(256),
#iter Int = 1,
#loopCatch Int = 0
While Exists (Select 1
From #tables)
Begin
Select #schema = SchemaName,
#table = TableName
From #tables
Where tID = #iter
If Exists (Select 1
From sysobjects o
Join sys.schemas s1
On o.uid = s1.schema_id
Join sysforeignkeys fk
On o.id = fk.rkeyid
Join sysobjects o2
On fk.fkeyid = o2.id
Join sys.schemas s2
On o2.uid = s2.schema_id
Join #tables t
On o2.name = t.TableName Collate Database_Default
And s2.name = t.SchemaName Collate Database_Default
Where o.name = #table
And s1.name = #schema)
Begin
Update t
Set tID = (Select Max(tID) From #tables) + 1
From #tables t
Where tableName = #table
And schemaName = #schema
Set #iter = #iter + 1
End
Else
Begin
Set #Sql = 'Truncate Table [' + #schema + '].[' + #table + ']'
Begin Try
Exec sp_executeSQL #SQL;
Delete t
From #tables t
Where tableName = #table
And schemaName = #schema
Set #iter = #iter + 1
End Try
Begin Catch
Print #SQL
Update t
Set tID = (Select Max(tID) From #tables) + 1
From #tables t
Where tableName = #table
And schemaName = #schema
Set #iter = #iter + 1
Set #loopCatch = #loopCatch + 1;
If #loopCatch > 5
Begin
Select 'WARNING: Endless FK redundancy loop. Drop the constraints and these tables, truncate and reapply constraints manually'
Union All
Select '[' + SchemaName + '].[' + TableName + ']'
From #tables;
Break;
End
End Catch
End
End
This is parameterized on database and schema. If no schema is supplied, it will clear all data in the specified database.
Handles tables with foreign key references appropriately by disabling constraints. If the procedure fails, which it shouldn't normally do, ensure that you run it successfully after fixing the cause of the problem, which should ensure constraint checking goes back to normal.
This will not handle foreign key references correctly if you have foreign keys between schemas, however, it could be fairly easily amended to handle this.
create procedure [removeData] (#database_name sysname, #schema_name sysname = null)
as
set nocount on
create table #tables (
TableName varchar(900) not null primary key,
HasFKRef bit not null
);
declare #sql nvarchar(4000),
#table_name varchar(900);
if (db_id(#database_name) is null)
raiserror ('You must at least specify the database name', 16, 1);
set #sql = 'select ''['' + TABLE_CATALOG + ''].['' + TABLE_SCHEMA + ''].['' + TABLE_NAME + '']'' as TableName, (case when exists(select * from [' + #database_name + '].INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc inner join [' + #database_name + '].INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc on rc.UNIQUE_CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG and rc.UNIQUE_CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA and rc.UNIQUE_CONSTRAINT_NAME = tc.CONSTRAINT_NAME where tc.TABLE_NAME = t.TABLE_NAME) then 1 else 0 end) as HasFKRef
from [' + #database_name + '].INFORMATION_SCHEMA.TABLES t
where TABLE_TYPE = ''BASE TABLE'' and TABLE_SCHEMA = isnull(#schema_name, TABLE_SCHEMA)';
insert into #tables
exec sp_executesql #sql, N'#schema_name sysname', #schema_name;
declare #curse cursor
set #curse = cursor fast_forward for
select sql from (
select 'alter table ' + TableName + ' nocheck constraint all' as sql, 1 as sort
from #tables
union all
select 'truncate table ' + TableName, 2 as sort
from #tables
where HasFKRef = 0
union all
select 'delete from ' + TableName, 3 as sort
from #tables
where HasFKRef = 1
union all
select 'alter table ' + TableName + ' with check check constraint all', 4 as sort
from #tables
) t
order by sort, sql
open #curse
fetch next from #curse into #sql
while (##fetch_status = 0)
begin
exec (#sql)
fetch next from #curse into #sql
end
close #curse
GO

How can I drop a table if there is a foreign key constraint in SQL Server?

I have the following:
DROP TABLE [dbo].[ExtraUserInformation];
DROP TABLE [dbo].[UserProfile];
DROP TABLE [dbo].[webpages_Membership];
DROP TABLE [dbo].[webpages_OAuthMembership];
DROP TABLE [dbo].[webpages_Roles];
DROP TABLE [dbo].[webpages_UsersInRoles];
CREATE TABLE [dbo].[ExtraUserInformation] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[UserId] INT NOT NULL,
[FullName] NVARCHAR (MAX) NULL,
[Link] NVARCHAR (MAX) NULL,
[Verified] BIT NULL,
CONSTRAINT [PK_dbo.ExtraUserInformation] PRIMARY KEY CLUSTERED ([Id] ASC)
);
CREATE TABLE [dbo].[webpages_UsersInRoles] (
[UserId] INT NOT NULL,
[RoleId] INT NOT NULL,
PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC),
CONSTRAINT [fk_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[UserProfile] ([UserId]),
CONSTRAINT [fk_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[webpages_Roles] ([RoleId])
);
However this is failing with a message saying:
Msg 3726, Level 16, State 1, Line 6
Could not drop object 'dbo.UserProfile' because it is referenced by a FOREIGN KEY constraint.
Msg 3726, Level 16, State 1, Line 9
Could not drop object 'dbo.webpages_Roles' because it is referenced by a FOREIGN KEY constraint.
Msg 2714, Level 16, State 6, Line 27
There is already an object named 'UserProfile' in the database.
Checking identity information: current identity value 'NULL', current column value 'NULL'.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
How can I drop a table in these circumstances?
You must drop the constraint before you can drop the table. Otherwise its rule violation that could break the databases Referential Integrity.
How to get foreign key relationships see this old question.
SQL DROP TABLE foreign key constraint
1-firstly, drop the foreign key constraint after that drop the tables.
2-you can drop all foreign key via executing the following query:
DECLARE #SQL varchar(4000)=''
SELECT #SQL =
#SQL + 'ALTER TABLE ' + s.name+'.'+t.name + ' DROP CONSTRAINT [' + RTRIM(f.name) +'];' + CHAR(13)
FROM sys.Tables t
INNER JOIN sys.foreign_keys f ON f.parent_object_id = t.object_id
INNER JOIN sys.schemas s ON s.schema_id = f.schema_id
--EXEC (#SQL)
PRINT #SQL
if you execute the printed results #SQL, the foreign keys will be dropped.
The Best Answer to dropping the table containing foreign constraints is :
Step 1 : Drop the Primary key of the table.
Step 2 : Now it will prompt whether to delete all the foreign references or not.
Step 3 : Delete the table.
To drop a table if there is a foreign key constraint in MySQL Server?
Run the sql query:
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE table_name
Hope it helps!
--Find and drop the constraints
DECLARE #dynamicSQL VARCHAR(MAX)
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT dynamicSQL = 'ALTER TABLE [' + OBJECT_SCHEMA_NAME(parent_object_id) + '].[' + OBJECT_NAME(parent_object_id) + '] DROP CONSTRAINT [' + name + ']'
FROM sys.foreign_keys
WHERE object_name(referenced_object_id) in ('table1', 'table2', 'table3')
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #dynamicSQL
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #dynamicSQL
EXEC (#dynamicSQL)
FETCH NEXT FROM MY_CURSOR INTO #dynamicSQL
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
-- Drop tables
DROP 'table1'
DROP 'table2'
DROP 'table3'
You have to drop the constraint before drop your table.
You can use those queries to find all FKs in your table and find the FKs in the tables in which your table is used.
Declare #SchemaName VarChar(200) = 'Your Schema name'
Declare #TableName VarChar(200) = 'Your Table Name'
-- Find FK in This table.
SELECT
' IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id =
OBJECT_ID(N''' +
'[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' + FK.name + ']'
+ ''') AND parent_object_id = OBJECT_ID(N''' +
'[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' +
OBJECT_NAME(FK.parent_object_id) + ']' + ''')) ' +
'ALTER TABLE ' + OBJECT_SCHEMA_NAME(FK.parent_object_id) +
'.[' + OBJECT_NAME(FK.parent_object_id) +
'] DROP CONSTRAINT ' + FK.name
, S.name , O.name, OBJECT_NAME(FK.parent_object_id)
FROM sys.foreign_keys AS FK
INNER JOIN Sys.objects As O
ON (O.object_id = FK.parent_object_id )
INNER JOIN SYS.schemas AS S
ON (O.schema_id = S.schema_id)
WHERE
O.name = #TableName
And S.name = #SchemaName
-- Find the FKs in the tables in which this table is used
SELECT
' IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id =
OBJECT_ID(N''' +
'[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' + FK.name + ']'
+ ''') AND parent_object_id = OBJECT_ID(N''' +
'[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' +
OBJECT_NAME(FK.parent_object_id) + ']' + ''')) ' +
' ALTER TABLE ' + OBJECT_SCHEMA_NAME(FK.parent_object_id) +
'.[' + OBJECT_NAME(FK.parent_object_id) +
'] DROP CONSTRAINT ' + FK.name
, S.name , O.name, OBJECT_NAME(FK.parent_object_id)
FROM sys.foreign_keys AS FK
INNER JOIN Sys.objects As O
ON (O.object_id = FK.referenced_object_id )
INNER JOIN SYS.schemas AS S
ON (O.schema_id = S.schema_id)
WHERE
O.name = #TableName
And S.name = #SchemaName
BhupeshC and murat , this is what I was looking for. However #SQL varchar(4000) wasn't big enough. So, small change
DECLARE #cmd varchar(4000)
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
select 'ALTER TABLE ['+s.name+'].['+t.name+'] DROP CONSTRAINT [' + RTRIM(f.name) +'];' FROM sys.Tables t INNER JOIN sys.foreign_keys f ON f.parent_object_id = t.object_id INNER JOIN sys.schemas s ON s.schema_id = f.schema_id
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #cmd
WHILE ##FETCH_STATUS = 0
BEGIN
-- EXEC (#cmd)
PRINT #cmd
FETCH NEXT FROM MY_CURSOR INTO #cmd
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
GO
Re removing constraints and dropping the tables, this script can get all constraints for a selected list of tables and then drop the constraints and then delete the tables.
DECLARE #id INT
DECLARE #strSQL NVARCHAR(max)
DECLARE #deleteCurser CURSOR
DECLARE #deleteTableCurser CURSOR
declare #SelectedTables table ([name] sysname, [object_id] int)
Insert into #SelectedTables ([name], [object_id])
SELECT t.name, t.object_id
FROM sys.Tables t
where t.name in
-- Change Name of tables here
('table1','table2','table100')
SET #deleteTableCurser = CURSOR FOR
SELECT strSQL =
'ALTER TABLE ' + s.name+'.'+t.name + ' DROP CONSTRAINT [' + RTRIM(f.name) +'];'
FROM #SelectedTables t
INNER JOIN sys.foreign_keys f ON f.parent_object_id = t.object_id
INNER JOIN sys.schemas s ON s.schema_id = f.schema_id
OPEN #deleteTableCurser
FETCH Next
FROM #deleteTableCurser INTO #strSQL
WHILE ##FETCH_STATUS = 0
BEGIN
print(#strSQL)
EXEC (#strSQL)
FETCH NEXT
FROM #deleteTableCurser INTO #strSQL
END
CLOSE #deleteTableCurser
DEALLOCATE #deleteTableCurser
SET #deleteCurser = CURSOR FOR
SELECT strSQL = 'DROP TABLE ['+ t.name+ '];'
FROM #SelectedTables t
OPEN #deleteCurser
FETCH Next
FROM #deleteCurser INTO #strSQL
WHILE ##FETCH_STATUS = 0
BEGIN
print(#strSQL)
EXEC (#strSQL)
FETCH NEXT
FROM #deleteCurser INTO #strSQL
END
CLOSE #deleteCurser
DEALLOCATE #deleteCurser
Type this .... SET foreign_key_checks = 0;
delete your table then type SET foreign_key_checks = 1;
MySQL – Temporarily disable Foreign Key Checks or Constraints

How to drop all tables in a SQL Server database?

I'm trying to write a script that will completely empty a SQL Server database. This is what I have so far:
USE [dbname]
GO
EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT all'
EXEC sp_msforeachtable 'DELETE ?'
When I run it in the Management Studio, I get:
Command(s) completed successfully.
but when I refresh the table list, they are all still there. What am I doing wrong?
You can also delete all tables from database using only MSSMS UI tools (without using SQL script). Sometimes this way can be more comfortable (especially if it is performed occasionally)
I do this step by step as follows:
Select 'Tables' on the database tree (Object Explorer)
Press F7 to open Object Explorer Details view
In this view select tables which have to be deleted (in this case all of them)
Keep pressing Delete until all tables have been deleted (you repeat it as many times as amount of errors due to key constraints/dependencies)
It doesn't work for me either when there are multiple foreign key tables.
I found that code that works and does everything you try (delete all tables from your database):
DECLARE #Sql NVARCHAR(500) DECLARE #Cursor CURSOR
SET #Cursor = CURSOR FAST_FORWARD FOR
SELECT DISTINCT sql = 'ALTER TABLE [' + tc2.TABLE_SCHEMA + '].[' + tc2.TABLE_NAME + '] DROP [' + rc1.CONSTRAINT_NAME + '];'
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc1
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc2 ON tc2.CONSTRAINT_NAME =rc1.CONSTRAINT_NAME
OPEN #Cursor FETCH NEXT FROM #Cursor INTO #Sql
WHILE (##FETCH_STATUS = 0)
BEGIN
Exec sp_executesql #Sql
FETCH NEXT FROM #Cursor INTO #Sql
END
CLOSE #Cursor DEALLOCATE #Cursor
GO
EXEC sp_MSforeachtable 'DROP TABLE ?'
GO
You can find the post here. It is the post by Groker.
In SSMS:
Right click the database
Go to "Tasks"
Click "Generate Scripts"
In the "Choose Objects" section, select "Script entire database and all database objects"
In the "Set Scripting Options" section, click the "Advanced" button
On "Script DROP and CREATE" switch "Script CREATE" to "Script DROP" and press OK
Then, either save to file, clipboard, or new query window.
Run script.
Now, this will drop everything, including the database. Make sure to remove the code for the items you don't want dropped. Alternatively, in the "Choose Objects" section, instead of selecting to script entire database just select the items you want to remove.
The accepted answer doesn't support Azure. It uses an undocumented stored procedure "sp_MSforeachtable". If you get an "azure could not find stored procedure 'sp_msforeachtable" error when running or simply want to avoid relying on undocumented features (which can be removed or have their functionality changed at any point) then try the below.
This version ignores the entity framework migration history table "__MigrationHistory" and the "database_firewall_rules" which is an Azure table you will not have permission to delete.
Lightly tested on Azure. Do check to make this this has no undesired effects on your environment.
DECLARE #sql NVARCHAR(2000)
WHILE(EXISTS(SELECT 1 from INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE='FOREIGN KEY'))
BEGIN
SELECT TOP 1 #sql=('ALTER TABLE ' + TABLE_SCHEMA + '.[' + TABLE_NAME + '] DROP CONSTRAINT [' + CONSTRAINT_NAME + ']')
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY'
EXEC(#sql)
PRINT #sql
END
WHILE(EXISTS(SELECT * from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME != '__MigrationHistory' AND TABLE_NAME != 'database_firewall_rules'))
BEGIN
SELECT TOP 1 #sql=('DROP TABLE ' + TABLE_SCHEMA + '.[' + TABLE_NAME + ']')
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME != '__MigrationHistory' AND TABLE_NAME != 'database_firewall_rules'
EXEC(#sql)
PRINT #sql
END
Taken from:
https://edspencer.me.uk/2013/02/25/drop-all-tables-in-a-sql-server-database-azure-friendly/
http://www.sqlservercentral.com/blogs/sqlservertips/2011/10/11/remove-all-foreign-keys/
delete is used for deleting rows from a table. You should use drop table instead.
EXEC sp_msforeachtable 'drop table [?]'
/* Drop all Primary Key constraints */
DECLARE #name VARCHAR(128)
DECLARE #constraint VARCHAR(254)
DECLARE #SQL VARCHAR(254)
SELECT #name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' ORDER BY TABLE_NAME)
WHILE #name IS NOT NULL
BEGIN
SELECT #constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = #name ORDER BY CONSTRAINT_NAME)
WHILE #constraint is not null
BEGIN
SELECT #SQL = 'ALTER TABLE [dbo].[' + RTRIM(#name) +'] DROP CONSTRAINT [' + RTRIM(#constraint)+']'
EXEC (#SQL)
PRINT 'Dropped PK Constraint: ' + #constraint + ' on ' + #name
SELECT #constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' AND CONSTRAINT_NAME <> #constraint AND TABLE_NAME = #name ORDER BY CONSTRAINT_NAME)
END
SELECT #name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' ORDER BY TABLE_NAME)
END
GO
/* Drop all tables */
DECLARE #name VARCHAR(128)
DECLARE #SQL VARCHAR(254)
SELECT #name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 ORDER BY [name])
WHILE #name IS NOT NULL
BEGIN
SELECT #SQL = 'DROP TABLE [dbo].[' + RTRIM(#name) +']'
EXEC (#SQL)
PRINT 'Dropped Table: ' + #name
SELECT #name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 AND [name] > #name ORDER BY [name])
END
GO
You are almost right, use instead:
EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT all'
EXEC sp_msforeachtable 'DROP TABLE ?'
but second line you might need to execute more then once until you stop getting error:
Could not drop object 'dbo.table' because it is referenced by a FOREIGN KEY constraint.
Message:
Command(s) completed successfully.
means that all table were successfully deleted.
Short and sweet:
USE YOUR_DATABASE_NAME
-- Disable all referential integrity constraints
EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
GO
-- Drop all PKs and FKs
declare #sql nvarchar(max)
SELECT #sql = STUFF((SELECT '; ' + 'ALTER TABLE ' + Table_Name +' drop constraint ' + Constraint_Name from Information_Schema.CONSTRAINT_TABLE_USAGE ORDER BY Constraint_Name FOR XML PATH('')),1,1,'')
EXECUTE (#sql)
GO
-- Drop all tables
EXEC sp_MSforeachtable 'DROP TABLE ?'
GO
Spot on!!
You can use below query to remove all the tables from database
EXEC sp_MSforeachtable #command1 = "DROP TABLE ?"
Happy coding !
Seems the command should be without the square blanket
EXEC sp_msforeachtable 'drop table ?'
The fasted way is:
New Database Diagrams
Add all table
Ctrl + A to select all
Right Click "Remove from Database"
Ctrl + S to save
Enjoy
For me, the easiest way:
--First delete all constraints
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'
ALTER TABLE ' + QUOTENAME(s.name) + N'.'
+ QUOTENAME(t.name) + N' DROP CONSTRAINT '
+ QUOTENAME(c.name) + ';'
FROM sys.objects AS c
INNER JOIN sys.tables AS t
ON c.parent_object_id = t.[object_id]
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE c.[type] IN ('D','C','F','PK','UQ')
ORDER BY c.[type];
EXEC sys.sp_executesql #sql;
-- Then drop all tables
exec sp_MSforeachtable 'DROP TABLE ?'
Azure SQL + tables (with constraints) in a different schema than dbo + ipv6_database_firewall_rules condition.
This is a little extension for https://stackoverflow.com/a/43128914/4510954 answer.
DECLARE #sql NVARCHAR(2000)
WHILE(EXISTS(SELECT 1 from INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE='FOREIGN KEY'))
BEGIN
SELECT TOP 1 #sql=('ALTER TABLE ' + CONSTRAINT_SCHEMA + '.[' + TABLE_NAME + '] DROP CONSTRAINT [' + CONSTRAINT_NAME + ']')
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY'
EXEC(#sql)
PRINT #sql
END
WHILE(EXISTS(SELECT * from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME != '__MigrationHistory' AND TABLE_NAME != 'database_firewall_rules' AND TABLE_NAME != 'ipv6_database_firewall_rules'))
BEGIN
SELECT TOP 1 #sql=('DROP TABLE ' + CONSTRAINT_SCHEMA + '.[' + TABLE_NAME + ']')
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME != '__MigrationHistory' AND TABLE_NAME != 'database_firewall_rules'
EXEC(#sql)
PRINT #sql
END
How about dropping the entire database and then creating it again? This works for me.
DROP DATABASE mydb;
CREATE DATABASE mydb;
For Temporal Tables it is a bit more complicated due to the fact there may be some foreign keys and also exception:
Drop table operation failed on table XXX because it is not a supported operation on system-versioned temporal tables
What you can use is:
-- Disable constraints (foreign keys)
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
GO
-- Disable system versioning (temporial tables)
EXEC sp_MSForEachTable '
IF OBJECTPROPERTY(object_id(''?''), ''TableTemporalType'') = 2
ALTER TABLE ? SET (SYSTEM_VERSIONING = OFF)
'
GO
-- Removing tables
EXEC sp_MSForEachTable 'DROP TABLE ?'
GO
I know this is an old post now but I have tried all the answers on here on a multitude of databases and I have found they all work sometimes but not all of the time for various (I can only assume) quirks of SQL Server.
Eventually I came up with this. I have tested this everywhere (generally speaking) I can and it works (without any hidden store procedures).
For note mostly on SQL Server 2014. (but most of the other versions I tried it also seems to worked fine).
I have tried while loops and nulls etc etc, cursors and various other forms but they always seem to fail on some databases but not others for no obvious reason.
Getting a count and using that to iterate always seems to work on everything Ive tested.
USE [****YOUR_DATABASE****]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- Drop all referential integrity constraints --
-- Drop all Primary Key constraints. --
DECLARE #sql NVARCHAR(296)
DECLARE #table_name VARCHAR(128)
DECLARE #constraint_name VARCHAR(128)
SET #constraint_name = ''
DECLARE #row_number INT
SELECT #row_number = Count(*) FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc1
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc2 ON tc2.CONSTRAINT_NAME = rc1.CONSTRAINT_NAME
WHILE #row_number > 0
BEGIN
BEGIN
SELECT TOP 1 #table_name = tc2.TABLE_NAME, #constraint_name = rc1.CONSTRAINT_NAME FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc1
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc2 ON tc2.CONSTRAINT_NAME = rc1.CONSTRAINT_NAME
AND rc1.CONSTRAINT_NAME > #constraint_name
ORDER BY rc1.CONSTRAINT_NAME
SELECT #sql = 'ALTER TABLE [dbo].[' + RTRIM(#table_name) +'] DROP CONSTRAINT [' + RTRIM(#constraint_name)+']'
EXEC (#sql)
PRINT 'Dropped Constraint: ' + #constraint_name + ' on ' + #table_name
SET #row_number = #row_number - 1
END
END
GO
-- Drop all tables --
DECLARE #sql NVARCHAR(156)
DECLARE #name VARCHAR(128)
SET #name = ''
DECLARE #row_number INT
SELECT #row_number = Count(*) FROM sysobjects WHERE [type] = 'U' AND category = 0
WHILE #row_number > 0
BEGIN
SELECT #name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 AND [name] > #name ORDER BY [name])
SELECT #sql = 'DROP TABLE [dbo].[' + RTRIM(#name) +']'
EXEC (#sql)
PRINT 'Dropped Table: ' + #name
SET #row_number = #row_number - 1
END
GO
sp_msforeachtable is not available in Azure SQL
For Azure SQL:
This query will drop Foreign Key constraints
DECLARE #Name VARCHAR(200)
DECLARE #Constraint VARCHAR(300)
DECLARE #SQL VARCHAR(300)
SELECT #Name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME)
WHILE #Name is not null
BEGIN
SELECT #Constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND TABLE_NAME = #Name ORDER BY CONSTRAINT_NAME)
WHILE #Constraint IS NOT NULL
BEGIN
SELECT #SQL = 'ALTER TABLE [dbo].[' + RTRIM(#Name) +'] DROP CONSTRAINT [' + RTRIM(#Constraint) +']'
EXEC (#SQL)
PRINT 'Dropped FK Constraint: ' + #Constraint + ' on ' + #Name
SELECT #Constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND CONSTRAINT_NAME <> #Constraint AND TABLE_NAME = #Name ORDER BY CONSTRAINT_NAME)
END
SELECT #Name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME)
END
GO
This will drop all the tables from the database
DECLARE #Name VARCHAR(200)
DECLARE #SQL VARCHAR(300)
SELECT #Name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 ORDER BY [name])
WHILE #Name IS NOT NULL
BEGIN
SELECT #SQL = 'DROP TABLE [dbo].[' + RTRIM(#name) +']' /*here you can change schema if it is different from dbo*/
EXEC (#SQL)
PRINT 'Dropped Table: ' + #Name
SELECT #Name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 AND [name] > #Name ORDER BY [name])
END
GO

Script for rebuilding and reindexing the fragmented index?

Can anyone provide the script for rebuilding and re-indexing the fragmented index when 'avg_fragmentation_in_percent' exceeds certain limits (better if cursor is not used)?
To rebuild use:
ALTER INDEX __NAME_OF_INDEX__ ON __NAME_OF_TABLE__ REBUILD
or to reorganize use:
ALTER INDEX __NAME_OF_INDEX__ ON __NAME_OF_TABLE__ REORGANIZE
Reorganizing should be used at lower (<30%) fragmentations but only rebuilding (which is heavier to the database) cuts the fragmentation down to 0%.
For further information see https://msdn.microsoft.com/en-us/library/ms189858.aspx
Two solutions: One simple and one more advanced.
Introduction
There are two solutions available to you depending on the severity of your issue
Replace with your own values, as follows:
Replace XXXMYINDEXXXX with the name of an index.
Replace XXXMYTABLEXXX with the name of a table.
Replace XXXDATABASENAMEXXX with the name of a database.
Solution 1. Indexing
Rebuild all indexes for a table in offline mode
ALTER INDEX ALL ON XXXMYTABLEXXX REBUILD
Rebuild one specified index for a table in offline mode
ALTER INDEX XXXMYINDEXXXX ON XXXMYTABLEXXX REBUILD
Solution 2. Fragmentation
Fragmentation is an issue in tables that regularly have entries both added and removed.
Check fragmentation percentage
SELECT
ips.[index_id] ,
idx.[name] ,
ips.[avg_fragmentation_in_percent]
FROM
sys.dm_db_index_physical_stats(DB_ID(N'XXXMYDATABASEXXX'), OBJECT_ID(N'XXXMYTABLEXXX'), NULL, NULL, NULL) AS [ips]
INNER JOIN sys.indexes AS [idx] ON [ips].[object_id] = [idx].[object_id] AND [ips].[index_id] = [idx].[index_id]
Fragmentation 5..30%
If the fragmentation value is greater than 5%, but less than 30% then it is worth reorganising indexes.
Reorganise all indexes for a table
ALTER INDEX ALL ON XXXMYTABLEXXX REORGANIZE
Reorganise one specified index for a table
ALTER INDEX XXXMYINDEXXXX ON XXXMYTABLEXXX REORGANIZE
Fragmentation 30%+
If the fragmentation value is 30% or greater then it is worth rebuilding then indexes in online mode.
Rebuild all indexes in online mode for a table
ALTER INDEX ALL ON XXXMYTABLEXXX REBUILD WITH (ONLINE = ON)
Rebuild one specified index in online mode for a table
ALTER INDEX XXXMYINDEXXXX ON XXXMYTABLEXXX REBUILD WITH (ONLINE = ON)
Query for REBUILD/REORGANIZE Indexes
30%<= Rebuild
5%<= Reorganize
5%> do nothing
Query:
SELECT OBJECT_NAME(ind.OBJECT_ID) AS TableName,
ind.name AS IndexName, indexstats.index_type_desc AS IndexType,
indexstats.avg_fragmentation_in_percent,
'ALTER INDEX ' + QUOTENAME(ind.name) + ' ON ' +QUOTENAME(object_name(ind.object_id)) +
CASE WHEN indexstats.avg_fragmentation_in_percent>30 THEN ' REBUILD '
WHEN indexstats.avg_fragmentation_in_percent>=5 THEN 'REORGANIZE'
ELSE NULL END as [SQLQuery] -- if <5 not required, so no query needed
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) indexstats
INNER JOIN sys.indexes ind ON ind.object_id = indexstats.object_id
AND ind.index_id = indexstats.index_id
WHERE
--indexstats.avg_fragmentation_in_percent , e.g. >10, you can specify any number in percent
ind.Name is not null
ORDER BY indexstats.avg_fragmentation_in_percent DESC
Output
TableName IndexName IndexType avg_fragmentation_in_percent SQLQuery
--------------------------------------------------------------------------------------- ------------------------------------------------------
Table1 PK_Table1 CLUSTERED INDEX 75 ALTER INDEX [PK_Table1] ON [Table1] REBUILD
Table1 IX_Table1_col1_col2 NONCLUSTERED INDEX 66,6666666666667 ALTER INDEX [IX_Table1_col1_col2] ON [Table1] REBUILD
Table2 IX_Table2_ NONCLUSTERED INDEX 10 ALTER INDEX [IX_Table2_] ON [Table2] REORGANIZE
Table2 IX_Table2_ NONCLUSTERED INDEX 3 NULL
Here is the modified script which i took from http://www.foliotek.com/devblog/sql-server-optimization-with-index-rebuilding which i found useful to post here.
Although it uses a cursor and i know what is the main problem with cursors it can be easily converted to a cursor-less version.
It is well-documented and you can easily read through it and modify to your needs.
IF OBJECT_ID('tempdb..#work_to_do') IS NOT NULL
DROP TABLE tempdb..#work_to_do
BEGIN TRY
--BEGIN TRAN
use yourdbname
-- Ensure a USE 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 #pagecount INT;
DECLARE #command NVARCHAR(4000);
DECLARE #page_count_minimum SMALLINT
SET #page_count_minimum = 50
DECLARE #fragmentation_minimum FLOAT
SET #fragmentation_minimum = 30.0
-- 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 ,
page_count AS page_count
INTO #work_to_do
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL,
'LIMITED')
WHERE avg_fragmentation_in_percent > #fragmentation_minimum
AND index_id > 0
AND page_count > #page_count_minimum;
IF CURSOR_STATUS('global', 'partitions') >= -1
BEGIN
PRINT 'partitions CURSOR DELETED' ;
CLOSE partitions
DEALLOCATE partitions
END
-- Declare the cursor for the list of partitions to be processed.
DECLARE partitions CURSOR LOCAL
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, #pagecount;
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;
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 (#command); //uncomment for testing
PRINT N'Rebuilding index ' + #indexname + ' on table '
+ #objectname;
PRINT N' Fragmentation: ' + CAST(#frag AS VARCHAR(15));
PRINT N' Page Count: ' + CAST(#pagecount AS VARCHAR(15));
PRINT N' ';
END;
-- Close and deallocate the cursor.
CLOSE partitions;
DEALLOCATE partitions;
-- Drop the temporary table.
DROP TABLE #work_to_do;
--COMMIT TRAN
END TRY
BEGIN CATCH
--ROLLBACK TRAN
PRINT 'ERROR ENCOUNTERED:' + ERROR_MESSAGE()
END CATCH
The real answer, in 2016 and 2017, is: Use Ola Hallengren's scripts:
https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html
That is all any of us need to know or bother with, at this point in our mutual evolution.
I have found the following script is very good at maintaining indexes, you can have this scheduled to run nightly or whatever other timeframe you wish.
http://sqlfool.com/2011/06/index-defrag-script-v4-1/

In SQL Server 2005, can I do a cascade delete without setting the property on my tables?

I have a database full of customer data. It's so big that it's really cumbersome to operate on, and I'd rather just slim it down to 10% of the customers, which is plenty for development. I have an awful lot of tables and I don't want to alter them all with "ON DELETE CASCADE", especially because this is a one-time deal.
Can I do a delete operation that cascades through all my tables without setting them up first? If not, what is my best option?
Combining your advice and a script I found online, I made a procedure that will produce SQL you can run to perform a cascaded delete regardless of ON DELETE CASCADE. It was probably a big waste of time, but I had a good time writing it. An advantage of doing it this way is, you can put a GO statement between each line, and it doesn't have to be one big transaction. The original was a recursive procedure; this one unrolls the recursion into a stack table.
create procedure usp_delete_cascade (
#base_table_name varchar(200), #base_criteria nvarchar(1000)
)
as begin
-- Adapted from http://www.sqlteam.com/article/performing-a-cascade-delete-in-sql-server-7
-- Expects the name of a table, and a conditional for selecting rows
-- within that table that you want deleted.
-- Produces SQL that, when run, deletes all table rows referencing the ones
-- you initially selected, cascading into any number of tables,
-- without the need for "ON DELETE CASCADE".
-- Does not appear to work with self-referencing tables, but it will
-- delete everything beneath them.
-- To make it easy on the server, put a "GO" statement between each line.
declare #to_delete table (
id int identity(1, 1) primary key not null,
criteria nvarchar(1000) not null,
table_name varchar(200) not null,
processed bit not null,
delete_sql varchar(1000)
)
insert into #to_delete (criteria, table_name, processed) values (#base_criteria, #base_table_name, 0)
declare #id int, #criteria nvarchar(1000), #table_name varchar(200)
while exists(select 1 from #to_delete where processed = 0) begin
select top 1 #id = id, #criteria = criteria, #table_name = table_name from #to_delete where processed = 0 order by id desc
insert into #to_delete (criteria, table_name, processed)
select referencing_column.name + ' in (select [' + referenced_column.name + '] from [' + #table_name +'] where ' + #criteria + ')',
referencing_table.name,
0
from sys.foreign_key_columns fk
inner join sys.columns referencing_column on fk.parent_object_id = referencing_column.object_id
and fk.parent_column_id = referencing_column.column_id
inner join sys.columns referenced_column on fk.referenced_object_id = referenced_column.object_id
and fk.referenced_column_id = referenced_column.column_id
inner join sys.objects referencing_table on fk.parent_object_id = referencing_table.object_id
inner join sys.objects referenced_table on fk.referenced_object_id = referenced_table.object_id
inner join sys.objects constraint_object on fk.constraint_object_id = constraint_object.object_id
where referenced_table.name = #table_name
and referencing_table.name != referenced_table.name
update #to_delete set
processed = 1
where id = #id
end
select 'print ''deleting from ' + table_name + '...''; delete from [' + table_name + '] where ' + criteria from #to_delete order by id desc
end
exec usp_delete_cascade 'root_table_name', 'id = 123'
Here's a version of the accepted answer optimised for sparsely populated data models. It checks for the existence of data in a FK chain before adding it to the deletion list. I use it to clean up test data.
Don't use it in an active transactional db- it will hold locks way too long.
/*
-- ============================================================================
-- Purpose: Performs a cascading hard-delete.
-- Not for use on an active transactional database- it holds locks for too long.
-- (http://stackoverflow.com/questions/116968/in-sql-server-2005-can-i-do-a-cascade-delete-without-setting-the-property-on-my)
-- eg:
exec dbo.hp_Common_Delete 'tblConsumer', 'Surname = ''TestDxOverdueOneReviewWm''', 1
-- ============================================================================
*/
create proc [dbo].[hp_Common_Delete]
(
#TableName sysname,
#Where nvarchar(4000), -- Shouldn't include 'where' keyword, e.g. Surname = 'smith', NOT where Surname = 'smith'
#IsDebug bit = 0
)
as
set nocount on
begin try
-- Prepare tables to store deletion criteria.
-- #tmp_to_delete stores criteria that is tested for results before being added to #to_delete
create table #to_delete
(
id int identity(1, 1) primary key not null,
criteria nvarchar(4000) not null,
table_name sysname not null,
processed bit not null default(0)
)
create table #tmp_to_delete
(
id int primary key identity(1,1),
criteria nvarchar(4000) not null,
table_name sysname not null
)
-- Open a transaction (it'll be a long one- don't use this on production!)
-- We need a transaction around criteria generation because we only
-- retain criteria that has rows in the db, and we don't want that to change under us.
begin tran
-- If the top-level table meets the deletion criteria, add it
declare #Sql nvarchar(4000)
set #Sql = 'if exists(select top(1) * from ' + #TableName + ' where ' + #Where + ')
insert #to_delete (criteria, table_name) values (''' + replace(#Where, '''', '''''') + ''', ''' + #TableName + ''')'
exec (#Sql)
-- Loop over deletion table, walking foreign keys to generate delete targets
declare #id int, #tmp_id int, #criteria nvarchar(4000), #new_criteria nvarchar(4000), #table_name sysname, #new_table_name sysname
while exists(select 1 from #to_delete where processed = 0)
begin
-- Grab table/criteria to work on
select top(1) #id = id,
#criteria = criteria,
#table_name = table_name
from #to_delete
where processed = 0
order by id desc
-- Insert all immediate child tables into a temp table for processing
insert #tmp_to_delete
select referencing_column.name + ' in (select [' + referenced_column.name + '] from [' + #table_name +'] where ' + #criteria + ')',
referencing_table.name
from sys.foreign_key_columns fk
inner join sys.columns referencing_column on fk.parent_object_id = referencing_column.object_id
and fk.parent_column_id = referencing_column.column_id
inner join sys.columns referenced_column on fk.referenced_object_id = referenced_column.object_id
and fk.referenced_column_id = referenced_column.column_id
inner join sys.objects referencing_table on fk.parent_object_id = referencing_table.object_id
inner join sys.objects referenced_table on fk.referenced_object_id = referenced_table.object_id
inner join sys.objects constraint_object on fk.constraint_object_id = constraint_object.object_id
where referenced_table.name = #table_name
and referencing_table.name != referenced_table.name
-- Loop on child table criteria, and insert them into delete table if they have records in the db
select #tmp_id = max(id) from #tmp_to_delete
while (#tmp_id >= 1)
begin
select #new_criteria = criteria, #new_table_name = table_name from #tmp_to_delete where id = #tmp_id
set #Sql = 'if exists(select top(1) * from ' + #new_table_name + ' where ' + #new_criteria + ')
insert #to_delete (criteria, table_name) values (''' + replace(#new_criteria, '''', '''''') + ''', ''' + #new_table_name + ''')'
exec (#Sql)
set #tmp_id = #tmp_id - 1
end
truncate table #tmp_to_delete
-- Move to next record
update #to_delete
set processed = 1
where id = #id
end
-- We have a list of all tables requiring deletion. Actually delete now.
select #id = max(id) from #to_delete
while (#id >= 1)
begin
select #criteria = criteria, #table_name = table_name from #to_delete where id = #id
set #Sql = 'delete from [' + #table_name + '] where ' + #criteria
if (#IsDebug = 1) print #Sql
exec (#Sql)
-- Next record
set #id = #id - 1
end
commit
end try
begin catch
-- Any error results in a rollback of the entire job
if (##trancount > 0) rollback
declare #message nvarchar(2047), #errorProcedure nvarchar(126), #errorMessage nvarchar(2048), #errorNumber int, #errorSeverity int, #errorState int, #errorLine int
select #errorProcedure = isnull(error_procedure(), N'hp_Common_Delete'),
#errorMessage = isnull(error_message(), N'hp_Common_Delete unable to determine error message'),
#errorNumber = error_number(), #errorSeverity = error_severity(), #errorState = error_state(), #errorLine = error_line()
-- Prepare error information as it would be output in SQL Mgt Studio
declare #event nvarchar(2047)
select #event = 'Msg ' + isnull(cast(#errorNumber as varchar), 'null') +
', Level ' + isnull(cast(#errorSeverity as varchar), 'null') +
', State ' + isnull(cast(#errorState as varchar), 'null') +
', Procedure ' + isnull(#errorProcedure, 'null') +
', Line ' + isnull(cast(#errorLine as varchar), 'null') +
': ' + isnull(#errorMessage, '#ErrorMessage null')
print #event
-- Re-raise error to ensure admin/job runners understand there was a failure
raiserror(#errorMessage, #errorSeverity, #errorState)
end catch
Unless you want to maintain all related queries as proposed by Chris, the ON DELETE CASCADE is by far the quickest and the most direct solution. And if you don't want it to be permanent, why don't you have some T-SQL code that will switch this option on and off like here
remove the original Tbl_A_MyFK constraint (without the ON DELETE CASCADE)
ALTER TABLE Tbl_A DROP CONSTRAINT Tbl_A_MyFK
set the constraint Tbl_A_MyFK with the ON DELETE CASCADE
ALTER TABLE Tbl_A ADD CONSTRAINT Tbl_A_MyFK FOREIGN KEY (MyFK) REFERENCES Tbl_B(Column) ON DELETE CASCADE
Here you can do your delete
DELETE FROM Tbl_A WHERE ...
drop your constraint Tbl_A_MyFK
ALTER TABLE Tbl_A DROP CONSTRAINT Tbl_A_MyFK
set the constraint Tbl_A_MyFK without the ON DELETE CASCADE
ALTER TABLE Tbl_A ADD CONSTRAINT Tbl_A_MyFK FOREIGN KEY (MyFK) REFERENCES (Tbl_B)
Go into SQL Server Management Studio and right-click the database. Select Tasks->Generate Scripts. Click Next twice. On the Options window choose set it to generate CREATE statements only, and put everything to False except for the Foreign Keys. Click Next. Select Tables and Click Next again. Click the "Select All" button and click Next then Finish and send the script to your choice of a query window or file (don't use the clipboard, since it might be a big script). Now remove all of the script that adds the tables and you should be left with a script to create your foreign keys.
Make a copy of that script because it is how you'll restore your database to its current state. Use a search and replace to add the ON DELETE CASCADE to the end of each constraint. This might vary depending on how your FKs are currently set up and you might need to do some manual editing.
Repeat the script generation, but this time set it to generate DROP statements only. Be sure to manually remove the table drops that are generated. Run the drops, then run your edited creates to make them all cascade on delete. Do your deletes, run the drop script again and then run the script that you saved off at the start.
Also - MAKE A BACKUP OF YOUR DB FIRST! Even if it's just a dev database, it will save you some headache if part of the script isn't quite right.
Hope this helps!
BTW - you should definitely do some testing with your full test data as another poster suggested, but I can see why you might not need that for initial development. Just don't forget to include that as part of QA at some point.
Kevin post is incomplete, his t-sql sp only prints the command, to execute these command, before last end add this
DECLARE #commandText VARCHAR(8000)
DECLARE curDeletes CURSOR FOR
select 'delete from [' + table_name + '] where ' + criteria from #to_delete order by id desc
OPEN curDeletes
FETCH NEXT FROM curDeletes
INTO
#commandText
WHILE(##FETCH_STATUS=0)
BEGIN
EXEC (#commandText)
FETCH NEXT FROM curDeletes INTO #commandText
END
CLOSE curDeletes
DEALLOCATE curDeletes
I usually just hand write the queries to delete the records I don't want and save that as a .sql file for future reference. The pseudocode is:
select id's of records from the main table that I want to delete into a temp table
write a delete query for each related table which joins to the temp table.
write a delete query for the main table joining to my temp table.
My suggestion is to go ahead and write a script that will add the on delete cascade to each relationship in the database while exporting a list of modified relationships. Then you can reverse the process and remove the on delete cascade command on each table in the list.
Personally if you are going to leave the records in production, I would also leave them in development. Otherwise you may write code that works fine when the recordset is small but times out when faced with the real recordset.
But if you are determined to do this, I would copy the id field of the records you want to dete from the main table first to a work table. Then I would take each related table and write a delete joining to that worktable to only delete those records. Finish up with the parent table. Make sure this ia written in a script and saved so the next time you want to do a similar thing to your test data, you can easily run it without having to figure out what are the reated tables that need records deleted from them.
Taking the accepted answer a bit further, I had the need to do this across tables in different schemas. I have updated the script to include schema in the outputted delete scripts.
CREATE PROCEDURE usp_delete_cascade (
#base_table_schema varchar(100), #base_table_name varchar(200), #base_criteria nvarchar(1000)
)
as begin
-- Expects the name of a table, and a conditional for selecting rows
-- within that table that you want deleted.
-- Produces SQL that, when run, deletes all table rows referencing the ones
-- you initially selected, cascading into any number of tables,
-- without the need for "ON DELETE CASCADE".
-- Does not appear to work with self-referencing tables, but it will
-- delete everything beneath them.
-- To make it easy on the server, put a "GO" statement between each line.
declare #to_delete table (
id int identity(1, 1) primary key not null,
criteria nvarchar(1000) not null,
table_schema varchar(100),
table_name varchar(200) not null,
processed bit not null,
delete_sql varchar(1000)
)
insert into #to_delete (criteria, table_schema, table_name, processed) values (#base_criteria, #base_table_schema, #base_table_name, 0)
declare #id int, #criteria nvarchar(1000), #table_name varchar(200), #table_schema varchar(100)
while exists(select 1 from #to_delete where processed = 0) begin
select top 1 #id = id, #criteria = criteria, #table_name = table_name, #table_schema = table_schema from #to_delete where processed = 0 order by id desc
insert into #to_delete (criteria, table_schema, table_name, processed)
select referencing_column.name + ' in (select [' + referenced_column.name + '] from [' + #table_schema + '].[' + #table_name +'] where ' + #criteria + ')',
schematable.name,
referencing_table.name,
0
from sys.foreign_key_columns fk
inner join sys.columns referencing_column on fk.parent_object_id = referencing_column.object_id
and fk.parent_column_id = referencing_column.column_id
inner join sys.columns referenced_column on fk.referenced_object_id = referenced_column.object_id
and fk.referenced_column_id = referenced_column.column_id
inner join sys.objects referencing_table on fk.parent_object_id = referencing_table.object_id
inner join sys.schemas schematable on referencing_table.schema_id = schematable.schema_id
inner join sys.objects referenced_table on fk.referenced_object_id = referenced_table.object_id
inner join sys.objects constraint_object on fk.constraint_object_id = constraint_object.object_id
where referenced_table.name = #table_name
and referencing_table.name != referenced_table.name
update #to_delete set
processed = 1
where id = #id
end
select 'print ''deleting from ' + table_name + '...''; delete from [' + table_schema + '].[' + table_name + '] where ' + criteria from #to_delete order by id desc
end
exec usp_delete_cascade 'schema', 'RootTable', 'Id = 123'
exec usp_delete_cascade 'schema', 'RootTable', 'GuidId = ''A7202F84-FA57-4355-B499-1F8718E29058'''
Expansion of croisharp's answer to take triggers into consideration, i.e. schema-aware solution that disables all affecting triggers, deletes rows, and enables the triggers.
CREATE PROCEDURE usp_delete_cascade (
#base_table_schema varchar(100),
#base_table_name varchar(200),
#base_criteria nvarchar(1000)
)
as begin
-- Expects the name of a table, and a conditional for selecting rows
-- within that table that you want deleted.
-- Produces SQL that, when run, deletes all table rows referencing the ones
-- you initially selected, cascading into any number of tables,
-- without the need for "ON DELETE CASCADE".
-- Does not appear to work with self-referencing tables, but it will
-- delete everything beneath them.
-- To make it easy on the server, put a "GO" statement between each line.
declare #to_delete table (
id int identity(1, 1) primary key not null,
criteria nvarchar(1000) not null,
table_schema varchar(100),
table_name varchar(200) not null,
processed bit not null,
delete_sql varchar(1000)
)
insert into #to_delete (criteria, table_schema, table_name, processed) values (#base_criteria, #base_table_schema, #base_table_name, 0)
declare #id int, #criteria nvarchar(1000), #table_name varchar(200), #table_schema varchar(100)
while exists(select 1 from #to_delete where processed = 0) begin
select top 1 #id = id, #criteria = criteria, #table_name = table_name, #table_schema = table_schema from #to_delete where processed = 0 order by id desc
insert into #to_delete (criteria, table_schema, table_name, processed)
select referencing_column.name + ' in (select [' + referenced_column.name + '] from [' + #table_schema + '].[' + #table_name +'] where ' + #criteria + ')',
schematable.name,
referencing_table.name,
0
from sys.foreign_key_columns fk
inner join sys.columns referencing_column on fk.parent_object_id = referencing_column.object_id
and fk.parent_column_id = referencing_column.column_id
inner join sys.columns referenced_column on fk.referenced_object_id = referenced_column.object_id
and fk.referenced_column_id = referenced_column.column_id
inner join sys.objects referencing_table on fk.parent_object_id = referencing_table.object_id
inner join sys.schemas schematable on referencing_table.schema_id = schematable.schema_id
inner join sys.objects referenced_table on fk.referenced_object_id = referenced_table.object_id
inner join sys.objects constraint_object on fk.constraint_object_id = constraint_object.object_id
where referenced_table.name = #table_name
and referencing_table.name != referenced_table.name
update #to_delete set
processed = 1
where id = #id
end
select 'print ''deleting from ' + table_name + '...''; delete from [' + table_schema + '].[' + table_name + '] where ' + criteria from #to_delete order by id desc
DECLARE #commandText VARCHAR(8000), #triggerOn VARCHAR(8000), #triggerOff VARCHAR(8000)
DECLARE curDeletes CURSOR FOR
select
'DELETE FROM [' + table_schema + '].[' + table_name + '] WHERE ' + criteria,
'ALTER TABLE [' + table_schema + '].[' + table_name + '] DISABLE TRIGGER ALL',
'ALTER TABLE [' + table_schema + '].[' + table_name + '] ENABLE TRIGGER ALL'
from #to_delete order by id desc
OPEN curDeletes
FETCH NEXT FROM curDeletes INTO #commandText, #triggerOff, #triggerOn
WHILE(##FETCH_STATUS=0)
BEGIN
EXEC (#triggerOff)
EXEC (#commandText)
EXEC (#triggerOn)
FETCH NEXT FROM curDeletes INTO #commandText, #triggerOff, #triggerOn
END
CLOSE curDeletes
DEALLOCATE curDeletes
end
after select you have to build and execute the actual delete
declare #deleteSql nvarchar(1200)
declare delete_cursor cursor for
select table_name, criteria
from #to_delete
order by id desc
open delete_cursor
fetch next from delete_cursor
into #table_name, #criteria
while ##fetch_status = 0
begin
select #deleteSql = 'delete from ' + #table_name + ' where ' + #criteria
--print #deleteSql
-- exec sp_execute #deleteSql
EXEC SP_EXECUTESQL #deleteSql
fetch next from delete_cursor
into #table_name, #criteria
end
close delete_cursor
deallocate delete_cursor
Post here a script that will work with foreign keys contain more than one column.
create procedure usp_delete_cascade (
#TableName varchar(200), #Where nvarchar(1000)
) as begin
declare #to_delete table (
id int identity(1, 1) primary key not null,
criteria nvarchar(1000) not null,
table_name varchar(200) not null,
processed bit not null default(0),
delete_sql varchar(1000)
)
DECLARE #MyCursor CURSOR
declare #referencing_column_name varchar(1000)
declare #referencing_table_name varchar(1000)
declare #Sql nvarchar(4000)
insert into #to_delete (criteria, table_name) values ('', #TableName)
declare #id int, #criteria nvarchar(1000), #table_name varchar(200)
while exists(select 1 from #to_delete where processed = 0) begin
select top 1 #id = id, #criteria = criteria, #table_name = table_name from #to_delete where processed = 0 order by id desc
SET #MyCursor = CURSOR FAST_FORWARD
FOR
select referencing_column.name as column_name,
referencing_table.name as table_name
from sys.foreign_key_columns fk
inner join sys.columns referencing_column on fk.parent_object_id = referencing_column.object_id
and fk.parent_column_id = referencing_column.column_id
inner join sys.columns referenced_column on fk.referenced_object_id = referenced_column.object_id
and fk.referenced_column_id = referenced_column.column_id
inner join sys.objects referencing_table on fk.parent_object_id = referencing_table.object_id
inner join sys.objects referenced_table on fk.referenced_object_id = referenced_table.object_id
inner join sys.objects constraint_object on fk.constraint_object_id = constraint_object.object_id
where referenced_table.name = #table_name
and referencing_table.name != referenced_table.name
OPEN #MyCursor
FETCH NEXT FROM #MYCursor
INTO #referencing_column_name, #referencing_table_name
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #referencing_column_name
PRINT #referencing_table_name
update #to_delete set criteria = criteria + ' AND '+#table_name+'.'+#referencing_column_name+'='+ #referencing_table_name+'.'+#referencing_column_name
where table_name = #referencing_table_name
if(##ROWCOUNT = 0)
BEGIN
--if(#id <> 1)
--BEGIN
insert into #to_delete (criteria, table_name)
VALUES( ' LEFT JOIN '+#table_name+' ON '+#table_name+'.'+#referencing_column_name+'='+ #referencing_table_name+'.'+#referencing_column_name+ #criteria,
#referencing_table_name
)
--END
--ELSE
--BEGIN
--insert into #to_delete (criteria, table_name)
--VALUES( ' LEFT JOIN '+#table_name+' ON '+#table_name+'.'+#referencing_column_name+'='+ #referencing_table_name+'.'+#referencing_column_name,
--#referencing_table_name
--)
--END
END
FETCH NEXT FROM #MYCursor
INTO #referencing_column_name, #referencing_table_name
END
CLOSE #MyCursor
DEALLOCATE #MyCursor
update #to_delete set
processed = 1
where id = #id
end
--select 'print ''deleting from ' + table_name + '...''; delete from [' + table_name + '] where ' + criteria from #to_delete order by id desc
--select id, table_name, criteria, #Where from #to_delete order by id desc
select #id = max(id) from #to_delete
while (#id >= 1)
begin
select #criteria = criteria, #table_name = table_name from #to_delete where id = #id
set #Sql = 'delete [' + #table_name + '] from [' + #table_name + '] ' + #criteria+' WHERE '+#Where
exec (#Sql)
PRINT #Sql
-- Next record
set #id = #id - 1
end
end
This script has two issues:
1. You must indicate the condition 1=1 in order to delete all table base.
2. This creates the direct relations with the base table only. If the final table has another table parent relation, the the delete fail
DELETE FROM [dbo].[table2] WHERE TableID in (select [ID] from [dbo].[table3] where 1=1)
If table2 has a parent relation table1

Resources