Generating DDL/DML scripts for all tables/columns in a database - sql

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

Related

SQL Server how to find specific data across entire database

I have this script which finds all tables that contain data in column RGNCODE. I'm trying to amend this to find tables that contain a specific value for rgncode across all tables. So RGNCODE = 'UK'. Unsure where to add this in?
SELECT
sys.columns.name AS ColumnName,
tables.name AS TableName
FROM sys.columns
JOIN sys.tables ON sys.columns.object_id = tables.object_id
WHERE
sys.columns.name = 'rgncode' and
tables.name in (
SELECT [TableName] = so.name
FROM sysobjects so, sysindexes si
WHERE so.xtype = 'U' AND si.id = so.id
GROUP BY so.name
HAVING max(si.rows) > 0
)
A dynamic statement, using system catalog views, is an option here:
DECLARE #stm nvarchar(max) = N''
SELECT #stm = #stm +
CASE WHEN #stm = N'' THEN '' ELSE N'UNION ALL ' END +
N'SELECT ''' +
QUOTENAME(sch.[name]) + '.' + QUOTENAME(tab.[name]) + ''' AS TableName FROM ' +
QUOTENAME(sch.[name]) + '.' + QUOTENAME(tab.[name]) +
N' WHERE RGNCODE = ''UK'' HAVING COUNT(*) > 0 '
FROM sys.columns col
JOIN sys.tables tab ON col.object_id = tab.object_id
JOIN sys.schemas sch ON tab.schema_id = sch.schema_id
WHERE col.[name] = 'RGNCODE'
PRINT #stm
EXEC sp_executesql #stm
I think the following cursor will help you:
CREATE TABLE ##DataTable
(TblName VARCHAR(100),RowNum INT)
GO
DECLARE #TableName AS VARCHAR(200)
DECLARE #SQLText AS nVARCHAR(MAX)
DECLARE #TableCount AS INT
DECLARE TableDect CURSOR
FOR
SELECT
tables.name AS TableName
FROM
sys.columns
JOIN sys.tables ON
sys.columns.object_id = tables.object_id
WHERE
sys.columns.name = 'rgncode' and tables.name in (
SELECT
[TableName] = so.name
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = so.id
GROUP BY
so.name
HAVING max(si.rows) >0 )
OPEN TableDect
FETCH NEXT FROM TableDect INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLText = N'SELECT #NumberOUT=COUNT(*) FROM ' + QUOTENAME(#TableName) + ' WHERE rgncode=''UK'''
SET #TableCount = 0
EXECUTE sp_executesql #SQLText ,N'#NumberOUT nvarchar(25) OUTPUT' ,#NumberOUT=#TableCount OUTPUT
INSERT INTO ##DataTable values(#TableName,#TableCount)
FETCH NEXT FROM TableDect INTO #TableName
END
CLOSE TableDect;
DEALLOCATE TableDect
SELECT * FROM ##DataTable

SQL query optimization

