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

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/

Related

Pass table name as parameter

I want to pass the table name as parameter and get all columns size of that table altered by column length + 50.
alter PROCEDURE dbo.testsp
#tablename varchar(100)
AS
BEGIN
DECLARE
#totalWeight TABLE (columnnames nvarchar(100));
INSERT INTO #totalWeight (columnnames)
SELECT name FROM sys.columns
WHERE object_id = object_id(#tablename);
END
GO
This query returns your alteration statements in the last column. You can then run them through dynamic sql.
SELECT
tbl.name 'Table Name',
c.name 'Column Name',
t.name,
c.max_length 'Max Length in Bytes',
c.precision ,
c.scale ,
c.is_nullable,
q1.*,
'alter table ['
+tbl.name
+'] alter column ['
+c.name
+'] '
+t.name
+'('+
convert(nvarchar(1000), CASE WHEN t.name IN ('nchar','nvarchar') THEN c.max_length/2 ELSE c.max_length END +50)
+') '
+ case c.is_nullable when 0 then ' not null' else ' null' end
as 'Alteration statement'
FROM
sys.columns c
INNER JOIN
sys.types t ON c.user_type_id = t.user_type_id
inner join sys.tables tbl on tbl.object_id=c.object_id
CROSS APPLY
(
select
t.name +
CASE WHEN t.name IN ('char', 'varchar','nchar','nvarchar') THEN '('+
CASE WHEN c.max_length=-1 THEN 'MAX'
ELSE CONVERT(VARCHAR(4),
CASE WHEN t.name IN ('nchar','nvarchar')
THEN c.max_length/2 ELSE c.max_length END )
END +')'
WHEN t.name IN ('decimal','numeric')
THEN '('+ CONVERT(VARCHAR(4),c.precision)+','
+ CONVERT(VARCHAR(4),c.Scale)+')'
ELSE '' END
+ case c.is_nullable when 0 then ' not null' else ' null' end
as [DDL name before]
)q1
where t.name IN ('char', 'varchar','nchar','nvarchar') and c.max_length>-1
Run the following example of query, it creates a bunch of ALTER statements. Copy-paste the result into a new query window and run it again. Add other data types from sys.types if need.
SELECT 'ALTER TABLE [' + s.name + '].[' + t.name + '] ALTER COLUMN [' +
c.name + '] ' + ty.name + '(' + CAST(c.max_length + 50 AS nvarchar(10)) + ') ' +
CASE WHEN c.is_nullable = 1 THEN 'NULL' ELSE 'NOT NULL' END + ';'
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.types ty ON c.system_type_id = ty.system_type_id
WHERE t.type = 'U' AND
c.system_type_id IN (167 /*varchar*/, 231 /*nvarchar*/)

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

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

INFORMATION_SCHEMA.columns won't list two columns

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'',

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

What is the equivalent of 'describe table' in SQL Server?

I have a SQL Server database and I want to know what columns and types it has. I'd prefer to do this through a query rather than using a GUI like Enterprise Manager. Is there a way to do this?
You can use the sp_columns stored procedure:
exec sp_columns MyTable
There are a few methods to get metadata about a table:
EXEC sp_help tablename
Will return several result sets, describing the table, it's columns and constraints.
The INFORMATION_SCHEMA views will give you the information you want, though unfortunately you have to query the views and join them manually.
Just in case you don't want to use stored proc, here's a simple query version
select *
from information_schema.columns
where table_name = 'aspnet_Membership'
order by ordinal_position
You can use following: sp_help tablename
Example: sp_help Customer
OR Use Shortcut Keys
Select the desired table and press ALT+F1.
Example: Customer Press ALT+F1.
Use this Query
Select * From INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME = 'TABLENAME'
In addition to the ways shown in other answers, you can use
SELECT TOP 0 * FROM table_name
This will give you the name of each column with no results in them, and completes almost instantly with minimal overhead.
Please use the following sql query; this worked for my case.
select * FROM INFORMATION_SCHEMA.Columns where table_name = 'tablename';
Just select table and press Alt+F1,
it will show all the information about table like Column name, datatype, keys etc.
The SQL Server equivalent to Oracle's describe command is the stored proc sp_help
The describe command gives you the information about the column names, types, length, etc.
In SQL Server, let's say you want to describe a table 'mytable' in schema 'myschema' in the database 'mydb', you can do following:
USE mydb;
exec sp_help 'myschema.mytable';
I wrote an sql*plus DESC(RIBE) like select (displays the column comments, too) in t-sql:
USE YourDB
GO
DECLARE #objectName NVARCHAR(128) = 'YourTable';
SELECT
a.[NAME]
,a.[TYPE]
,a.[CHARSET]
,a.[COLLATION]
,a.[NULLABLE]
,a.[DEFAULT]
,b.[COMMENTS]
-- ,a.[ORDINAL_POSITION]
FROM
(
SELECT
COLUMN_NAME AS [NAME]
,CASE DATA_TYPE
WHEN 'char' THEN DATA_TYPE + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')'
WHEN 'numeric' THEN DATA_TYPE + '(' + CAST(NUMERIC_PRECISION AS VARCHAR) + ', ' + CAST(NUMERIC_SCALE AS VARCHAR) + ')'
WHEN 'nvarchar' THEN DATA_TYPE + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')'
WHEN 'varbinary' THEN DATA_TYPE + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')'
WHEN 'varchar' THEN DATA_TYPE + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')'
ELSE DATA_TYPE
END AS [TYPE]
,CHARACTER_SET_NAME AS [CHARSET]
,COLLATION_NAME AS [COLLATION]
,IS_NULLABLE AS [NULLABLE]
,COLUMN_DEFAULT AS [DEFAULT]
,ORDINAL_POSITION
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = #objectName
) a
FULL JOIN
(
SELECT
CAST(value AS NVARCHAR) AS [COMMENTS]
,CAST(objname AS NVARCHAR) AS [NAME]
FROM
::fn_listextendedproperty ('MS_Description', 'user', 'dbo', 'table', #objectName, 'column', default)
) b
ON a.NAME COLLATE YourCollation = b.NAME COLLATE YourCollation
ORDER BY
a.[ORDINAL_POSITION];
The above mentioned select can be used in a system marked stored procedure and it can be called from any database of your instance on a simple way:
USE master;
GO
IF OBJECT_ID('sp_desc', 'P') IS NOT NULL
DROP PROCEDURE sp_desc
GO
CREATE PROCEDURE sp_desc (
#tableName nvarchar(128)
) AS
BEGIN
DECLARE #dbName sysname;
DECLARE #schemaName sysname;
DECLARE #objectName sysname;
DECLARE #objectID int;
DECLARE #tmpTableName varchar(100);
DECLARE #sqlCmd nvarchar(4000);
SELECT #dbName = PARSENAME(#tableName, 3);
IF #dbName IS NULL SELECT #dbName = DB_NAME();
SELECT #schemaName = PARSENAME(#tableName, 2);
IF #schemaName IS NULL SELECT #schemaName = SCHEMA_NAME();
SELECT #objectName = PARSENAME(#tableName, 1);
IF #objectName IS NULL
BEGIN
PRINT 'Object is missing from your function call!';
RETURN;
END;
SELECT #objectID = OBJECT_ID(#dbName + '.' + #schemaName + '.' + #objectName);
IF #objectID IS NULL
BEGIN
PRINT 'Object [' + #dbName + '].[' + #schemaName + '].[' + #objectName + '] does not exist!';
RETURN;
END;
SELECT #tmpTableName = '#tmp_DESC_' + CAST(##SPID AS VARCHAR) + REPLACE(REPLACE(REPLACE(REPLACE(CAST(CONVERT(CHAR, GETDATE(), 121) AS VARCHAR), '-', ''), ' ', ''), ':', ''), '.', '');
--PRINT #tmpTableName;
SET #sqlCmd = '
USE ' + #dbName + '
CREATE TABLE ' + #tmpTableName + ' (
[NAME] nvarchar(128) NOT NULL
,[TYPE] varchar(50)
,[CHARSET] varchar(50)
,[COLLATION] varchar(50)
,[NULLABLE] varchar(3)
,[DEFAULT] nvarchar(4000)
,[COMMENTS] nvarchar(3750));
INSERT INTO ' + #tmpTableName + '
SELECT
a.[NAME]
,a.[TYPE]
,a.[CHARSET]
,a.[COLLATION]
,a.[NULLABLE]
,a.[DEFAULT]
,b.[COMMENTS]
FROM
(
SELECT
COLUMN_NAME AS [NAME]
,CASE DATA_TYPE
WHEN ''char'' THEN DATA_TYPE + ''('' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + '')''
WHEN ''numeric'' THEN DATA_TYPE + ''('' + CAST(NUMERIC_PRECISION AS VARCHAR) + '', '' + CAST(NUMERIC_SCALE AS VARCHAR) + '')''
WHEN ''nvarchar'' THEN DATA_TYPE + ''('' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + '')''
WHEN ''varbinary'' THEN DATA_TYPE + ''('' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + '')''
WHEN ''varchar'' THEN DATA_TYPE + ''('' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + '')''
ELSE DATA_TYPE
END AS [TYPE]
,CHARACTER_SET_NAME AS [CHARSET]
,COLLATION_NAME AS [COLLATION]
,IS_NULLABLE AS [NULLABLE]
,COLUMN_DEFAULT AS [DEFAULT]
,ORDINAL_POSITION
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = ''' + #objectName + '''
) a
FULL JOIN
(
SELECT
CAST(value AS NVARCHAR) AS [COMMENTS]
,CAST(objname AS NVARCHAR) AS [NAME]
FROM
::fn_listextendedproperty (''MS_Description'', ''user'', ''' + #schemaName + ''', ''table'', ''' + #objectName + ''', ''column'', default)
) b
ON a.NAME COLLATE Hungarian_CI_AS = b.NAME COLLATE Hungarian_CI_AS
ORDER BY
a.[ORDINAL_POSITION];
SELECT * FROM ' + #tmpTableName + ';'
--PRINT #sqlCmd;
EXEC sp_executesql #sqlCmd;
RETURN;
END;
GO
EXEC sys.sp_MS_marksystemobject sp_desc
GO
To execute the procedure type:
EXEC sp_desc 'YourDB.YourSchema.YourTable';
If you want to get a description an object of the current database (and schema) simple type:
EXEC sp_desc 'YourTable';
As sp_desc is a system marked procedure, you can even leave the exec command, too (not recommended anyway):
sp_desc 'YourTable';
You can use the sp_help 'TableName'
try it:
EXEC [ServerName].[DatabaseName].dbo.sp_columns 'TableName'
and you can get some table structure information, such as:
TABLE_QUALIFIER, TABLE_OWNER, TABLE_NAME, COLUMN_NAME, DATA_TYPE, TYPE_NAME...
In addition to above questions, if we have table in DB like db_name.dbo.table_name, we may use following steps
Connect with DB
USE db_name;
Use EXEC sp_help and don't forget to put table name as 'dbo.tablename' if you have dbo as schema.
exec sp_help 'dbo.table_name'
This should work!
I tried this and it's working for me
exec sp_help TABLE_NAME
First connect to your DB,
use DB_name
Then
exec sp_help 'Production.Et_Issue'
here 'production' is the schema name. If you dont have a schema,
you may simply write sp_help table_name
The problem with those answers is that you're missing the key info.
While this is a bit messy this is a quick version I came up with to make sure it contains the same info the MySQL Describe displays.
Select SC.name AS 'Field', ISC.DATA_TYPE AS 'Type', ISC.CHARACTER_MAXIMUM_LENGTH AS 'Length', SC.IS_NULLABLE AS 'Null', I.is_primary_key AS 'Key', SC.is_identity AS 'Identity'
From sys.columns AS SC
LEFT JOIN sys.index_columns AS IC
ON IC.object_id = OBJECT_ID('dbo.Expenses') AND
IC.column_id = SC.column_id
LEFT JOIN sys.indexes AS I
ON I.object_id = OBJECT_ID('dbo.Expenses') AND
IC.index_id = I.index_id
LEFT JOIN information_schema.columns ISC
ON ISC.TABLE_NAME = 'Expenses'
AND ISC.COLUMN_NAME = SC.name
WHERE SC.object_id = OBJECT_ID('dbo.Expenses')
This is the code I use within the EntityFramework Reverse POCO Generator (available here)
Table SQL:
SELECT c.TABLE_SCHEMA AS SchemaName,
c.TABLE_NAME AS TableName,
t.TABLE_TYPE AS TableType,
c.ORDINAL_POSITION AS Ordinal,
c.COLUMN_NAME AS ColumnName,
CAST(CASE WHEN IS_NULLABLE = 'YES' THEN 1
ELSE 0
END AS BIT) AS IsNullable,
DATA_TYPE AS TypeName,
ISNULL(CHARACTER_MAXIMUM_LENGTH, 0) AS [MaxLength],
CAST(ISNULL(NUMERIC_PRECISION, 0) AS INT) AS [Precision],
ISNULL(COLUMN_DEFAULT, '') AS [Default],
CAST(ISNULL(DATETIME_PRECISION, 0) AS INT) AS DateTimePrecision,
ISNULL(NUMERIC_SCALE, 0) AS Scale,
CAST(COLUMNPROPERTY(OBJECT_ID(QUOTENAME(c.TABLE_SCHEMA) + '.' + QUOTENAME(c.TABLE_NAME)), c.COLUMN_NAME, 'IsIdentity') AS BIT) AS IsIdentity,
CAST(CASE WHEN COLUMNPROPERTY(OBJECT_ID(QUOTENAME(c.TABLE_SCHEMA) + '.' + QUOTENAME(c.TABLE_NAME)), c.COLUMN_NAME, 'IsIdentity') = 1 THEN 1
WHEN COLUMNPROPERTY(OBJECT_ID(QUOTENAME(c.TABLE_SCHEMA) + '.' + QUOTENAME(c.TABLE_NAME)), c.COLUMN_NAME, 'IsComputed') = 1 THEN 1
WHEN DATA_TYPE = 'TIMESTAMP' THEN 1
ELSE 0
END AS BIT) AS IsStoreGenerated,
CAST(CASE WHEN pk.ORDINAL_POSITION IS NULL THEN 0
ELSE 1
END AS BIT) AS PrimaryKey,
ISNULL(pk.ORDINAL_POSITION, 0) PrimaryKeyOrdinal,
CAST(CASE WHEN fk.COLUMN_NAME IS NULL THEN 0
ELSE 1
END AS BIT) AS IsForeignKey
FROM INFORMATION_SCHEMA.COLUMNS c
LEFT OUTER JOIN (SELECT u.TABLE_SCHEMA,
u.TABLE_NAME,
u.COLUMN_NAME,
u.ORDINAL_POSITION
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE u
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
ON u.TABLE_SCHEMA = tc.CONSTRAINT_SCHEMA
AND u.TABLE_NAME = tc.TABLE_NAME
AND u.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
WHERE CONSTRAINT_TYPE = 'PRIMARY KEY') pk
ON c.TABLE_SCHEMA = pk.TABLE_SCHEMA
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.COLUMN_NAME = pk.COLUMN_NAME
LEFT OUTER JOIN (SELECT DISTINCT
u.TABLE_SCHEMA,
u.TABLE_NAME,
u.COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE u
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
ON u.TABLE_SCHEMA = tc.CONSTRAINT_SCHEMA
AND u.TABLE_NAME = tc.TABLE_NAME
AND u.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY') fk
ON c.TABLE_SCHEMA = fk.TABLE_SCHEMA
AND c.TABLE_NAME = fk.TABLE_NAME
AND c.COLUMN_NAME = fk.COLUMN_NAME
INNER JOIN INFORMATION_SCHEMA.TABLES t
ON c.TABLE_SCHEMA = t.TABLE_SCHEMA
AND c.TABLE_NAME = t.TABLE_NAME
WHERE c.TABLE_NAME NOT IN ('EdmMetadata', '__MigrationHistory')
Foreign Key SQL:
SELECT FK.name AS FK_Table,
FkCol.name AS FK_Column,
PK.name AS PK_Table,
PkCol.name AS PK_Column,
OBJECT_NAME(f.object_id) AS Constraint_Name,
SCHEMA_NAME(FK.schema_id) AS fkSchema,
SCHEMA_NAME(PK.schema_id) AS pkSchema,
PkCol.name AS primarykey,
k.constraint_column_id AS ORDINAL_POSITION
FROM sys.objects AS PK
INNER JOIN sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS k
ON k.constraint_object_id = f.object_id
INNER JOIN sys.indexes AS i
ON f.referenced_object_id = i.object_id
AND f.key_index_id = i.index_id
ON PK.object_id = f.referenced_object_id
INNER JOIN sys.objects AS FK
ON f.parent_object_id = FK.object_id
INNER JOIN sys.columns AS PkCol
ON f.referenced_object_id = PkCol.object_id
AND k.referenced_column_id = PkCol.column_id
INNER JOIN sys.columns AS FkCol
ON f.parent_object_id = FkCol.object_id
AND k.parent_column_id = FkCol.column_id
ORDER BY FK_Table, FK_Column
Extended Properties:
SELECT s.name AS [schema],
t.name AS [table],
c.name AS [column],
value AS [property]
FROM sys.extended_properties AS ep
INNER JOIN sys.tables AS t
ON ep.major_id = t.object_id
INNER JOIN sys.schemas AS s
ON s.schema_id = t.schema_id
INNER JOIN sys.columns AS c
ON ep.major_id = c.object_id
AND ep.minor_id = c.column_id
WHERE class = 1
ORDER BY t.name
I like this format:
name DataType Collation Constraints PK FK Comment
id int NOT NULL IDENTITY PK Order Line Id
pid int NOT NULL tbl_orders Order Id
itemCode varchar(10) Latin1_General_CI_AS NOT NULL Product Code
So I have used this:
DECLARE #tname varchar(100) = 'yourTableName';
SELECT col.name,
CASE typ.name
WHEN 'nvarchar' THEN 'nvarchar('+CAST((col.max_length / 2) as varchar)+')'
WHEN 'varchar' THEN 'varchar('+CAST(col.max_length as varchar)+')'
WHEN 'char' THEN 'char('+CAST(col.max_length as varchar)+')'
WHEN 'nchar' THEN 'nchar('+CAST((col.max_length / 2) as varchar)+')'
WHEN 'binary' THEN 'binary('+CAST(col.max_length as varchar)+')'
WHEN 'varbinary' THEN 'varbinary('+CAST(col.max_length as varchar)+')'
WHEN 'numeric' THEN 'numeric('+CAST(col.precision as varchar)+(CASE WHEN col.scale = 0 THEN '' ELSE ','+CAST(col.scale as varchar) END) +')'
WHEN 'decimal' THEN 'decimal('+CAST(col.precision as varchar)+(CASE WHEN col.scale = 0 THEN '' ELSE ','+CAST(col.scale as varchar) END) +')'
ELSE typ.name
END DataType,
ISNULL(col.collation_name,'') Collation,
CASE WHEN col.is_nullable = 0 THEN 'NOT NULL ' ELSE '' END + CASE WHEN col.is_identity = 1 THEN 'IDENTITY' ELSE '' END Constraints,
ISNULL((SELECT 'PK'
FROM sys.key_constraints kc INNER JOIN
sys.tables tb ON tb.object_id = kc.parent_object_id INNER JOIN
sys.indexes si ON si.name = kc.name INNER JOIN
sys.index_columns sic ON sic.index_id = si.index_id AND sic.object_id = si.object_id
WHERE kc.type = 'PK'
AND tb.name = #tname
AND sic.column_id = col.column_id),'') PK,
ISNULL((SELECT (SELECT name FROM sys.tables st WHERE st.object_id = fkc.referenced_object_id)
FROM sys.foreign_key_columns fkc INNER JOIN
sys.columns c ON c.column_id = fkc.parent_column_id AND fkc.parent_object_id = c.object_id INNER JOIN
sys.tables t ON t.object_id = c.object_id
WHERE t.name = tab.name
AND c.name = col.name),'') FK,
ISNULL((SELECT value
FROM sys.extended_properties
WHERE major_id = tab.object_id
AND minor_id = col.column_id),'') Comment
FROM sys.columns col INNER JOIN
sys.tables tab ON tab.object_id = col.object_id INNER JOIN
sys.types typ ON typ.system_type_id = col.system_type_id
WHERE tab.name = #tname
AND typ.name != 'sysname'
ORDER BY col.column_id;
use
SELECT COL_LENGTH('tablename', 'colname')
None of other solution worked for me.
SELECT C.COLUMN_NAME, C.IS_NULLABLE, C.DATA_TYPE, TC.CONSTRAINT_TYPE, C.COLUMN_DEFAULT
FROM INFORMATION_SCHEMA.COLUMNS AS C
FULL JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS CC ON C.COLUMN_NAME = CC.COLUMN_NAME
FULL JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON CC.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
WHERE C.TABLE_NAME = '<Table Name>';
Sample Output
If you are using FirstResponderKit from Brent Ozar team, you can run this query also:
exec sp_blitzindex #tablename='MyTable'
It will return all information about table:
indexes with their usage statistics(reads, writes, locks, etc), space
used and other
missing indexes
columns
foreign keys
statistics contents
Of course it's not a system and not so universal stp like sp_help or sp_columns, but it returns all possible information about your table and I think it's worth creating it at your environment and mentioning it here.
Just double click on the table name and press Alt+F1
CREATE PROCEDURE [dbo].[describe]
(
#SearchStr nvarchar(max)
)
AS
BEGIN
SELECT
CONCAT([COLUMN_NAME],' ',[DATA_TYPE],' ',[CHARACTER_MAXIMUM_LENGTH],' ',
(SELECT CASE [IS_NULLABLE] WHEN 'NO' THEN 'NOT NULL' ELSE 'NULL' END),
(SELECT CASE WHEN [COLUMN_DEFAULT] IS NULL THEN '' ELSE CONCAT(' DEFAULT ',[COLUMN_DEFAULT]) END)
) AS DESCRIPTION
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME LIKE #SearchStr
END
The query below will provide similar output as the info() function in python, Pandas library.
USE [Database_Name]
IF OBJECT_ID('tempdo.dob.#primary_key', 'U') IS NOT NULL DROP TABLE #primary_key
SELECT
CONS_T.TABLE_CATALOG,
CONS_T.TABLE_SCHEMA,
CONS_T.TABLE_NAME,
CONS_C.COLUMN_NAME,
CONS_T.CONSTRAINT_TYPE,
CONS_T.CONSTRAINT_NAME
INTO #primary_key
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS CONS_T
JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS CONS_C ON CONS_C.CONSTRAINT_NAME= CONS_T.CONSTRAINT_NAME
SELECT
SMA.name AS [Schema Name],
ST.name AS [Table Name],
SC.column_id AS [Column Order],
SC.name AS [Column Name],
PKT.CONSTRAINT_TYPE,
PKT.CONSTRAINT_NAME,
SC.system_type_id,
STP.name AS [Data Type],
SC.max_length,
SC.precision,
SC.scale,
SC.is_nullable,
SC.is_masked
FROM sys.tables AS ST
JOIN sys.schemas AS SMA ON SMA.schema_id = ST.schema_id
JOIN sys.columns AS SC ON SC.object_id = ST.object_id
JOIN sys.types AS STP ON STP.system_type_id = SC.system_type_id
LEFT JOIN #primary_key AS PKT ON PKT.TABLE_SCHEMA = SMA.name
AND PKT.TABLE_NAME = ST.name
AND PKT.COLUMN_NAME = SC.name
ORDER BY ST.name ASC, SMA.name ASC