ScriptTable_TF is a table-valued function for Microsoft SQL Server that generates a T-SQL script for recreating an existing table in another database. Instead of scripting the table by hand or through SQL Server Management Studio, you call the function with the table name and the target database name, and it returns the script as rows that can be read, copied and executed.
What the function does
The function reads the table structure with sys.dm_exec_describe_first_result_set and joins it with the system views sys.indexes, sys.index_columns, sys.computed_columns and sys.default_constraints. For every column of the source table it returns one row with three columns:
column_ordinal– the order of the statement in the script,sql– the T-SQL statement,cname– the column name.
The first column is created with CREATE TABLE, and every following column is added with ALTER TABLE ... ADD. For each column the script keeps:
- the data type,
- the collation, when the column has one,
NULLorNOT NULL,IDENTITY(1,1)for identity columns,- the definition of computed columns,
- the primary key, as a
NONCLUSTEREDconstraint namedPK_table_column, - the default value, as a constraint named
DF_table_column.
The first and the last row of the result wrap the whole script in a transaction with SET XACT_ABORT ON and a TRY...CATCH block. If any statement fails, the table creation is rolled back completely.
Parameters
@TABLE_NAME– the name of the source table in thedboschema of the current database,@ScriptForDb– the name of the database the script is generated for.
Constraint names are written as delimited identifiers in double quotes, so they may contain any character that the table or column name contains. The script therefore has to be executed with SET QUOTED_IDENTIFIER ON, which is the default in SQL Server Management Studio.
Function code
The code starts with ALTER FUNCTION; on the first installation replace it with CREATE FUNCTION.
ALTER FUNCTION [dbo].[ScriptTable_TF](
@TABLE_NAME nvarchar(127),
@ScriptForDb nvarchar(127))
returns @t table(column_ordinal int, sql nvarchar(4000), cname nvarchar(127))
as
begin
insert into @t(column_ordinal, sql, cname) values(0, 'SET XACT_ABORT ON; BEGIN TRAN BEGIN TRY', '')
;WITH A AS
(
SELECT
c.is_identity_column
, c.column_ordinal
, c.name
, c.is_nullable
, c.system_type_name
, c.collation_name
, c.is_xml_document
, c.is_part_of_unique_key
, c.is_computed_column
, cc.definition computed_definition
, dc.definition default_constraint
, (SELECT sc.name AS ColumnName
FROM sys.indexes AS i INNER JOIN
sys.index_columns AS ic ON i.OBJECT_ID = ic.OBJECT_ID
AND i.index_id = ic.index_id JOIN
sys.columns sc on ic.column_id = sc.column_id and ic.object_id = sc.object_id
WHERE i.is_primary_key = 1
AND OBJECT_NAME(I.object_id) = @TABLE_NAME
AND sc.name collate Latin1_General_CI_AS = c.name collate Latin1_General_CI_AS
) pk
FROM sys.dm_exec_describe_first_result_set('select * from [dbo].[' + @TABLE_NAME +']', NULL, 0) c left JOIN
sys.computed_columns cc on OBJECT_NAME(cc.object_id) = @TABLE_NAME and cc.name collate Latin1_General_CI_AS = c.name collate Latin1_General_CI_AS left join
sys.default_constraints dc on OBJECT_NAME(parent_object_id) = @TABLE_NAME and c.name = COL_NAME(parent_object_id, parent_column_id)
)
insert into @t(column_ordinal, sql, cname)
SELECT TOP 100 PERCENT column_ordinal,
CASE WHEN column_ordinal = 1 THEN
'CREATE TABLE "' + @ScriptForDb + '".dbo."' + @TABLE_NAME + '" ("' + NAME + '"'
ELSE
'ALTER TABLE "' + @ScriptForDb + '".dbo."' + @TABLE_NAME + '" ADD "' + NAME + '"'
END +
CASE WHEN is_computed_column = 1 THEN
' AS ' + computed_definition COLLATE Latin1_General_CI_AS
ELSE
' ' + system_type_name +
CASE WHEN collation_name IS NOT NULL THEN
' COLLATE ' + collation_name
ELSE
''
END +
CASE WHEN is_nullable = 0 THEN
' NOT'
ELSE
''
END +
' NULL'+
CASE WHEN is_identity_column = 1 THEN
' IDENTITY(1,1)'
ELSE
''
END
END +
CASE WHEN column_ordinal = 1 THEN
')'
ELSE
''
END +
CASE WHEN pk IS NOT NULL THEN
';ALTER TABLE "' + @ScriptForDb + '"."dbo"."' + @TABLE_NAME + '" ADD CONSTRAINT "PK_' + @TABLE_NAME + '_' + NAME + '" PRIMARY KEY NONCLUSTERED ("' + NAME + '") '
WHEN default_constraint IS NOT NULL THEN
';ALTER TABLE "' + @ScriptForDb + '"."dbo"."' + @TABLE_NAME + '" ADD CONSTRAINT "DF_' + @TABLE_NAME + '_' + NAME + '" DEFAULT ' + default_constraint + ' FOR "' + NAME + '"'
ELSE
''
END
SQL, NAME
FROM A
ORDER BY column_ordinal
insert into @t(column_ordinal, sql, cname) values((select count(*) from @t) + 1, 'END TRY BEGIN CATCH IF (XACT_STATE()) = -1 BEGIN ROLLBACK TRAN; THROW; END END CATCH; IF (XACT_STATE()) = 1 COMMIT TRAN;', '')
return
end
Usage example
The rows have to be read in the order of column_ordinal:
SELECT sql
FROM dbo.ScriptTable_TF('Customers', 'TargetDb')
ORDER BY column_ordinal
The resulting rows are copied, in that order, into a new query window and executed on the server that holds the target database. Since the table name in the script is qualified with the database name, the script can be run from any database on the same server.
Limitations
- only tables in the
dboschema are supported, - foreign keys, indexes other than the primary key,
CHECKconstraints and triggers are not scripted, - the primary key is always created as
NONCLUSTEREDand per column, so a table with a composite primary key needs manual adjustment, IDENTITYalways gets seed and increment1,1, regardless of the original values,- only the structure is scripted, not the data.
For designing, speeding up or migrating a whole database, see database development services.
Leave a Comment