SQL Server : ALTER COLUMN only if existing column has a smaller length - sql-server

I am trying to create a script which will alter a column only if it is not of the correct size. Something like this.
IF NOT EXISTS (SELECT COL_LENGTH('dbo.TSC701_OCT_CONTEXT', 'sql_stmt') = 1000 )
BEGIN
ALTER TABLE dbo.TSC701_OCT_CONTEXT
ALTER COLUMN sql_stmt VARCHAR(1000)
END
Sorry for the screwed up syntax

You actually do not need the SELECT statement in the IF clause.
IF (COL_LENGTH('dbo.TSC701_OCT_CONTEXT', 'sql_stmt') < 1000)
BEGIN
ALTER TABLE [Table Name]
ALTER COLUMN [Column Name] varchar(1000) null
END
Also, if your column needs to be NOT NULL or is already set to NOT NULL, you will want to switch the null from above out for NOT NULL.
If your goal is to check every column in a database, you can use the below code. Just be aware this could be very slow and could cause unintended errors and issues. I would backup whatever database you plan to use this on immediately before running it and I would also run it on a small test database or one table to see if it does what you want without taking too long.
DECLARE #TABLE_CATALOG NVARCHAR(128), #TABLE_SCHEMA NVARCHAR(128), #TABLE_NAME NVARCHAR(128), #COLUMN_NAME NVARCHAR(128), #DATA_TYPE NVARCHAR(128)
DECLARE #IS_NULLABLE NVARCHAR(3)
DECLARE #sql NVARCHAR(1000)
WHILE EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG='teoe' AND CHARACTER_MAXIMUM_LENGTH < 1000)
BEGIN
SELECT TOP 1
#TABLE_CATALOG = TABLE_CATALOG,
#TABLE_SCHEMA = TABLE_SCHEMA,
#TABLE_NAME = TABLE_NAME,
#COLUMN_NAME = COLUMN_NAME,
#IS_NULLABLE = IS_NULLABLE,
#DATA_TYPE = DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG='teoe'
AND CHARACTER_MAXIMUM_LENGTH < 1000
SET #sql = 'ALTER TABLE [' + #TABLE_CATALOG + '].' + #TABLE_SCHEMA + '.[' + #TABLE_NAME + '] ALTER COLUMN [' + #COLUMN_NAME + '] '+ #DATA_TYPE + '(1000) ' +
CASE WHEN #IS_NULLABLE = 'YES' THEN 'NULL' ELSE 'NOT NULL' END
EXECUTE #sql
BREAK
END

You can try it like this.
IF (SELECT CASE WHEN COL_LENGTH('dbo.TSC701_OCT_CONTEXT', 'sql_stmt') <> 1000 THEN 1 END) = 1
BEGIN ALTER TABLE dbo.TSC701_OCT_CONTEXT alter column sql_stmt varchar(1000) END

IF 1000 = (SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG='Database Name' AND TABLE_NAME='Table Name' AND COLUMN_NAME='Column Name')
BEGIN
ALTER TABLE [Table Name]
ALTER COLUMN [Column Name] varchar(1000) null
END

Here is what I ended up with:
IF NOT EXISTS (SELECT 1 FROM sys.columns
WHERE name = 'sql_stmt' AND object_id = object_id('CONTEXT') and max_length = 1000)
BEGIN
ALTER TABLE dbo.CONTEXT ALTER COLUMN [sql_stmt] varchar (1000) NULL
END
GO

Related

Add a column in SQL with a special character?

I know it's bad practice but the current situation requires a special character (sharp s = ß) as a column name. How can I write a SQL query which adds the column with the special character? With SSMS it adds Straße but when I run the SQL query with sqlcmd.exe through an external program it adds Straße.
This is the script:
DECLARE #Street varchar(50)='Straße';
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'#Street'
AND Object_ID = Object_ID(N'Document'))
BEGIN
EXECUTE
(
'ALTER TABLE dbo.Document ADD ' +
#Street + ' varchar(50) NULL
')
END
You need to use nvarchar not varchar:
DECLARE #Street sysname = N'Straße'; --synonym for nvarchar(128) NOT NULL
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = #Street --This shouldn't be a literal string
AND Object_ID = Object_ID(N'Document'))
BEGIN
DECLARE #SQL nvarchar(MAX);
SET #SQL - N'ALTER TABLE dbo.Document ADD ' QUOTENAME#(Street) + N' varchar(50) NULL;';
--Should the column be an nvarchar too, considering it's name?
EXEC sys.sp_executesql #SQL;
END;

