Ho apportato alcune modifiche al lavoro di Gareth, che funziona BTW e penso sia grandioso.Volevo includere la clonazione dei trigger e copiare anche il contenuto delle tabelle. In sostanza, "copia" la maggior parte del tavolo come meglio posso in un colpo solo. Ho incluso l'intero pezzo di codice. Ricorda che questo non è del tutto originale, e non rivendico credito per nessuno dei lavori di Gareth. Spero che questo sia utile per chiunque sia interessato.
CREATE PROCEDURE [dbo].[spCloneTableStructure]
@SourceSchema nvarchar(255)
, @SourceTable nvarchar(255)
, @DestinationSchema nvarchar(255)
, @DestinationTable nvarchar(255)
, @RecreateIfExists bit = 0
AS
BEGIN
/*
Clones an existing table to another table (without data)
Optionally drops and re-creates target table
Copies:
* Structure
* Primary key
* Indexes (including ASC/DESC, included columns, filters)
* Constraints (and unique constraints)
DOES NOT copy:
* Triggers (It seems to do this now)
* File groups
* Probably a lot of other things
Note: Assumes that you name (unique) constraints with the table name in it (in order to not duplicate constraint names)
*/
SET NOCOUNT ON;
BEGIN TRANSACTION
--drop the table
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = @DestinationSchema AND TABLE_NAME = @DestinationTable)
BEGIN
IF @RecreateIfExists = 1
BEGIN
EXEC('DROP TABLE [' + @DestinationSchema + '].[' + @DestinationTable + ']')
END
ELSE
BEGIN
RETURN
END
END
--create the table
EXEC('SELECT TOP (0) * INTO [' + @DestinationSchema + '].[' + @DestinationTable + '] FROM [' + @SourceSchema + '].[' + @SourceTable + ']')
DECLARE @PKSchema nvarchar(255), @PKName nvarchar(255)
SELECT TOP 1 @PKSchema = CONSTRAINT_SCHEMA, @PKName = CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA = @SourceSchema
AND TABLE_NAME = @SourceTable
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
-----------------------------------------------------------------------------------
DECLARE @SourceColumns int
DECLARE @DestinationColumns int
DECLARE @MyColumn int
SELECT @SourceColumns = count(*)
FROM information_schema.columns
WHERE TABLE_NAME = @SourceTable
AND TABLE_SCHEMA = @SourceSchema
SELECT @DestinationColumns = count(*)
FROM information_schema.columns
WHERE TABLE_NAME = @DestinationTable
AND TABLE_SCHEMA = @DestinationSchema
IF @SourceColumns = @DestinationColumns
BEGIN
DECLARE @FullSourceTable varchar(128)
DECLARE @FullDestinationTable varchar(128)
SET @FullSourceTable = @SourceSchema+'.'[email protected]
SET @FullDestinationTable = @DestinationSchema+'.'[email protected]
DECLARE @MySQL varchar(MAX)
DECLARE @MyValues varchar(MAX)
SET @MyColumn = 2
SET @MySQL = 'INSERT INTO '[email protected]+' ('
SET @MyValues = COL_NAME(OBJECT_ID(@FullSourceTable), 1) + ', '
WHILE @MyColumn <= @DestinationColumns --Change this back
BEGIN
SET @MyValues = @MyValues+ COL_NAME(OBJECT_ID(@FullSourceTable), @MyColumn) + ', '
SET @MyColumn = @MyColumn + 1
END
SELECT @MyValues = SUBSTRING(LTRIM(RTRIM(@MyValues)),1,DATALENGTH(LTRIM(RTRIM(@MyValues)))-1)
SET @MySQL = @[email protected]+') '
SET @MySQL = @MySQL+' SELECT '[email protected]+' FROM '[email protected]
--SELECT @MySQL
EXEC(@MySQL)
END
ELSE
BEGIN
RAISERROR('Number of Source and Destination Columns do not match. Cannot continue with copying content.',16,1)
END
-----------------------------------------------------------------------------------
--create primary key
IF NOT @PKSchema IS NULL
AND NOT @PKName IS NULL
BEGIN
DECLARE @PKColumns nvarchar(MAX)
SET @PKColumns = ''
SELECT @PKColumns = @PKColumns + '[' + COLUMN_NAME + '],'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = @SourceTable
AND TABLE_SCHEMA = @SourceSchema
AND CONSTRAINT_SCHEMA = @PKSchema
AND CONSTRAINT_NAME= @PKName
ORDER BY ORDINAL_POSITION
SET @PKColumns = LEFT(@PKColumns, LEN(@PKColumns) - 1)
EXEC('ALTER TABLE [' + @DestinationSchema + '].[' + @DestinationTable + '] ADD CONSTRAINT [PK_' + @DestinationTable + '] PRIMARY KEY CLUSTERED (' + @PKColumns + ')');
END
--create other indexes
DECLARE @IndexId int, @IndexName nvarchar(255), @IsUnique bit, @IsUniqueConstraint bit, @FilterDefinition nvarchar(max)
-------------------------------------------------------------------------------
-- Cursor Start
-------------------------------------------------------------------------------
DECLARE indexcursor CURSOR FOR
SELECT index_id, name, is_unique, is_unique_constraint, filter_definition
FROM sys.indexes
WHERE type = 2
AND object_id = object_id('[' + @SourceSchema + '].[' + @SourceTable + ']')
OPEN indexcursor;
FETCH NEXT FROM indexcursor INTO @IndexId, @IndexName, @IsUnique, @IsUniqueConstraint, @FilterDefinition;
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @Unique nvarchar(255)
DECLARE @KeyColumns nvarchar(max), @IncludedColumns nvarchar(max)
SET @Unique = CASE WHEN @IsUnique = 1 THEN ' UNIQUE ' ELSE '' END
SET @KeyColumns = ''
SET @IncludedColumns = ''
SELECT @KeyColumns = @KeyColumns + '[' + c.name + '] ' + CASE WHEN is_descending_key = 1 THEN 'DESC' ELSE 'ASC' END + ','
FROM sys.index_columns ic
INNER JOIN sys.columns c
ON c.object_id = ic.object_id
AND c.column_id = ic.column_id
WHERE index_id = @IndexId
AND ic.object_id = object_id('[' + @SourceSchema + '].[' + @SourceTable + ']')
AND key_ordinal > 0
ORDER BY index_column_id
SELECT @IncludedColumns = @IncludedColumns + '[' + c.name + '],'
FROM sys.index_columns ic
INNER JOIN sys.columns c
ON c.object_id = ic.object_id
AND c.column_id = ic.column_id
WHERE index_id = @IndexId
AND ic.object_id = object_id('[' + @SourceSchema + '].[' + @SourceTable + ']')
AND key_ordinal = 0
ORDER BY index_column_id
IF LEN(@KeyColumns) > 0
BEGIN
SET @KeyColumns = LEFT(@KeyColumns, LEN(@KeyColumns) - 1)
END
IF LEN(@IncludedColumns) > 0
BEGIN
SET @IncludedColumns = ' INCLUDE (' + LEFT(@IncludedColumns, LEN(@IncludedColumns) - 1) + ')'
END
IF @FilterDefinition IS NULL
BEGIN
SET @FilterDefinition = ''
END
ELSE
BEGIN
SET @FilterDefinition = 'WHERE ' + @FilterDefinition + ' '
END
IF @IsUniqueConstraint = 0
BEGIN
EXEC('CREATE ' + @Unique + ' NONCLUSTERED INDEX [' + @IndexName + '] ON [' + @DestinationSchema + '].[' + @DestinationTable + '] (' + @KeyColumns + ')' + @IncludedColumns + @FilterDefinition)
END
ELSE
BEGIN
SET @IndexName = REPLACE(@IndexName, @SourceTable, @DestinationTable)
EXEC('ALTER TABLE [' + @DestinationSchema + '].[' + @DestinationTable + '] ADD CONSTRAINT [' + @IndexName + '] UNIQUE NONCLUSTERED (' + @KeyColumns + ')');
END
FETCH NEXT FROM indexcursor INTO @IndexId, @IndexName, @IsUnique, @IsUniqueConstraint, @FilterDefinition;
END;
CLOSE indexcursor;
DEALLOCATE indexcursor;
-------------------------------------------------------------------------------
-- Cursor END
-------------------------------------------------------------------------------
--create constraints
DECLARE @ConstraintName nvarchar(max), @CheckClause nvarchar(max)
-------------------------------------------------------------------------------
-- Cursor START
-------------------------------------------------------------------------------
DECLARE constraintcursor CURSOR FOR
SELECT REPLACE(c.CONSTRAINT_NAME, @SourceTable, @DestinationTable), CHECK_CLAUSE
FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE t
INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS c
ON c.CONSTRAINT_SCHEMA = TABLE_SCHEMA
AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME
WHERE TABLE_SCHEMA = @SourceSchema
AND TABLE_NAME = @SourceTable
OPEN constraintcursor;
FETCH NEXT FROM constraintcursor INTO @ConstraintName, @CheckClause;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC('ALTER TABLE [' + @DestinationSchema + '].[' + @DestinationTable + '] WITH CHECK ADD CONSTRAINT [' + @ConstraintName + '] CHECK ' + @CheckClause)
EXEC('ALTER TABLE [' + @DestinationSchema + '].[' + @DestinationTable + '] CHECK CONSTRAINT [' + @ConstraintName + ']')
FETCH NEXT FROM constraintcursor INTO @ConstraintName, @CheckClause;
END;
CLOSE constraintcursor;
DEALLOCATE constraintcursor;
-------------------------------------------------------------------------------
-- Cursor END
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Build Triggers on new table START
-------------------------------------------------------------------------------
DECLARE @TriggerType varchar(32)
DECLARE @CHeader varchar(255)
DECLARE @trigger_name varchar(128)
DECLARE @table_schema varchar(128)
DECLARE @table_name varchar(128)
DECLARE @isupdate tinyint
DECLARE @isdelete tinyint
DECLARE @isinsert tinyint
DECLARE @isafter tinyint
DECLARE @isinsteadof tinyint
DECLARE @disabled tinyint
DECLARE @TriggerCode varchar(MAX)
DECLARE db_cursor CURSOR FOR
SELECT so.name
,(SELECT TOP 1 SCHEMA_NAME(T1.schema_id)
FROM sys.tables AS T1
WHERE T1.name = OBJECT_NAME(parent_obj))
,OBJECT_NAME(parent_obj)
,OBJECTPROPERTY(so.id, 'ExecIsUpdateTrigger')
,OBJECTPROPERTY(so.id, 'ExecIsDeleteTrigger')
,OBJECTPROPERTY(so.id, 'ExecIsInsertTrigger')
,OBJECTPROPERTY(so.id, 'ExecIsAfterTrigger')
,OBJECTPROPERTY(so.id, 'ExecIsInsteadOfTrigger')
,OBJECTPROPERTY(so.id, 'ExecIsTriggerDisabled')
,LTRIM(RTRIM(c.[text]))
FROM sys.sysobjects AS so
INNER JOIN sys.objects o ON so.id = o.object_id
INNER JOIN sys.syscomments AS c ON o.object_id = c.id
WHERE so.type = 'TR'
AND OBJECT_NAME(parent_object_id) = @SourceTable
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @trigger_name, @table_schema, @table_name, @isupdate, @isdelete, @isinsert, @isafter, @isinsteadof, @disabled, @TriggerCode
WHILE @@FETCH_STATUS = 0
BEGIN
--SELECT @trigger_name, @table_schema, @table_name, @isupdate, @isdelete, @isinsert, @isafter, @isinsteadof, @disabled, @TriggerCode
SET @TriggerCode = LTRIM(RTRIM(REPLACE(@TriggerCode, CHAR(13)+CHAR(13)+CHAR(13), CHAR(13))))
SET @TriggerCode = LTRIM(RTRIM(REPLACE(@TriggerCode, CHAR(13)+CHAR(13), CHAR(13))))
-------------------------------------------------------------------------------
--Which one is first?
-------------------------------------------------------------------------------
DECLARE @MyStart tinyint
DECLARE @MyForStart tinyint
DECLARE @MyAfterStart tinyint
DECLARE @MyInsteadStart tinyint
SELECT @MyForStart = CHARINDEX('for',@TriggerCode)
SELECT @TriggerType = LTRIM(RTRIM(SUBSTRING(@TriggerCode,CHARINDEX('for',@TriggerCode)-1, 4)))
SELECT @MyAfterStart = CHARINDEX('after',@TriggerCode)
SELECT @TriggerType = LTRIM(RTRIM(SUBSTRING(@TriggerCode,CHARINDEX('after',@TriggerCode)-1, 6)))
SELECT @MyInsteadStart = CHARINDEX('instead',@TriggerCode)
SELECT @TriggerType = LTRIM(RTRIM(SUBSTRING(@TriggerCode,CHARINDEX('Instead',@TriggerCode)-1, 8)))
IF @MyAfterStart <> 0
AND @MyAfterStart < @MyForStart
BEGIN
SET @MyStart = @MyAfterStart
SELECT @TriggerType = LTRIM(RTRIM(SUBSTRING(@TriggerCode,@MyStart-1, 6)))
END
ELSE IF @MyInsteadStart <> 0
AND @MyInsteadStart < @MyForStart
BEGIN
SET @MyStart = @MyInsteadStart
SELECT @TriggerType = LTRIM(RTRIM(SUBSTRING(@TriggerCode,@MyStart-1, 8)))
END
ELSE IF @MyForStart <> 0
AND @MyForStart < @MyAfterStart
AND @MyForStart < @MyInsteadStart
BEGIN
SET @MyStart = @MyForStart
SELECT @TriggerType = LTRIM(RTRIM(SUBSTRING(@TriggerCode,@MyStart-1, 4)))
END
-------------------------------------------------------------------------------
--Build the correct header and append it to the create trigger code then run it
-------------------------------------------------------------------------------
IF @TriggerType LIKE '%FOR%'
BEGIN
SET @CHeader = 'CREATE TRIGGER ['[email protected]+'].['[email protected]_name+'] ON ['[email protected]+'].['[email protected]+']'
--print @CHeader+char(13)+SUBSTRING(@TriggerCode,CHARINDEX('FOR',@TriggerCode)-1,DATALENGTH(@TriggerCode))
SET @TriggerCode = @CHeader+char(13)+SUBSTRING(@TriggerCode,CHARINDEX('for',@TriggerCode)-1,DATALENGTH(@TriggerCode))
EXEC(@TriggerCode)
END
ELSE IF @TriggerType LIKE '%AFTER%'
BEGIN
SET @CHeader = 'CREATE TRIGGER ['[email protected]+'].['[email protected]_name+'] ON ['[email protected]+'].['[email protected]+']'
--print @CHeader+char(13)+SUBSTRING(@TriggerCode,CHARINDEX('AFTER',@TriggerCode)-1,DATALENGTH(@TriggerCode))
SET @TriggerCode = @CHeader+char(13)+SUBSTRING(@TriggerCode,CHARINDEX('after',@TriggerCode)-1,DATALENGTH(@TriggerCode))
EXEC(@TriggerCode)
END
ELSE IF @TriggerType LIKE '%INSTEAD%'
BEGIN
SET @CHeader = 'CREATE TRIGGER ['[email protected]+'].['[email protected]_name+'] ON ['[email protected]+'].['[email protected]+']'
--print @CHeader+char(13)+SUBSTRING(@TriggerCode,CHARINDEX('INSTEAD',@TriggerCode)-1,DATALENGTH(@TriggerCode))
SET @TriggerCode = @CHeader+char(13)+SUBSTRING(@TriggerCode,CHARINDEX('instead',@TriggerCode)-1,DATALENGTH(@TriggerCode))
EXEC(@TriggerCode)
END
FETCH NEXT FROM db_cursor INTO @trigger_name, @table_schema, @table_name, @isupdate, @isdelete, @isinsert, @isafter, @isinsteadof, @disabled, @TriggerCode
END
CLOSE db_cursor
DEALLOCATE db_cursor
COMMIT TRANSACTION
END
Sta usando TSQL un requisito fisso e, in caso affermativo, perché? La soluzione preferita per gli oggetti di scripting è l'utilizzo di Smo da PowerShell, C# o un altro linguaggio .NET. – Pondlife
SQL 2008 r2 consente PowerShell all'interno di SSMS – JNK
Sì, lo so, ma ho un requisito per farlo in TSQL in quanto farà parte di un batch più grande (che è TSQL) – Gareth