I am trying to find out a script that can help me in Data Density of my DBs. the point is I already figure out the query and what I do need but the problem is the query takes for ever. it works find for small DBs, but that doesn't happen a lot.
So I am looking for kind of optimization or any ideas to help me.
the script:
DECLARE Cur CURSOR
FOR
SELECT DB_Name() AS DatabaseName
,s.[name] AS SchemaName
,t.[name] AS TableName
,c.[name] AS ColumnName
,'[' + DB_Name() + ']' + '.[' + s.NAME + '].' + '[' + T.NAME + ']' AS FullQualifiedTableName
,d.[name] AS DataType
FROM sys.schemas s
INNER JOIN sys.tables t ON s.schema_id = t.schema_id
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN sys.types d ON c.user_type_id = d.user_type_id
WHERE d.NAME LIKE '%int%'
OR d.NAME LIKE '%float%'
OR d.NAME LIKE '%decimal%'
OR d.NAME LIKE '%numeric%'
OR d.NAME LIKE '%real%'
OR d.NAME LIKE '%money%'
OR d.NAME LIKE '%date%'
OR d.NAME LIKE '%datetime%'
AND is_identity = 0
OPEN Cur
FETCH NEXT
FROM Cur
INTO #DatabaseName
,#SchemaName
,#TableName
,#ColumnName
,#FullyQualifiedTableName
,#DataType
WHILE ##FETCH_STATUS = 0 -- The FETCH statement was successful.
BEGIN
DECLARE #SQL VARCHAR(MAX) = NULL
SET #SQL = ' Select ''' + #DatabaseName + ''' AS DatabaseName, ''' +
#SchemaName + ''' AS TableName,
''' + #TableName + ''' AS SchemaName,
''' + #ColumnName + ''' AS ColumnName,
''' + #DataType + ''' AS ColumnName,
(Select MAX(' + #ColumnName + ') from ' + #FullyQualifiedTableName + ' with (nolock))
AS MaxValue,
(Select MIN(' + #ColumnName + ') from ' + #FullyQualifiedTableName + ' with (nolock))
AS MinValue,
(Select COUNT(*) from ' + #FullyQualifiedTableName + ' with (nolock))
AS CountValue,
(Select COUNT(*) from ' + #FullyQualifiedTableName + ' Where ' + #ColumnName + ' IS NOT NULL )
AS NotNULLCount,
(Select 0 from ' + #FullyQualifiedTableName + ')
AS DataDensity'
PRINT #SQL
The following script will give me the MAX, MIN, COUNT, NotNULLCount and the DATA DENSITY for every and each column form the declared types above. but u can imagine a DB with 70 tables and each table has 30-50 columns....
running this script will take for ever.
You should always try and avoid using cursors, this query will give you a list of select queries that you can copy and paste to get the data that you require. Note also I have removed the sub selects as they are not required:
SELECT 'Select ''' + DB_Name() + ''' AS DatabaseName, ''' + s.Name + ''' AS SchemaName, ''' + t.Name + ''' AS TableName, ''' + c.Name + ''' AS ColumnName, ''' + d.Name + ''' AS ColumnName,' +
'MAX([' + c.Name + ']) AS MaxValue,' +
'MIN([' + c.Name + ']) AS MinValue,' +
'COUNT(*) AS CountValue,' +
'COUNT([' + c.Name + ']) AS NotNullCount,' +
'CAST(COUNT(DISTINCT [' + c.name + ']) AS float) / COUNT([' + C.Name + ']) AS DataDensity ' +
'from [' + DB_Name() + '].[' + s.Name + '].[' + t.name + '] with (nolock)'
FROM sys.schemas s
INNER JOIN sys.tables t ON s.schema_id = t.schema_id
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN sys.types d ON c.user_type_id = d.user_type_id
WHERE d.NAME LIKE '%int%'
OR d.NAME LIKE '%float%'
OR d.NAME LIKE '%decimal%'
OR d.NAME LIKE '%numeric%'
OR d.NAME LIKE '%real%'
OR d.NAME LIKE '%money%'
OR d.NAME LIKE '%date%'
OR d.NAME LIKE '%datetime%'
AND is_identity = 0
This will give you a list of select statements in the following form:
Select 'MyDB' AS DatabaseName, 'dbo' AS SchemaName, 'MyTable' AS TableName, 'ID' AS ColumnName, 'int' AS ColumnName,MAX([ID]) AS MaxValue,MIN([ID]) AS MinValue,COUNT(*) AS CountValue,COUNT([ID]) AS NotNullCount,CAST(COUNT(DISTINCT [ID]) AS float) / COUNT([ID]) AS DataDensity from [MyDB].[dbo].[MyTable] with (nolock)
Of course SQL Server stores these sorts of statistics for useful columns, You can find which ones it has be using:
EXEC SP_HELPSTATS 'MyTable', 'ALL'
Then using the list of statistics returned such as:
_WA_Sys_00000014_004FB3FB ID
to get the actual stats using:
DBCC SHOW_STATISTICS('MyTable','_WA_Sys_00000002_004FB3FB')
This will return data like:
Name Updated Rows Rows Sampled Steps Density Average key length String Index Filter Expression Unfiltered Rows
_WA_Sys_00000002_004FB3FB Jan 8 2017 8:01PM 16535 16535 200 0.2493151 4.459389 NO NULL 16535
and
All density Average Length Columns
0.0006038647 4.459389 EffectiveDate
and another rowset showing a histogram of values.
You can automatically generate these DBCC commands using:
SELECT 'DBCC SHOW_STATISTICS([' + OBJECT_NAME(s.object_Id) + '],''' + s.Name + ''')'
FROM sys.stats s
INNER JOIN sys.stats_columns sc
ON s.object_id = sc.object_id AND s.stats_id = sc.stats_id
INNER JOIN sys.columns c
ON sc.object_id = c.object_id AND sc.column_id = c.column_id
WHERE s.Name LIKE '_WA%'
ORDER BY s.stats_id, sc.column_id;

Programmatically rename all system name constraints

I have a distributed application that lives at many customer sites. The previous developer was not very good in giving user defined names to constraints. How could I go about renaming all the constrains to something like 'DF_[TableName]_[ColumnName]'? I could try and cursor over the list of tables. Do you think this would be a problem if I deploy this to a customer site? Or is there a better way to accomplish this?
SELECT sys.schemas.name as [Schema],
sys.tables.name as [TableName],
sys.all_columns.name as [Column],
default_constraints.name as [Constraint]
FROM
sys.all_columns
INNER JOIN sys.tables
ON all_columns.object_id = tables.object_id
INNER JOIN sys.schemas
ON tables.schema_id = schemas.schema_id
INNER JOIN sys.default_constraints
ON all_columns.default_object_id = default_constraints.object_id
where default_constraints.name like '%[0-9]%'
If you are using a naming convention for each constraint type, then this would help you. Here I am putting an example of how to handle this with Default constraint. The procedure will find default constraints, drop those that don't follow naming conventions and create new ones with the old one's definition.
This is a simple version for one time run
DECLARE #SchemaName sysname = 'dbo';
DECLARE #TableName sysname = NULL;
DECLARE #ColumnName sysname = NULL;
DECLARE #sql VARCHAR(max) = '';
SELECT
#sql +=
'ALTER TABLE [' + s.name + '].[' + o.name + '] DROP CONSTRAINT [' + dc.name + ']; ' +
'ALTER TABLE [' + s.name + '].[' + o.name + '] ADD CONSTRAINT [DF_' + o.name + '_' + c.name + '] DEFAULT ' + dc.DEFINITION + ' FOR [' + c.name + '];'
FROM dbo.sysobjects do
INNER JOIN dbo.sysobjects o ON do.parent_obj = o.id
INNER JOIN sys.default_constraints dc ON dc.object_id = do.id
INNER JOIN sys.columns c ON c.object_id = o.id
AND c.column_id = dc.parent_column_id
INNER JOIN sys.schemas s ON s.schema_id = dc.schema_id
WHERE o.type = 'U'
AND do.type = 'D'
AND s.name = #SchemaName
AND o.name = CASE WHEN ISNULL(#TableName,'') = ''
THEN o.name
ELSE #TableName END
AND c.name = CASE WHEN ISNULL(#ColumnName,'') = ''
THEN c.name
ELSE #ColumnName END
AND do.name NOT LIKE 'DF_' + o.name + '_' + c.name
ORDER BY o.name
--PRINT (#sql)
EXECUTE(#sql)
And this is a procedure in case you want to keep running the procedure every once in a while
USE [YourDatabaseName]
GO
-- Check if the procedure exists and drop it if so
IF EXISTS (SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[usp_NamingConventionDefaultConstraint]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[usp_NamingConventionDefaultConstraint]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--procedure can be used for specific schema/table/columns or those can be left null to include everything.
CREATE PROC [dbo].[usp_NamingConventionDefaultConstraint]
#SchemaName sysname = 'dbo',
#TableName sysname = NULL,
#ColumnName sysname = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql VARCHAR(max)
DECLARE GetIndexes CURSOR
FOR
SELECT
--here drop the old constraint and create a new one with the old's definition
'ALTER TABLE [' + s.name + '].[' + o.name + '] DROP CONSTRAINT [' + dc.name + ']; ' +
'ALTER TABLE [' + s.name + '].[' + o.name + '] ADD CONSTRAINT [DF_' + o.name + '_' + c.name + '] DEFAULT ' + dc.DEFINITION + ' FOR [' + c.name + '];'
AS dc_alter
FROM dbo.sysobjects do
INNER JOIN dbo.sysobjects o ON do.parent_obj = o.id
INNER JOIN sys.default_constraints dc ON dc.object_id = do.id
INNER JOIN sys.columns c ON c.object_id = o.id
AND c.column_id = dc.parent_column_id
INNER JOIN sys.schemas s ON s.schema_id = dc.schema_id
WHERE o.type = 'U'
-- work only on default constraints
AND do.type = 'D'
AND s.name = #SchemaName
AND o.name = CASE WHEN ISNULL(#TableName,'') = '' THEN o.name ELSE #TableName END
AND c.name = CASE WHEN ISNULL(#ColumnName,'') = '' THEN c.name ELSE #ColumnName END
-- here goes the naming convention you have in mind
--DF_TableName_ColumnName
AND do.name NOT LIKE 'DF_' + o.name + '_' + c.name
ORDER BY o.name
OPEN GetIndexes
FETCH NEXT FROM GetIndexes
INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
IF ISNULL(#sql,'')<>''
BEGIN
EXEC (#sql)
END
FETCH NEXT FROM GetIndexes
INTO #sql
END
CLOSE GetIndexes
DEALLOCATE GetIndexes
END
GO

Know relationships between all the tables of database in SQL Server

I wish to all know how the tables in my database are related to each other (i.e PK/FK/UK) and hence i created a database diagram of all my tables in SQL Server. The diagram that was created was not easily readable and had to scroll (horizontally and sometimes vertically) to see the table on the other end.
In short SQL's db diagram are not UI friendly when it comes to knowing relationships between many tables.
My (simple) Question: Is there something like database diagram which can do what db diagram did but in "good" way?
Sometimes, a textual representation might also help; with this query on the system catalog views, you can get a list of all FK relationships and how the link two tables (and what columns they operate on).
SELECT
fk.name 'FK Name',
tp.name 'Parent table',
cp.name, cp.column_id,
tr.name 'Refrenced table',
cr.name, cr.column_id
FROM
sys.foreign_keys fk
INNER JOIN
sys.tables tp ON fk.parent_object_id = tp.object_id
INNER JOIN
sys.tables tr ON fk.referenced_object_id = tr.object_id
INNER JOIN
sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
INNER JOIN
sys.columns cp ON fkc.parent_column_id = cp.column_id AND fkc.parent_object_id = cp.object_id
INNER JOIN
sys.columns cr ON fkc.referenced_column_id = cr.column_id AND fkc.referenced_object_id = cr.object_id
ORDER BY
tp.name, cp.column_id
Dump this into Excel, and you can slice and dice - based on the parent table, the referenced table or anything else.
I find visual guides helpful - but sometimes, textual documentation is just as good (or even better) - just my 2 cents.....
Just another way to retrieve the same data using INFORMATION_SCHEMA
The information schema views included in SQL Server comply with the ISO standard definition for the INFORMATION_SCHEMA.
sqlauthority way
SELECT
K_Table = FK.TABLE_NAME,
FK_Column = CU.COLUMN_NAME,
PK_Table = PK.TABLE_NAME,
PK_Column = PT.COLUMN_NAME,
Constraint_Name = C.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN (
SELECT i1.TABLE_NAME, i2.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
) PT ON PT.TABLE_NAME = PK.TABLE_NAME
---- optional:
ORDER BY
1,2,3,4
WHERE PK.TABLE_NAME='something'WHERE FK.TABLE_NAME='something'
WHERE PK.TABLE_NAME IN ('one_thing', 'another')
WHERE FK.TABLE_NAME IN ('one_thing', 'another')
Or you can look at schemacrawler
This stored procedure will provide you with a hierarchical tree of relationship. Based on this article from Technet. It will also optionally provide you a query for reading or deleting all the related data.
IF OBJECT_ID('GetForeignKeyRelations','P') IS NOT NULL
DROP PROC GetForeignKeyRelations
GO
CREATE PROC GetForeignKeyRelations
#Schemaname Sysname = 'dbo'
,#Tablename Sysname
,#WhereClause NVARCHAR(2000) = ''
,#GenerateDeleteScripts bit = 0
,#GenerateSelectScripts bit = 0
AS
SET NOCOUNT ON
DECLARE #fkeytbl TABLE
(
ReferencingObjectid int NULL
,ReferencingSchemaname Sysname NULL
,ReferencingTablename Sysname NULL
,ReferencingColumnname Sysname NULL
,PrimarykeyObjectid int NULL
,PrimarykeySchemaname Sysname NULL
,PrimarykeyTablename Sysname NULL
,PrimarykeyColumnname Sysname NULL
,Hierarchy varchar(max) NULL
,level int NULL
,rnk varchar(max) NULL
,Processed bit default 0 NULL
);
WITH fkey (ReferencingObjectid,ReferencingSchemaname,ReferencingTablename,ReferencingColumnname
,PrimarykeyObjectid,PrimarykeySchemaname,PrimarykeyTablename,PrimarykeyColumnname,Hierarchy,level,rnk)
AS
(
SELECT
soc.object_id
,scc.name
,soc.name
,convert(sysname,null)
,convert(int,null)
,convert(sysname,null)
,convert(sysname,null)
,convert(sysname,null)
,CONVERT(VARCHAR(MAX), scc.name + '.' + soc.name ) as Hierarchy
,0 as level
,rnk=convert(varchar(max),soc.object_id)
FROM SYS.objects soc
JOIN sys.schemas scc
ON soc.schema_id = scc.schema_id
WHERE scc.name =#Schemaname
AND soc.name =#Tablename
UNION ALL
SELECT sop.object_id
,scp.name
,sop.name
,socp.name
,soc.object_id
,scc.name
,soc.name
,socc.name
,CONVERT(VARCHAR(MAX), f.Hierarchy + ' --> ' + scp.name + '.' + sop.name ) as Hierarchy
,f.level+1 as level
,rnk=f.rnk + '-' + convert(varchar(max),sop.object_id)
FROM SYS.foreign_key_columns sfc
JOIN Sys.Objects sop
ON sfc.parent_object_id = sop.object_id
JOIN SYS.columns socp
ON socp.object_id = sop.object_id
AND socp.column_id = sfc.parent_column_id
JOIN sys.schemas scp
ON sop.schema_id = scp.schema_id
JOIN SYS.objects soc
ON sfc.referenced_object_id = soc.object_id
JOIN SYS.columns socc
ON socc.object_id = soc.object_id
AND socc.column_id = sfc.referenced_column_id
JOIN sys.schemas scc
ON soc.schema_id = scc.schema_id
JOIN fkey f
ON f.ReferencingObjectid = sfc.referenced_object_id
WHERE ISNULL(f.PrimarykeyObjectid,0) <> f.ReferencingObjectid
)
INSERT INTO #fkeytbl
(ReferencingObjectid,ReferencingSchemaname,ReferencingTablename,ReferencingColumnname
,PrimarykeyObjectid,PrimarykeySchemaname,PrimarykeyTablename,PrimarykeyColumnname,Hierarchy,level,rnk)
SELECT ReferencingObjectid,ReferencingSchemaname,ReferencingTablename,ReferencingColumnname
,PrimarykeyObjectid,PrimarykeySchemaname,PrimarykeyTablename,PrimarykeyColumnname,Hierarchy,level,rnk
FROM fkey
SELECT F.Relationshiptree
FROM
(
SELECT DISTINCT Replicate('------',Level) + CASE LEVEL WHEN 0 THEN '' ELSE '>' END + ReferencingSchemaname + '.' + ReferencingTablename 'Relationshiptree'
,RNK
FROM #fkeytbl
) F
ORDER BY F.rnk ASC
-------------------------------------------------------------------------------------------------------------------------------
-- Generate the Delete / Select script
-------------------------------------------------------------------------------------------------------------------------------
DECLARE #Sql VARCHAR(MAX)
DECLARE #RnkSql VARCHAR(MAX)
DECLARE #Jointables TABLE
(
ID INT IDENTITY
,Object_id int
)
DECLARE #ProcessTablename SYSNAME
DECLARE #ProcessSchemaName SYSNAME
DECLARE #JoinConditionSQL VARCHAR(MAX)
DECLARE #Rnk VARCHAR(MAX)
DECLARE #OldTablename SYSNAME
IF #GenerateDeleteScripts = 1 or #GenerateSelectScripts = 1
BEGIN
WHILE EXISTS ( SELECT 1
FROM #fkeytbl
WHERE Processed = 0
AND level > 0 )
BEGIN
SELECT #ProcessTablename = ''
SELECT #Sql = ''
SELECT #JoinConditionSQL = ''
SELECT #OldTablename = ''
SELECT TOP 1 #ProcessTablename = ReferencingTablename
,#ProcessSchemaName = ReferencingSchemaname
,#Rnk = RNK
FROM #fkeytbl
WHERE Processed = 0
AND level > 0
ORDER BY level DESC
SELECT #RnkSql ='SELECT ' + REPLACE (#rnk,'-',' UNION ALL SELECT ')
DELETE FROM #Jointables
INSERT INTO #Jointables
EXEC(#RnkSql)
IF #GenerateDeleteScripts = 1
SELECT #Sql = 'DELETE [' + #ProcessSchemaName + '].[' + #ProcessTablename + ']' + CHAR(10) + ' FROM [' + #ProcessSchemaName + '].[' + #ProcessTablename + ']' + CHAR(10)
IF #GenerateSelectScripts = 1
SELECT #Sql = 'SELECT [' + #ProcessSchemaName + '].[' + #ProcessTablename + '].*' + CHAR(10) + ' FROM [' + #ProcessSchemaName + '].[' + #ProcessTablename + ']' + CHAR(10)
SELECT #JoinConditionSQL = #JoinConditionSQL
+ CASE
WHEN #OldTablename <> f.PrimarykeyTablename THEN 'JOIN [' + f.PrimarykeySchemaname + '].[' + f.PrimarykeyTablename + '] ' + CHAR(10) + ' ON '
ELSE ' AND '
END
+ ' [' + f.PrimarykeySchemaname + '].[' + f.PrimarykeyTablename + '].[' + f.PrimarykeyColumnname + '] = [' + f.ReferencingSchemaname + '].[' + f.ReferencingTablename + '].[' + f.ReferencingColumnname + ']' + CHAR(10)
, #OldTablename = CASE
WHEN #OldTablename <> f.PrimarykeyTablename THEN f.PrimarykeyTablename
ELSE #OldTablename
END
FROM #fkeytbl f
JOIN #Jointables j
ON f.Referencingobjectid = j.Object_id
WHERE charindex(f.rnk + '-',#Rnk + '-') <> 0
AND F.level > 0
ORDER BY J.ID DESC
SELECT #Sql = #Sql + #JoinConditionSQL
IF LTRIM(RTRIM(#WhereClause)) <> ''
SELECT #Sql = #Sql + ' WHERE (' + #WhereClause + ')'
PRINT #SQL
PRINT CHAR(10)
UPDATE #fkeytbl
SET Processed = 1
WHERE ReferencingTablename = #ProcessTablename
AND rnk = #Rnk
END
IF #GenerateDeleteScripts = 1
SELECT #Sql = 'DELETE FROM [' + #Schemaname + '].[' + #Tablename + ']'
IF #GenerateSelectScripts = 1
SELECT #Sql = 'SELECT * FROM [' + #Schemaname + '].[' + #Tablename + ']'
IF LTRIM(RTRIM(#WhereClause)) <> ''
SELECT #Sql = #Sql + ' WHERE ' + #WhereClause
PRINT #SQL
END
SET NOCOUNT OFF
go
Microsoft Visio is probably the best I've came across, although as far as I know it won't automatically generate based on your relationships.
EDIT: try this in Visio, could give you what you need http://office.microsoft.com/en-us/visio-help/reverse-engineering-an-existing-database-HA001182257.aspx
My solution is based on #marc_s solution, i just concatenated columns in cases that a constraint is based on more than one column:
SELECT
FK.[name] AS ForeignKeyConstraintName
,SCHEMA_NAME(FT.schema_id) + '.' + FT.[name] AS ForeignTable
,STUFF(ForeignColumns.ForeignColumns, 1, 2, '') AS ForeignColumns
,SCHEMA_NAME(RT.schema_id) + '.' + RT.[name] AS ReferencedTable
,STUFF(ReferencedColumns.ReferencedColumns, 1, 2, '') AS ReferencedColumns
FROM
sys.foreign_keys FK
INNER JOIN sys.tables FT
ON FT.object_id = FK.parent_object_id
INNER JOIN sys.tables RT
ON RT.object_id = FK.referenced_object_id
CROSS APPLY
(
SELECT
', ' + iFC.[name] AS [text()]
FROM
sys.foreign_key_columns iFKC
INNER JOIN sys.columns iFC
ON iFC.object_id = iFKC.parent_object_id
AND iFC.column_id = iFKC.parent_column_id
WHERE
iFKC.constraint_object_id = FK.object_id
ORDER BY
iFC.[name]
FOR XML PATH('')
) ForeignColumns (ForeignColumns)
CROSS APPLY
(
SELECT
', ' + iRC.[name]AS [text()]
FROM
sys.foreign_key_columns iFKC
INNER JOIN sys.columns iRC
ON iRC.object_id = iFKC.referenced_object_id
AND iRC.column_id = iFKC.referenced_column_id
WHERE
iFKC.constraint_object_id = FK.object_id
ORDER BY
iRC.[name]
FOR XML PATH('')
) ReferencedColumns (ReferencedColumns)
If you have LINQPad (it's free), this script I just wrote will list every possible path between every table in your database.
Given the following database:
...the script will produce the following output:
Or you can set the longestOnly flag at the top of the script, and it will just output the longest paths:
And here's the script:
var longestOnly = true;
var pathLists = new List<List<string>>();
foreach (var table in Mapping.GetTables()) {
var subPaths = new List<string>();
pathLists.Add(subPaths);
subPaths.Add(table.TableName);
Go(table, subPaths);
}
var pathStrings = pathLists
.Select(p => string.Join(", ", p))
.Distinct()
.OrderBy(p => p)
.ToList();
if (longestOnly) {
pathStrings.RemoveAll(z => pathStrings.Any(i => i != z && i.Contains(z)));
} else {
pathStrings.RemoveAll(z => pathStrings.Any(i => i != z && i.StartsWith(z)));
}
pathStrings.Dump();
void Go(System.Data.Linq.Mapping.MetaTable table, List<string> paths)
{
foreach (var association in table.RowType.Associations) {
var subPaths = paths.Concat(new List<string>()).ToList(); // create a copy
pathLists.Add(subPaths);
var subPathTableName = association.OtherType.Table.TableName;
if (!subPaths.Contains(subPathTableName)) {
subPaths.Add(subPathTableName);
var subPathTable = Mapping.GetTable(association.OtherMember.DeclaringType.Type);
if (subPathTable != null) {
Go(subPathTable, subPaths);
}
}
}
}
For a complex database, this can take a surprisingly long time to complete, and will return a surprisingly large list of results. I needed to write this for work, and the end result left me feeling pretty defeated. :)
I couldn't find anything else that would do this, so I'm pretty happy with it, though.
All suggestions thus far have shown relationships between entities via primary and foreign keys. Occasionally, it may be useful to also identify relationships via dependencies. I found the need for this to identify the relationships between views and tables when building network graph visualizations.
select distinct
v.name as referencer_name,
V.type_desc as referencer_type,
o.name as referenced_entity_name,
o.type_desc as referenced_entity_type
from sys.views v
join sys.sql_expression_dependencies d
on d.referencing_id = v.object_id
and d.referenced_id is not null
join sys.objects o
on o.object_id = d.referenced_id
order by referencer_name;
The above code results in the following table:
This can be further extended, using python, to generate network graphs to visually see linkages.
select * from information_schema.REFERENTIAL_CONSTRAINTS where
UNIQUE_CONSTRAINT_SCHEMA = 'SCHEMA_NAME'
This will list the constraints with SCHEMA_NAMEenter image description here

Easiest Way To Diff Two Table Schemas In SQL Server 2008?

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/