Run a query in every table that contains a specific column

I run this query to get all the tables that include a specific column name
SELECT COLUMN_NAME, TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%myColumn%'
Then, for every single table, I want to run a query like this one:
SELECT * FROM table WHERE myColumn = xxx
Is there any way to do it automatically and run the second query to all the tables from the first query?
I have a fairly big database and I want to check if there is anywhere stored something about the specific Id on myColumn. And this column is used in about 200 tables.
I guess you need to use dynamic query for this. in this I saved the Schema result in a table variable and then loop through them to generate the required query. please try the below:
DECLARE #Template varchar(max)='SELECT * FROM [TABLE_NAME] WHERE [COLUMN_NAME] = ''xxx''';
DECLARE #CMD varchar(max);
DECLARE #id int=1, #TABLE_NAME varchar(255), #COLUMN_NAME varchar(255)
declare #Table table(id int identity(1,1), COLUMN_NAME varchar(255), TABLE_NAME varchar(255))
INSERT INTO #Table (TABLE_NAME, COLUMN_NAME)
SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%Label%'
SELECT #id=ID, #TABLE_NAME = TABLE_NAME, #COLUMN_NAME = COLUMN_NAME FROM #Table WHERE ID = #id
While ##ROWCOUNT>0 BEGIN
SET #CMD = REPLACE(#Template, '[TABLE_NAME]', #TABLE_NAME)
SET #CMD = REPLACE(#CMD, '[COLUMN_NAME]', #COLUMN_NAME)
Print #cmd
EXEC (#CMD)
SELECT #id=ID, #TABLE_NAME = TABLE_NAME, #COLUMN_NAME = COLUMN_NAME FROM #Table WHERE ID = #id + 1
End
You could do this as follows. The script builds all the SELECT statements in a VARCHAR and then executes dynamically. Works when you for integers as well as varchars.
DECLARE #col_name SYSNAME='myColumn';
DECLARE #expected_value NVARCHAR(256)='xxx';
DECLARE #sel_stmts NVARCHAR(MAX) = (
SELECT
';SELECT '+
'* '+
'FROM '+
QUOTENAME(t.TABLE_SCHEMA)+'.'+QUOTENAME(t.TABLE_NAME) +
'WHERE '+
QUOTENAME(c.COLUMN_NAME)+'='''+REPLACE(#expected_value,'''','''''')+''''
FROM
INFORMATION_SCHEMA.TABLES AS t
INNER JOIN INFORMATION_SCHEMA.COLUMNS AS c ON
c.TABLE_SCHEMA=t.TABLE_SCHEMA AND
c.TABLE_NAME=t.TABLE_NAME
WHERE
t.TABLE_TYPE='BASE TABLE' AND
c.COLUMN_NAME LIKE '%'+#col_name+'%'
FOR
XML PATH('')
)+';';
--SELECT #sel_stmts; -- review
EXEC sp_executesql #sel_stmts; -- execute it
I made a couple changes to Ahmed Saeed's answer that were helpful for me so posting them here
Adding the schema name for dbs with more than one schema
Adding extra [] around the schema/db names in case one of those names is also a keyword
Printing the tablename with the results
DECLARE #Template varchar(max)='SELECT ''[TABLE_NAME]'' as TableName, * FROM [[DBSCHEMA_NAME]].[[TABLE_NAME]] WHERE [COLUMN_NAME] = ''xxx''';
DECLARE #CMD varchar(max);
DECLARE #id int=1, #DBSCHEMA_NAME varchar(255), #TABLE_NAME varchar(255), #COLUMN_NAME varchar(255)
declare #Table table(id int identity(1,1), COLUMN_NAME varchar(255), TABLE_NAME varchar(255), DBSCHEMA_NAME varchar(255))
INSERT INTO #Table (TABLE_NAME, COLUMN_NAME, DBSCHEMA_NAME)
SELECT TABLE_NAME, COLUMN_NAME, TABLE_SCHEMA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%colname%'
SELECT #id=ID, #DBSCHEMA_NAME = DBSCHEMA_NAME, #TABLE_NAME = TABLE_NAME, #COLUMN_NAME = COLUMN_NAME FROM #Table WHERE ID = #id
While ##ROWCOUNT>0 BEGIN
SET #CMD = REPLACE(#Template, '[TABLE_NAME]', #TABLE_NAME)
SET #CMD = REPLACE(#CMD, '[COLUMN_NAME]', #COLUMN_NAME)
SET #CMD = REPLACE(#CMD, '[DBSCHEMA_NAME]', #DBSCHEMA_NAME)
Print #cmd
EXEC (#CMD)
SELECT #id=ID, #DBSCHEMA_NAME = DBSCHEMA_NAME, #TABLE_NAME = TABLE_NAME, #COLUMN_NAME = COLUMN_NAME FROM #Table WHERE ID = #id + 1
End

SQL Server query to select all columns of a certain type and also show its max values

I am using SQL Server 2012.
The first part of my query is already answered in this thread. But I also want a second column that will show the corresponding maximum value of that column in its corresponding table.
I have tried this approach: use a function that takes in table name and column name as parameter and return the max value. But it is illegal to use dynamic SQL from a function. Moreover, i cannot seem to call a function from within a SELECT query.
I have also tried using stored procedure, but i cannot figure out how to call it and use it. Please suggest alternative ways to achieve this.
I am new to SQL Server.
Thanks
I think the easiest solution would be stored procedure. As far as I know:
Dynamic SQL can't be placed in functions
Dynamic SQL can't be place in OPENROWSET
I addition, if you write such procedure:
Beware of names containing spaces, qoutes (SQL injection possible)
MAX(column) on non-Indexed columns would require full scan (can be very slow)
Table and column names can be duplicated (placed in differend schemas)
Id duplicates and performance is not a problem, take a look at the following snippet:
CREATE PROC FindMaxColumnValues
#type sysname = '%',
#table sysname = '%'
AS
DECLARE #result TABLE (TableName sysname, ColumnName sysname, MaxValue NVARCHAR(MAX))
DECLARE #tab sysname
DECLARE #col sysname
DECLARE cur CURSOR FOR
SELECT TABLE_NAME TableName, COLUMN_NAME [Column Name]
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE LIKE #type and TABLE_NAME LIKE #table
OPEN cur
FETCH NEXT FROM cur INTO #tab, #col
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #sql nvarchar(MAX) = 'SELECT '+QUOTENAME(#tab,'''')+' [TableName], '+QUOTENAME(#col, '''')+' [ColumnName], MAX('+QUOTENAME(#col)+') FROM '+QUOTENAME(#tab)
INSERT INTO #result EXEC(#sql)
FETCH NEXT FROM cur INTO #tab, #col
END
CLOSE cur
DEALLOCATE cur
SELECT * FROM #result
Samples:
--MAX of INT's
EXEC FindMaxColumnValues 'INT'
--MAX of INT's in tables matching 'TestTab%'
EXEC FindMaxColumnValues 'INT', 'TestTab%'
--MAX of ALL columns
EXEC FindMaxColumnValues
Results:
TableName ColumnName MaxValue
IdNameTest ID 2
TestTable ID 5
TestTable Number 3
TableName ColumnName MaxValue
TestTable ID 5
TestTable Number 3
TableName ColumnName MaxValue
UpdateHistory UpdateTime 2016-07-14 12:21:37.00
IdNameTest ID 2
IdNameTest Name T2
TestTable ID 5
TestTable Name F
TestTable Number 3
You can use the below SP and enhance it per your Need,
CRETE PROCEDURE Getmaxtablecolval
AS
BEGIN
CREATE TABLE #t
(
tablename VARCHAR(50),
columnname VARCHAR(50),
id INT,
counts INT
)
INSERT INTO #t
SELECT table_name [Table Name],
column_name [Column Name],
NULL,
NULL
FROM information_schema.columns
WHERE data_type = 'INT'
BEGIN TRAN
DECLARE #id INT
SET #id = 0
UPDATE #t
SET #id = id = #id + 1
COMMIT TRAN
DECLARE #RowCount INT
SET #RowCount = (SELECT Count(0)
FROM #t)
DECLARE #I INT
SET #I = 1
DECLARE #Counter INT
DECLARE #TName VARCHAR(50)
DECLARE #CName VARCHAR(50)
DECLARE #DynamicSQL AS VARCHAR(500)
WHILE ( #I <= #RowCount )
BEGIN
SELECT #TName = tablename
FROM #t
WHERE id = #I
SELECT #CName = columnname
FROM #t
WHERE id = #I
SET #DynamicSQL = 'Update #T Set Counts = '
+ '(Select ISNull(Max(' + #CName + '), 0) From '
+ #TName + ') Where Id = '
+ CONVERT(VARCHAR(10), #I)
--PRINT #DynamicSQL
EXEC (#DynamicSQL)
SET #I = #I + 1
END
SELECT *
FROM #t
END
go
Getmaxtablecolval
You can create a procedure out of this:
CREATE PROCEDURE GET_COLUMNS_WITH_MAX_VALUE
#COLUMN_TYPE NVARCHAR(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- DUMMY VARIABLE TO COPY STRUCTURE TO TEMP
DECLARE #DUMMY TABLE
(
TABLE_NAME NVARCHAR(50),
COLUMN_NAME NVARCHAR(50),
MAX_VALUE NVARCHAR(MAX)
)
-- CREATE TEMP TABLE FOR DYNAMIC SQL
SELECT TOP 0 * INTO #TABLE FROM #DUMMY
INSERT INTO #TABLE
(TABLE_NAME, COLUMN_NAME)
SELECT TABLE_NAME, COLUMN_NAME
FROM information_schema.columns where data_type = #COLUMN_TYPE
DECLARE #TABLE_NAME VARCHAR(50) -- database name
DECLARE #COLUMN_NAME VARCHAR(256) -- path for backup files
DECLARE db_cursor CURSOR FOR
SELECT TABLE_NAME, COLUMN_NAME
FROM #TABLE
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #TABLE_NAME, #COLUMN_NAME
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(MAX) = 'UPDATE #TABLE SET MAX_VALUE = (SELECT MAX([' + #COLUMN_NAME + ']) FROM [' + #TABLE_NAME + ']) '
+ 'WHERE [COLUMN_NAME] = ''' + #COLUMN_NAME + ''' AND TABLE_NAME = ''' + #TABLE_NAME + '''';
PRINT #SQL
EXEC (#SQL)
FETCH NEXT FROM db_cursor INTO #TABLE_NAME, #COLUMN_NAME
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT * FROM #TABLE
DROP TABLE #TABLE
END
GO
Usage:
EXEC GET_COLUMNS_WITH_MAX_VALUE 'INT'
Results:
TABLE1 ID 50
TABLE2 ID 100
TABLE3 CarID 20
TABLE4 StudentID 30

Get the column_Name if I know the column value in sql server

My requirement is to compare data between two environments and i there is diff in both tables of both environments, insert that data to a temp table and display it.The above solution is not suiting for my scenario. I will explain my scenario in a better way.
In a Curor Cur1, I have all data of DEV from a Table(Report) where Rep_ID=1, Getting corresponding data from the TEST of REPORT Table where Rep_ID=1 In a while loop I am comparing the data of DEV and TEST
if (#DevData1 <> #TestData1)
BEGIN Get ColumnName from Report table where #DevData1 =1 Insert Into #TempTable (ColumnName, DevData1, TestData1)
ENDS Cur1 Ends
When I try to get the column name for a varchar column, I am getting the column name properly with the below query
Declare #ColStrRep nvarchar(1000)= 'select #retValOut= Col.value(''local-name(.)'', ''varchar(max)'') from (select * from Rep_attr where Rep_Name = '''+#reptName +''' for xml path(''''), type) as T(XMLCol) cross apply T.XMLCol.nodes(''*'') as n(Col) where Col.value(''.'', ''varchar(100)'') = '+#reptName +''
print #ColStrRep
EXEC Sp_executesql #ColStrRep,N'#retValOut nvarchar(100) out',#Column_Name OUT
But when I try to get the columnName for an integer column, and that too when we have the same value as 1 in the table( like RepID=1, Flag=1 , IsEmpty=1 etc), the query is getting confused and instead of Rep_ID, it retrieves the column IsEmpty. SO I need another query which just give me the columnname for a columnValue.
Thanks and Regards,
Sajitha
This solution will search using LIKE operator for varchar columns (i.e. column like '%5%') and a strict value for int columns (i.e. column=5)
DECLARE #table_name SYSNAME = 'your_table',
#search_string VARCHAR(100) = '5', --what to search
#column_name SYSNAME,
#type_name SYSNAME,
#sql_string VARCHAR(2000)
BEGIN TRY
DECLARE columns_cur CURSOR
FOR
SELECT columns.name, types.name type_name FROM sys.columns
JOIN sys.types ON columns.system_type_id = types.system_type_id
JOIN sys.objects ON columns.object_id=objects.object_id
WHERE objects.type = 'U' AND objects.name=#table_name
AND types.name IN ('varchar', 'nvarchar', 'int', 'bigint', 'smallint') --types of columns which you want to use for search
OPEN columns_cur
FETCH NEXT FROM columns_cur INTO #column_name, #type_name
WHILE (##FETCH_STATUS = 0)
BEGIN
IF #type_name IN ( 'varchar', 'nvarchar')
SET #sql_string = 'IF EXISTS (SELECT * FROM ' + #table_name + ' WHERE [' + #column_name + '] LIKE ''%' + #search_string + '%'') RAISERROR(''' + #table_name + ', ' + #column_name + ''',0,1) WITH NOWAIT'
ELSE
SET #sql_string = 'IF EXISTS (SELECT * FROM ' + #table_name + ' WHERE [' + #column_name + '] = TRY_CAST(''' + #search_string + ''' AS '+ #type_name +')) RAISERROR(''' + #table_name + ', ' + #column_name + ''',0,1) WITH NOWAIT'
EXECUTE(#sql_string)
FETCH NEXT FROM columns_cur INTO #column_name, #type_name
END
CLOSE columns_cur
DEALLOCATE columns_cur
END TRY
BEGIN CATCH
CLOSE columns_cur
DEALLOCATE columns_cur
RAISERROR(' - No access to table %s',0,1,#table_name) WITH NOWAIT
END CATCH
Thanks for the suggestion.
But I could manage the situation with the below query.
In case, the column value is a varchar, then the below query gives me the column Name.
Declare #ColStrDesc nvarchar(1000)= 'select
#retValOut= Col.value(''local-name(.)'', ''varchar(max)'')
from (select *
from Rep_attr
where Rep_Name = '''+#reptName +'''
for xml path(''''), type) as T(XMLCol)
cross apply
T.XMLCol.nodes(''*'') as n(Col)
where Col.value(''.'', ''varchar(100)'') = '''+#rep_Desc +''''
print #ColStrDesc
EXEC Sp_executesql #ColStrDesc,N'#retValOut nvarchar(100) out',#Column_Name OUT
In case, the column Value is an integer , thenn below query gives me column name.
Declare #ColErr nvarchar(1000)= 'SELECT #retValOut= STUFF(''''
+ CASE WHEN EXISTS (SELECT 1 FROM [dbo].[Rep_attr] WHERE Cast([Rep_Errs] as VARCHAR(64)) = '+#rep_Errs+') THEN '' Rep_Errs'' ELSE '''' END , 1, 1, '''')'
EXEC Sp_executesql #ColErr,N'#retValOut nvarchar(100) out',#Column_Name OUT

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

Resources