I've got a query that selects all indexes from a table and generate an output text to drop them individually.
I want to put the DB_NAME() info in the beginning of command for once, but at this moment the data is showed at every index drop.
I've tried to put a select before, but without success.
Could someone help me? Query and results are below:
- QUERY
SELECT
'USE ' + QUOTENAME(db_name()) + ';' + CHAR(13) +
'GO ' + CHAR(13) +
'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.Name) + ';' + CHAR(13) +
'GO'
FROM
sys.indexes i
JOIN sys.tables t ON t.object_id = i.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE
t.Name = 'Queue';
- Results that I get (example)
USE [master];
GO
DROP INDEX [PK_Queue] ON [dbo].[Queue];
GO
USE [master];
GO
DROP INDEX [I_001] ON [dbo].[Queue];
GO
USE [master];
GO
DROP INDEX [I_002] ON [dbo].[Queue];
GO
- Results that I want
USE [master];
GO
DROP INDEX [PK_Queue] ON [dbo].[Queue];
GO
DROP INDEX [I_001] ON [dbo].[Queue];
GO
DROP INDEX [I_002] ON [dbo].[Queue];
GO
You can just initialize a variable with the USE command, and then append the DROP INDEX commands using string concatenation. This is easier in SQL Server 2017 (STRING_AGG()), but you didn't tell us what version you use, so...
DECLARE #sql nvarchar(max) = N'USE ' + QUOTENAME(db_name()) + ';';
SELECT #sql += char(13) + N'GO' + char(13) + N'DROP INDEX '
+ QUOTENAME(i.name) + ' ON ' + QUOTENAME(s.name)
+ '.' + QUOTENAME(t.name) + ';' + CHAR(13)
FROM
sys.indexes i
JOIN sys.tables t ON t.object_id = i.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE
t.Name = 'Queue' AND i.name IS NOT NULL;
PRINT #sql;
Example db<>fiddle
SELECT Txt
from
(
SELECT
1 as Seq,
'USE ' + QUOTENAME(db_name()) + ';' + CHAR(13) +
'GO ' + CHAR(13) as Txt
union all
SELECT
2 as Seq,
'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.Name) + ';' + CHAR(13) +
'GO'
FROM
sys.indexes i
JOIN sys.tables t ON t.object_id = i.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE
t.Name = 'Queue'
) S1
order by Seq
But, why bother? You can script almost everything in SQL Server Management Studio as #shreepat18 said!
This is mostly an experiment I've made to learn some SQL Server concepts. Suppose the following scenario:
I have a production database, a development database and a test database;
The development database is more updated than the test database, containing several new tables and columns that were recently developed;
I would like to update the test database as well (with these new tables and columns), but would rather not drop and recreate that DB (it contains useful test data)
The script I wrote below is executed against the "development" database so that it will generate a script with conditions for every column of the database. The script should then be used against the other database to update it, and the conditions should add whatever column or table that the test database doesn't already have:
DECLARE #CURRENT_COLUMN nvarchar(100)
DECLARE #COLUMN_LITERAL nvarchar(100)
DECLARE #CURRENT_DEFAULT nvarchar(20)
DECLARE #CURRENT_DATATYPE nvarchar(100)
DECLARE #CURRENT_SCHEMA nvarchar(100)
DECLARE #SQLA nvarchar(max)
DECLARE #SQLB nvarchar(max)
DECLARE #CURRENT_TABLE nvarchar(100)
DECLARE #COMPUTED smallint
SET #COMPUTED = 0
PRINT '
DECLARE #SQL nvarchar(max)
'
DECLARE CUR_SCHEMA CURSOR FOR
SELECT TABLE_SCHEMA from INFORMATION_SCHEMA.TABLES
OPEN CUR_SCHEMA
FETCH NEXT FROM CUR_SCHEMA
INTO #CURRENT_SCHEMA
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE CUR_TAB CURSOR FOR
SELECT ist.TABLE_NAME from INFORMATION_SCHEMA.TABLES ist
WHERE ist.TABLE_SCHEMA = #CURRENT_SCHEMA
AND EXISTS (
SELECT TOP 1 name
FROM sys.tables
where name = ist.TABLE_NAME)
ORDER BY ist.TABLE_NAME
OPEN CUR_TAB
FETCH NEXT FROM CUR_TAB
INTO #CURRENT_TABLE
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT '
IF OBJECT_ID('''+#CURRENT_TABLE+''') IS NULL
BEGIN
SET #SQL = ''
CREATE TABLE [' + #CURRENT_TABLE +'] (placeholder bit)''
EXEC sp_executesql #SQL
END
'
DECLARE CUR CURSOR FOR
SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #CURRENT_TABLE
AND TABLE_SCHEMA = #CURRENT_SCHEMA
ORDER BY ORDINAL_POSITION asc
OPEN CUR
FETCH NEXT FROM CUR
INTO #CURRENT_COLUMN, #CURRENT_DATATYPE
SET #COLUMN_LITERAL = '[' + #CURRENT_COLUMN + ']'
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLB = ''
SET #COMPUTED = 0
/* Check if column is computed */
IF (SELECT is_computed FROM sys.columns
WHERE object_id = OBJECT_ID(#CURRENT_TABLE)
AND name = #CURRENT_COLUMN) = 1
BEGIN
SET #SQLB = #SQLB + 'IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''' + #CURRENT_COLUMN + '''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE ' + #CURRENT_SCHEMA + '.[' + #CURRENT_TABLE + ']
ADD ' + #CURRENT_COLUMN + ' AS ' +
(SELECT definition FROM sys.computed_columns
WHERE object_id = OBJECT_ID(#CURRENT_TABLE)
AND name = #CURRENT_COLUMN)
SET #COMPUTED = 1
END
/* Check for identity */
IF (SELECT is_identity FROM sys.columns WHERE object_id = OBJECT_ID(#CURRENT_TABLE)
AND name = #CURRENT_COLUMN) = 1
BEGIN
SET #SQLB = #SQLB + ' IDENTITY (' +
CAST((SELECT IDENT_SEED(#CURRENT_SCHEMA + '.[' + #CURRENT_TABLE + ']')) AS VARCHAR(4)) + ',' +
CAST((SELECT IDENT_INCR(#CURRENT_SCHEMA + '.[' + #CURRENT_TABLE + ']')) AS VARCHAR(4)) + ')'
END
/* Check if NULL is allowed */
IF (SELECT sc.is_nullable from sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
INNER JOIN sys.types sp ON sp.system_type_id = sc.system_type_id
WHERE st.name = #CURRENT_TABLE
AND sc.name = #CURRENT_COLUMN
AND ss.name = #CURRENT_SCHEMA
AND sp.name = #CURRENT_DATATYPE
) = 0
BEGIN
SET #SQLB = #SQLB + ' NOT NULL'
END
ELSE SET #SQLB = #SQLB + ' NULL'
/* Check for defaults */
IF (SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = #CURRENT_COLUMN
AND TABLE_SCHEMA = #CURRENT_SCHEMA
AND TABLE_NAME = #CURRENT_TABLE) IS NOT NULL
BEGIN
SET #CURRENT_DEFAULT = ' DEFAULT ' + (SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = #CURRENT_COLUMN
AND TABLE_SCHEMA = #CURRENT_SCHEMA
AND TABLE_NAME = #CURRENT_TABLE)
END
ELSE SET #CURRENT_DEFAULT = ''
IF #CURRENT_DATATYPE in ('date','datetime2','datetime','time',
'smalldatetime','datetimeoffset','text','ntext',
'varchar','char','nchar','nvarchar')
BEGIN
/* Check for date related data types */
IF #CURRENT_DATATYPE in ('date','datetime2','datetime','time',
'smalldatetime','datetimeoffset')
BEGIN
SET #SQLA = '
IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''' + #CURRENT_COLUMN + '''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE ' + #CURRENT_SCHEMA + '.['+ #CURRENT_TABLE + ']
ADD '+#COLUMN_LITERAL+'' + ' ' + ''+#CURRENT_DATATYPE+' '+#CURRENT_DEFAULT
END
/* Check for MAX column length */
IF (SELECT sc.max_length FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
INNER JOIN sys.types sp ON sp.system_type_id = sc.system_type_id
WHERE st.name = #CURRENT_TABLE
AND sc.name = #CURRENT_COLUMN
AND ss.name = #CURRENT_SCHEMA
AND sp.name = #CURRENT_DATATYPE) = -1
BEGIN
SET #SQLA = '
IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''' + #CURRENT_COLUMN + '''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE ' + #CURRENT_SCHEMA + '.['+ #CURRENT_TABLE + ']
ADD '+#COLUMN_LITERAL+'' + ' ' + ''+#CURRENT_DATATYPE+'(MAX)'+' ' + #CURRENT_DEFAULT
END
/* Check for string data types */
ELSE IF #CURRENT_DATATYPE in ('varchar','char','nchar','nvarchar')
BEGIN
SET #SQLA = '
IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''' + #CURRENT_COLUMN + '''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE ' + #CURRENT_SCHEMA + '.[' + #CURRENT_TABLE + ']
ADD '+#COLUMN_LITERAL+'' + ' ' + ''+#CURRENT_DATATYPE+''
+ '(' +
CAST(
( SELECT
CASE WHEN #CURRENT_DATATYPE IN ('nchar', 'nvarchar') THEN MAX(sc.max_length)/2
ELSE MAX(sc.max_length) END AS 'max_length' FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
INNER JOIN sys.types sp ON sp.system_type_id = sc.system_type_id
WHERE st.name = #CURRENT_TABLE
AND sc.name = #CURRENT_COLUMN
AND ss.name = #CURRENT_SCHEMA
AND sp.name = #CURRENT_DATATYPE
)
AS VARCHAR(10)) +')'+#CURRENT_DEFAULT
END
/* Check for text and ntext types (no column width) */
ELSE IF #CURRENT_DATATYPE in ('text','ntext')
BEGIN
SET #SQLA = '
IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''' + #CURRENT_COLUMN + '''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE ' + #CURRENT_SCHEMA + '.[' + #CURRENT_TABLE + ']
ADD '+#COLUMN_LITERAL+'' + ' ' + ''+#CURRENT_DATATYPE+' '+#CURRENT_DEFAULT
END
END
ELSE
/* Check for decimal and numeric types */
IF #CURRENT_DATATYPE in ('decimal','numeric')
BEGIN
SET #SQLA = '
IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''' + #CURRENT_COLUMN + '''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE ' + #CURRENT_SCHEMA + '.[' + #CURRENT_TABLE + ']
ADD '+#COLUMN_LITERAL+'' + ' ' + ''+#CURRENT_DATATYPE+''+'(' + CAST( (SELECT MIN(NUMERIC_PRECISION) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #CURRENT_TABLE
AND COLUMN_NAME = #CURRENT_COLUMN
AND TABLE_SCHEMA = #CURRENT_SCHEMA
AND DATA_TYPE = #CURRENT_DATATYPE
) AS VARCHAR(10)) + ',' +
CAST( (SELECT MIN(NUMERIC_SCALE) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #CURRENT_TABLE
AND COLUMN_NAME = #CURRENT_COLUMN
AND DATA_TYPE = #CURRENT_DATATYPE
) AS VARCHAR(10)) + ')'+ #CURRENT_DEFAULT
END
ELSE
BEGIN
SET #SQLA = '
IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''' + #CURRENT_COLUMN + '''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE ' + #CURRENT_SCHEMA + '.[' + #CURRENT_TABLE + ']
ADD '+#COLUMN_LITERAL+'' + ' ' + ''+#CURRENT_DATATYPE+''+#CURRENT_DEFAULT
END
IF #COMPUTED = 0
BEGIN
PRINT #SQLA + #SQLB + '
END
'
END
FETCH NEXT FROM CUR
INTO #CURRENT_COLUMN, #CURRENT_DATATYPE
SET #COLUMN_LITERAL = '[' + #CURRENT_COLUMN + ']'
END
CLOSE CUR;
DEALLOCATE CUR;
PRINT '
IF EXISTS
(SELECT TOP 1 sc.name FROM sys.columns sc
INNER JOIN sys.tables st ON st.object_id = sc.object_id
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id
WHERE sc.Name = ''placeholder''
AND st.Object_ID = OBJECT_ID('''+ #CURRENT_TABLE+ ''')
AND ss.name = ''' + #CURRENT_SCHEMA + ''')
BEGIN
ALTER TABLE '+#CURRENT_SCHEMA+'.['+#CURRENT_TABLE+'] DROP COLUMN [placeholder]
END
'
FETCH NEXT FROM CUR_TAB
INTO #CURRENT_TABLE
END
CLOSE CUR_TAB
DEALLOCATE CUR_TAB
END
CLOSE CUR_SCHEMA
DEALLOCATE CUR_SCHEMA
Questions:
Could my use of a "placeholder" column be avoided? (I added it because I couldn't create empty columns in case they didn't exist).
Is the use of three cursors accepted? I believe this could be simplified, perhaps with temp tables, or table variables.
Is my approach to catch exceptional formatting cases (like the numeric data type definition or the maximum column character length) cohesive?
Is the script fully correct? I intensively tested it against a copy of a real database and also tested the script it generated against a blank database, and it seems to have produced the expected results.
Is the number of variables I used excessive? Is any of my variables irrelevant?
Is the use of both INFORMATION_SCHEMA and system tables acceptable? (I used INFORMATION_SCHEMA some times to avoid excess of table joining).
Am I using cursors correctly?
Would you suggest a different approach to some section of my script?
Thanks, and sorry for asking so many questions. Answer just one or some if you don't wish to answer all of them!
** NOTES **
I wrote this for an SQL Server 2008 database, but you could point out alternatives for newer versions to help improve my knowledge
I know this script doesn't replicate stored procedures, triggers, and other things, but that could be scripted automatically with SSMS afterwards so I included just column properties on the script.
So I would propose the usage of the Sql Server Data Tools and database project. It allows you to import a schema of existing database or create a new database project. You can link it to the TFS or Git, your preference for source control. In this case you do not have to have a separate SQL script to generate schema. If you decide to move to the schema development from SSDT then the codebase will always have a latest known schema. You can then generate scripts for deployment of the new codebase and SSDT or Visual Studio will figure the SQL out, I would strongly recommend checking those scripts before deployment to prod.
It is also possible to publish changes to target databases from this tool.
SSDT
I have the following code which is supposed to display the column name, data type, size, # of filled columns and the total rows.
The problem that I am running into is that using INFORMATION_SCHEMA.columns I can only display either DATA_TYPE or CHARACTER_MAXIMUM_LENGTH. If I try to SELECT both I get errors:
An expression of non-boolean type specified in a context where a condition is expected, near 'si'.
The code statement is:
DECLARE #TableName VARCHAR(512) = 'state';
DECLARE #SQL VARCHAR(1024);
WITH SQLText AS (
SELECT
ROW_NUMBER() OVER (ORDER BY c.Name) AS RowNum,
'SELECT ''' + c.name + ''',
MAX(b.DATA_TYPE) AS ''Data Type'',
SUM(CASE WHEN ' + c.Name + ' IS NULL THEN 0 ELSE 1 END) AS ''Filled Values'',
COUNT(*) AS ''Total Records''
FROM INFORMATION_SCHEMA.columns b
JOIN ' + #TableName + ' ON b.column_name = ''' + c.name +'''' AS SQLRow
FROM
sys.tables t
INNER JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.types a ON c.user_type_id = a.user_type_id
WHERE
t.name = #TableName)
Any ideas on how to get both to display?
DECLARE #TableName VARCHAR(512) = 'Settings';
DECLARE #SQL VARCHAR(1024);
WITH SQLText AS (
SELECT
ROW_NUMBER() OVER (ORDER BY c.Name) AS RowNum,
'SELECT ''' + c.name + ''',
MAX(b.DATA_TYPE) AS ''Data Type'',
Max(b.CHARACTER_MAXIMUM_LENGTH) as ''CHARACTER_MAXIMUM_LENGTH'',
SUM(CASE WHEN ' + c.Name + ' IS NULL THEN 0 ELSE 1 END) AS ''Filled Values'',
COUNT(*) AS ''Total Records''
FROM INFORMATION_SCHEMA.columns b
JOIN ' + #TableName + ' ON b.column_name = ''' + c.name +'''' AS SQLRow
FROM
sys.tables t
INNER JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.types a ON c.user_type_id = a.user_type_id
WHERE
t.name = #TableName)
select * from SQLText
This works for me, tbh though i'd add to get a better idea of the size a column uses.
SUM(CASE WHEN ' + c.Name + ' IS NULL THEN 0 ELSE Datalength(' + c.Name + ') END) AS ''Filled Values'',
I'm using a stored procedure on a autocomplete dropdownlist to retreive a list of clients. Some of the clients have an '&' (ampersand) sign in the name e.g. 'H&M', 'Marks & Spencers' and users would like to search on the '&' sign.
When i directly use the & sign it uses it as a word breaker and does not pick the list which has '&' sign.
Is there any way i could search the table using the '&' and retreive values that have '&' sign.
Hope i've made sence explaining what i need to do.
Thanks for all your help!
Perhaps something like:
WHERE column LIKE REPLACE(#searchvalue, '&', '/&') ESCAPE '/'
Another idea:
DECLARE #randomstring #text
SET #randomstring ='randomstringthatwillneverbeusedforsearch'
WHERE REPLACE(column, #searchvalue, #randomstring) LIKE '%'+#randomstring+'%'
No idea about performance issues though.
Try this one -
DECLARE #char CHAR(1)
SELECT #char = '&'
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = (
SELECT CHAR(13) +
'IF EXISTS(SELECT 1 FROM ' +
QUOTENAME(s.name) + '.' + QUOTENAME(o.name) +
' WHERE' + b.cols + ')
SELECT table_name = ''[' + s.name + '].[' + o.name + ']'' ' + c.cols +' FROM ' +
QUOTENAME(s.name) + '.' + QUOTENAME(o.name) +
' WHERE' + b.cols + ';'
FROM sys.objects o
JOIN sys.schemas s ON o.[schema_id] = s.[schema_id]
JOIN (
SELECT DISTINCT p.[object_id]
FROM sys.partitions p
WHERE p.[rows] > 0
) p ON p.[object_id] = o.[object_id]
OUTER APPLY (
SELECT cols = STUFF((
SELECT 'OR CHARINDEX(''' + #char + ''', ' + QUOTENAME(c.name) + ') > 0 '
FROM sys.columns c
JOIN sys.types t ON c.user_type_id = t.user_type_id
WHERE t.name IN ('char', 'nchar', 'ntext', 'nvarchar', 'text', 'varchar')
AND c.[object_id] = o.[object_id]
ORDER BY c.column_id
FOR XML PATH(''), TYPE, ROOT).value('root[1]', 'NVARCHAR(MAX)'), 1, 2, '')
) b
OUTER APPLY (
SELECT cols = (
SELECT ', ' + QUOTENAME(c.name) + ' '
FROM sys.columns c
JOIN sys.types t ON c.user_type_id = t.user_type_id
WHERE t.name IN ('char', 'nchar', 'ntext', 'nvarchar', 'text', 'varchar')
AND c.[object_id] = o.[object_id]
ORDER BY c.column_id
FOR XML PATH(''), TYPE, ROOT).value('root[1]', 'NVARCHAR(MAX)')
) c
WHERE o.[type] = 'U'
AND b.cols IS NOT NULL
FOR XML PATH(''), TYPE, ROOT).value('root[1]', 'NVARCHAR(MAX)')
EXEC sys.sp_executesql #sql
This query searches for the substring in all db's which have tables with text fields -
DECLARE #phrase NVARCHAR(100)
SELECT #phrase = '&'
DECLARE
#db_name SYSNAME
, #output NVARCHAR(200)
DECLARE db CURSOR READ_ONLY FAST_FORWARD LOCAL FOR
SELECT d.name
FROM sys.databases d
WHERE d.state_desc = 'ONLINE'
AND d.name NOT IN ('tempdb', 'model', 'msdb', 'master')
--AND d.name = 'your db name'
ORDER BY d.name
OPEN db
FETCH NEXT FROM db INTO #db_name
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #output = CONVERT(NVARCHAR(15), GETDATE(), 114) + ': Find in ' + QUOTENAME(#db_name) + ':'
RAISERROR(#output, 0, 1) WITH NOWAIT
DECLARE #tsql NVARCHAR(MAX) = '
USE [' + #db_name + '];
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = ''USE [' + #db_name + '];'' + (
SELECT
CHAR(13) +
''IF EXISTS(SELECT 1 FROM '' +
QUOTENAME(s.name) + ''.'' + QUOTENAME(o.name) +
'' WHERE'' + b.cols + '') PRINT ''''['' +
s.name + ''].['' + o.name + '']'''';''
FROM sys.objects o
JOIN sys.schemas s ON o.[schema_id] = s.[schema_id]
JOIN (
SELECT DISTINCT p.[object_id]
FROM sys.partitions p
WHERE p.[rows] > 0
) p ON p.[object_id] = o.[object_id]
OUTER APPLY (
SELECT cols = STUFF((
SELECT ''OR CHARINDEX(''''' + #phrase + ''''', '' + QUOTENAME(c.name) + '') > 0 ''
FROM sys.columns c
JOIN sys.types t ON c.user_type_id = t.user_type_id
WHERE t.name IN (''char'', ''nchar'', ''ntext'', ''nvarchar'', ''text'', ''varchar'')
AND c.[object_id] = o.[object_id]
ORDER BY c.column_id
FOR XML PATH(''''), TYPE, ROOT).value(''root[1]'', ''NVARCHAR(MAX)''), 1, 2, '''')
) b
WHERE o.[type] = ''U''
AND b.cols IS NOT NULL
FOR XML PATH(''''), TYPE, ROOT).value(''root[1]'', ''NVARCHAR(MAX)'')
EXEC sys.sp_executesql #sql
'
EXEC sys.sp_executesql #tsql
PRINT REPLICATE('-', 100) + CHAR(13)
FETCH NEXT FROM db INTO #db_name
END
CLOSE db
DEALLOCATE db
I have to do checks between a development and release database and do this manually, which is both slow and not 100% reliable (I only visually inspect the tables).
Is there a quick and easy way to compare table schemas automatically? Maybe even a feature that does this built right into SQL server?
Edit: I'm comparing structure only, thank you for pointing this out.
I'm a fan of SQL DBDiff, which is an open source tool you can use to compare tables, views, functions, users, etc. of two instances of SQL Server databases and generate a change script between the source and destination databases.
There are some commercial products that do this; Visual Studio 2010 Premium Edition can compare schemas.
Some others:
http://www.red-gate.com/products/SQL_Compare/index.htm
http://www.apexsql.com/sql_tools_diff.aspx
Data or structure or both? Try RedGate sql compare or data compare. They both have free trials and are awesome.
http://www.red-gate.com/products/SQL_Compare/index.htm
http://www.red-gate.com/products/SQL_Data_Compare/index.htm
I'm a little late to the game...but this script I made has worked well for me. It'll work across linked servers too if needed.
use master
go
DECLARE #Server1 VARCHAR(100) ='[CARNYSQLTEST1].'; --include a dot at the end
DECLARE #DB1 VARCHAR(100) = '[ZipCrim]';
DECLARE #Table1 VARCHAR(100) = 'IntAction';
DECLARE #Server2 VARCHAR(100) ='[CARNYSQLDEV].'; --include a dot at the end
DECLARE #DB2 VARCHAR(100) = '[ZipCrim]';
DECLARE #Table2 VARCHAR(100) = 'IntAction';
DECLARE #SQL NVARCHAR(MAX);
SET #SQL =
'
SELECT Table1.ServerName,
Table1.DBName,
Table1.SchemaName,
Table1.TableName,
Table1.ColumnName,
Table1.name DataType,
Table1.Length,
Table1.Precision,
Table1.Scale,
Table1.Is_Identity,
Table1.Is_Nullable,
Table2.ServerName,
Table2.DBName,
Table2.SchemaName,
Table2.TableName,
Table2.ColumnName,
Table2.name DataType,
Table2.Length,
Table2.Precision,
Table2.Scale,
Table2.Is_Identity,
Table2.Is_Nullable
FROM
(SELECT ''' + #Server1 + ''' ServerName,
''' + #DB1 + ''' DbName,
SCHEMA_NAME(t.schema_id) SchemaName,
t.Name TableName,
c.Name ColumnName,
st.Name,
c.Max_Length Length,
c.Precision,
c.Scale,
c.Is_Identity,
c.Is_Nullable
FROM ' + #Server1 + #DB1 + '.sys.tables t
INNER JOIN ' + #Server1 + #DB1 + '.sys.columns c ON t.Object_ID = c.Object_ID
INNER JOIN sys.types st ON St.system_type_id = c.System_Type_id AND st.user_type_id = c.user_type_id
WHERE t.Name = ''' + #Table1 + ''') Table1
FULL OUTER JOIN
(SELECT ''' + #Server2 + ''' ServerName,
''' + #DB2 + ''' DbName,
SCHEMA_NAME(t.schema_id) SchemaName,
t.name TableName,
c.name ColumnName,
st.Name,
c.max_length Length,
c.Precision,
c.Scale,
c.Is_Identity,
c.Is_Nullable
FROM ' + #Server2 + #DB2 + '.sys.tables t
INNER JOIN ' + #Server2 + #DB2 + '.sys.columns c ON t.Object_ID = c.Object_ID
INNER JOIN sys.types st ON St.system_type_id = c.System_Type_id AND st.user_type_id = c.user_type_id
WHERE t.Name = ''' + #Table2 + ''') Table2
ON Table1.ColumnName = Table2.ColumnName
ORDER BY CASE WHEN Table1.ColumnName IS NULL THEN 2 ELSE 1 END, Table1.ColumnName
'
EXEC sp_executesql #SQL
Soo,
Googled this:
for structures:
see also:
My previous answers' links doesn't work anymore for some reason, so here's another answer from TechNet:
DECLARE #Sourcedb sysname
DECLARE #Destdb sysname
DECLARE #Tablename sysname
DECLARE #SQL varchar(max)
SELECT #Sourcedb = '<<SourceDatabaseName>>'
SELECT #Destdb = '<<DestinationDatabaseName>>'
SELECT #Tablename = '<<Tablename>>' -- '%' for all tables
SELECT #SQL = ' SELECT Tablename = ISNULL(Source.tablename,Destination.tablename)
,ColumnName = ISNULL(Source.Columnname,Destination.Columnname)
,Source.Datatype
,Source.Length
,Source.precision
,Destination.Datatype
,Destination.Length
,Destination.precision
,[Column] =
Case
When Source.Columnname IS NULL then ''Column Missing in the Source''
When Destination.Columnname IS NULL then ''Column Missing in the Destination''
ELSE ''''
end
,DataType = CASE WHEN Source.Columnname IS NOT NULL
AND Destination.Columnname IS NOT NULL
AND Source.Datatype <> Destination.Datatype THEN ''Data Type mismatch''
END
,Length = CASE WHEN Source.Columnname IS NOT NULL
AND Destination.Columnname IS NOT NULL
AND Source.Length <> Destination.Length THEN ''Length mismatch''
END
,Precision = CASE WHEN Source.Columnname IS NOT NULL
AND Destination.Columnname IS NOT NULL
AND Source.precision <> Destination.precision THEN ''precision mismatch''
END
,Collation = CASE WHEN Source.Columnname IS NOT NULL
AND Destination.Columnname IS NOT NULL
AND ISNULL(Source.collation_name,'''') <> ISNULL(Destination.collation_name,'''') THEN ''Collation mismatch''
END
FROM
(
SELECT Tablename = so.name
, Columnname = sc.name
, DataType = St.name
, Length = Sc.max_length
, precision = Sc.precision
, collation_name = Sc.collation_name
FROM ' + #Sourcedb + '.SYS.objects So
JOIN ' + #Sourcedb + '.SYS.columns Sc
ON So.object_id = Sc.object_id
JOIN ' + #Sourcedb + '.SYS.types St
ON Sc.system_type_id = St.system_type_id
AND Sc.user_type_id = St.user_type_id
WHERE SO.TYPE =''U''
AND SO.Name like ''' + #Tablename + '''
) Source
FULL OUTER JOIN
(
SELECT Tablename = so.name
, Columnname = sc.name
, DataType = St.name
, Length = Sc.max_length
, precision = Sc.precision
, collation_name = Sc.collation_name
FROM ' + #Destdb + '.SYS.objects So
JOIN ' + #Destdb + '.SYS.columns Sc
ON So.object_id = Sc.object_id
JOIN ' + #Destdb + '.SYS.types St
ON Sc.system_type_id = St.system_type_id
AND Sc.user_type_id = St.user_type_id
WHERE SO.TYPE =''U''
AND SO.Name like ''' + #Tablename + '''
) Destination
ON source.tablename = Destination.Tablename
AND source.Columnname = Destination.Columnname '
EXEC (#Sql)
For a free solution, you can use SQL Server Managements Objects to output the DDL script for each table, view, index, SP, UDF, etc. Then you can compare, either in code, or using a diff tool like WinMerge.
If two tables in same database, you can use this query
select c2.table_name,c2.COLUMN_NAME
from [INFORMATION_SCHEMA].[COLUMNS] c2
where table_name='table1'
and c2.COLUMN_NAME not in (select column_name
from [INFORMATION_SCHEMA].[COLUMNS]
where table_name='table1')
you can take a look at http://cdttools.com/2011/10/sql-diff-erence/ its a low cost alternative, will walk schema between two databases and tell you whats changed. You can then use SQL Mgmt studio to generate "script->As Alter" to build change scripts. (caveat: I wrote it)
Modified a bit of BD.'s query, all credit goes to him. (Changed SCHEMA_NAME(schema_id) to sys.schemas join because SCHEMA_NAME(schema_id) works with current db context which is master, changed sorting and changed column names and added the status column)
USE master
GO
DECLARE
#Server1 VARCHAR(100) = 'Server1.', -- don't forget to include a dot at the end
#Server2 VARCHAR(100) = 'Server2.', -- don't forget to include a dot at the end
#DB1 VARCHAR(100) = 'Database1',
#DB2 VARCHAR(100) = 'Database2'
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = '
SELECT
CASE
WHEN s1.[Column] IS NOT NULL
AND s2.[Column] IS NULL
THEN ''New''
WHEN s1.[Column] IS NULL
AND s2.[Column] IS NOT NULL
THEN ''Deleted''
WHEN s1.[Column] IS NOT NULL
AND s2.[Column] IS NOT NULL
AND (s1.[Type] <> s2.[Type]
OR s1.[Length] <> s2.[Length]
OR s1.[Precision] <> s2.[Precision]
OR s1.Scale <> s2.Scale
OR s1.IsNullable <> s2.IsNullable
OR s1.IsIdentity <> s2.IsIdentity
OR s1.IdentitySeed <> s2.IdentitySeed
OR s1.IdentityIncrement <> s2.IdentityIncrement
OR s1.DefaultValue <> s2.DefaultValue)
THEN ''Changed''
ELSE ''Identical''
END [Status],
s1.[Database],
s1.[Schema],
s1.[Table],
s1.[Column],
s1.[Type],
s1.IsCharType,
s1.[Length],
s1.[Precision],
s1.Scale,
s1.IsNullable,
s1.IsIdentity,
s1.IdentitySeed,
s1.IdentityIncrement,
s1.DefaultValue,
s1.[Order],
s2.[Database],
s2.[Schema],
s2.[Table],
s2.[Column],
s2.[Type],
s2.IsCharType,
s2.[Length],
s2.[Precision],
s2.Scale,
s2.IsNullable,
s2.IsIdentity,
s2.IdentitySeed,
s2.IdentityIncrement,
s2.DefaultValue,
s2.[Order]
FROM (
SELECT
''' + #DB1 + ''' AS [Database],
s.name AS [Schema],
t.name AS [Table],
c.name AS [Column],
tp.name AS [Type],
CASE
WHEN tp.collation_name IS NOT NULL
THEN 1
ELSE 0
END AS IsCharType,
CASE
WHEN c.max_length = -1
THEN ''MAX''
ELSE CAST(c.max_length AS VARCHAR(4))
END AS [Length],
c.[precision],
c.scale,
c.is_nullable AS IsNullable,
c.is_identity AS IsIdentity,
CAST(ISNULL(ic.seed_value, 0) AS INT) AS IdentitySeed,
CAST(ISNULL(ic.increment_value, 0) AS INT) AS IdentityIncrement,
dc.definition AS DefaultValue,
c.column_id AS [Order]
FROM ' + #Server1 + #DB1 + '.sys.tables t
INNER JOIN ' + #Server1 + #DB1 + '.sys.schemas s ON s.schema_id = t.schema_id
INNER JOIN ' + #Server1 + #DB1 + '.sys.columns c ON c.object_id = t.object_id
INNER JOIN ' + #Server1 + #DB1 + '.sys.types tp ON tp.system_type_id = c.system_type_id
LEFT OUTER JOIN ' + #Server1 + #DB1 + '.sys.identity_columns ic ON ic.object_id = t.object_id AND ic.name = c.name
LEFT OUTER JOIN ' + #Server1 + #DB1 + '.sys.default_constraints dc ON dc.object_id = c.default_object_id
) s1
FULL OUTER JOIN (
SELECT
''' + #DB2 + ''' AS [Database],
s.name AS [Schema],
t.name AS [Table],
c.name AS [Column],
tp.name AS [Type],
CASE
WHEN tp.collation_name IS NOT NULL
THEN 1
ELSE 0
END AS IsCharType,
CASE
WHEN c.max_length = -1
THEN ''MAX''
ELSE CAST(c.max_length AS VARCHAR(4))
END AS [Length],
c.[precision],
c.scale,
c.is_nullable AS IsNullable,
c.is_identity AS IsIdentity,
CAST(ISNULL(ic.seed_value, 0) AS INT) AS IdentitySeed,
CAST(ISNULL(ic.increment_value, 0) AS INT) AS IdentityIncrement,
dc.definition AS DefaultValue,
c.column_id AS [Order]
FROM ' + #Server2 + #DB2 + '.sys.tables t
INNER JOIN ' + #Server2 + #DB2 + '.sys.schemas s ON s.schema_id = t.schema_id
INNER JOIN ' + #Server2 + #DB2 + '.sys.columns c ON c.object_id = t.object_id
INNER JOIN ' + #Server2 + #DB2 + '.sys.types tp ON tp.system_type_id = c.system_type_id
LEFT OUTER JOIN ' + #Server2 + #DB2 + '.sys.identity_columns ic ON ic.object_id = t.object_id AND ic.name = c.name
LEFT OUTER JOIN ' + #Server2 + #DB2 + '.sys.default_constraints dc ON dc.object_id = c.default_object_id
) s2
ON s2.[Schema] = s1.[Schema]
AND s2.[Table] = s1.[Table]
AND s2.[Column] = s1.[Column]
ORDER BY
CASE WHEN s1.[Database] IS NULL THEN s2.[Database] ELSE s1.[Database] END,
CASE WHEN s1.[Schema] IS NULL THEN s2.[Schema] ELSE s1.[Schema] END,
CASE WHEN s1.[Table] IS NULL THEN s2.[Table] ELSE s1.[Table] END,
CASE WHEN s1.[Order] IS NULL THEN s2.[Order] ELSE s1.[Order] END
'
EXEC sp_executesql #SQL
You can use the SQL Management studio tools to "Generate scripts" from both databases. Then use your favorite text comparison tool to see any differences.
In the old days, this worked great, but in SQL 2005 the generate script code changed and the objects were no longer created in the same order, so the text comparison is less useful. I have not tested this in more recent versions of SQL so it may have been fixed. You can also try http://exportsqlscript.codeplex.com/ which I have used with good success to output the DDL as scripts for source code control and comparing versions.
References:
https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=332400&wa=wsignin1.0
http://social.msdn.microsoft.com/Forums/en-US/sqltools/thread/505fd238-e0dc-42ae-8a54-2dceace81bb3/
http://exportsqlscript.codeplex.com/