У меня часто есть базы данных Test и Live с одинаковой схемой. Они обычно имеют хранимые процедуры с изменениями в зависимости от состояния моих изменений в разработке. Поэтому я не всегда могу просто сделать резервную копию и восстановить. Я написал запрос, который перебирает все таблицы в базе данных, удаляет данные. Затем снова зацикливается и вставляет данные из активной базы данных.
Ниже моя тестовая база данных называется WorkflowTest, а моя рабочая база называется Workflow, но вы можете просто заменить имена баз данных в переменных. Просто убедитесь, что вы подключились к базе данных TEST.
Но имена таблиц и столбцы совершенно произвольны.
Я зацикливаюсь несколько раз, потому что не хочу беспокоиться об ограничениях внешнего ключа. Некоторые операции удаления / вставки завершатся неудачно, поскольку ожидается, что данные существуют в другой таблице.
Я обнаружил, что все 45 или около того моих столов полностью переполнены примерно за 2-3 цикла.
Во время цикла Вставка сначала проверяет, есть ли в таблице столбец идентификаторов, пытаясь включить IDENTITY_INSERT. Если это не сработает, то будет создан оператор вставки с предыдущим включенным IDENTITY_INSERT и выключенным следующим IDENTITY_INSERT. Это должно быть сделано в том же операторе EXEC, потому что команды в EXEC выходят из области видимости после выполнения.
Задним числом, я думаю, я мог бы написать все свои тестовые хранимые процедуры как операторы alter, восстановить тестовую базу данных из резервной копии действующей, а затем выполнить мои операторы alter. Но я считаю, что настройки безопасности пользователя не восстанавливаются должным образом, поэтому иногда это тоже может быть проблемой.
-- Gets a list of all tables in the Test database
-- first loops through them and deletes all records.
-- if it encounters an error, it does not remove that table from #tablesNeedingCopy so it will try again.
-- this is because we don't know the order to delete and may encounter foreign key constraints.
-- It usually deletes all records from all tables after 2 or so loops.
-- the 2nd step is nearly identical, but instead it inserts the data
Declare @CopyToDatabase varchar(100)
declare @CopyFromDatabase varchar(100)
set @CopyToDatabase = 'WorkflowTest'
set @CopyFromDatabase = 'Workflow'
use WorkflowTest -- [Connect to Database that you want to copy to]
DECLARE @sqlCommand varchar(max)
declare @columnNames varchar(max)
DECLARE @tableName as NVARCHAR(100);
DECLARE @tableNameCursor as CURSOR;
create table #tablesNeedingCopy
(
Table_Name varchar(100)
)
insert into #tablesNeedingCopy
(Table_Name)
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
and Table_Name not like 'sys%'
declare @hasTableError as char(1)
declare @remainingTableCount int
declare @loopControl int
set @loopControl = 0
select @remainingTableCount = count(*)
from #tablesNeedingCopy
while (@remainingTableCount > 0 And @loopControl < 10)
begin
set @loopControl = @loopControl + 1
SET @tableNameCursor = CURSOR FOR
SELECT TABLE_NAME
FROM #tablesNeedingCopy
OPEN @tableNameCursor;
FETCH NEXT FROM @tableNameCursor INTO @tableName;
WHILE @@FETCH_STATUS = 0
BEGIN
set @hasTableError = 'N'
SET @sqlCommand = 'Delete from ' + @tableName
print @sqlCommand
begin try
exec (@sqlCommand)
end try
begin catch
set @hasTableError = 'Y'
print ERROR_MESSAGE()
end catch
if (@hasTableError = 'N')
begin
-- otherwise leave the table in
delete from #tablesNeedingCopy
where Table_Name = @tableName
end
FETCH NEXT FROM @tableNameCursor INTO @tableName;
END
CLOSE @tableNameCursor;
DEALLOCATE @tableNameCursor;
select @remainingTableCount = count(*)
from #tablesNeedingCopy
end -- end while
select @remainingTableCount = count(*)
from #tablesNeedingCopy
if (@remainingTableCount > 0)
begin
select Table_Name as DeleteTableNames
from #tablesNeedingCopy
end
delete from #tablesNeedingCopy
-------
insert into #tablesNeedingCopy
(Table_Name)
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
and Table_Name not like 'sys%'
declare @hasIdentityColumn as char(1)
set @loopControl = 0
select @remainingTableCount = count(*)
from #tablesNeedingCopy
while (@remainingTableCount > 0 And @loopControl < 10)
begin
set @loopControl = @loopControl + 1
SET @tableNameCursor = CURSOR FOR
SELECT TABLE_NAME
from #tablesNeedingCopy
OPEN @tableNameCursor;
FETCH NEXT FROM @tableNameCursor INTO @tableName;
WHILE @@FETCH_STATUS = 0
BEGIN
set @hasTableError = 'N'
set @hasIdentityColumn = 'Y'
SET @sqlCommand = 'SET IDENTITY_INSERT ' + @CopyToDatabase + '.dbo.' + @tableName + ' ON;' -- Database to copy to
begin try
print @sqlCommand
exec (@sqlCommand)
end try
begin catch
--print ERROR_MESSAGE()
set @hasIdentityColumn = 'N'
end catch
if (@hasTableError = 'N')
begin
SELECT top 1 @columnNames =
STUFF((SELECT N', ' + Column_Name
FROM INFORMATION_SCHEMA.COLUMNS AS t2
WHERE t2.TABLE_NAME=t.TABLE_NAME
FOR XML PATH,TYPE).value(N'.','nvarchar(max)'),1,2,'')
FROM INFORMATION_SCHEMA.COLUMNS t
WHERE TABLE_NAME = @tableName
order by ORDINAL_POSITION
set @sqlCommand = 'Insert into ' + @CopyToDatabase + '.dbo.' + @tableName + ' (' + @columnNames + ') select ' + @columnNames + ' from ' + @CopyFromDatabase + '.dbo.' + @tableName
if (@hasIdentityColumn = 'Y')
begin
set @sqlCommand = 'SET IDENTITY_INSERT ' + @CopyToDatabase + '.dbo.' + @tableName + ' ON; ' + @sqlCommand + ' SET IDENTITY_INSERT ' + @CopyToDatabase + '.dbo.' + @tableName + ' OFF;'
end
print @sqlCommand
begin try
exec (@sqlCommand)
end try
begin catch
set @hasTableError = 'Y'
print ERROR_MESSAGE()
end catch
end
if (@hasTableError = 'N')
begin
-- otherwise leave the table in
delete from #tablesNeedingCopy
where Table_Name = @tableName
end
FETCH NEXT FROM @tableNameCursor INTO @tableName;
END
CLOSE @tableNameCursor;
DEALLOCATE @tableNameCursor;
select @remainingTableCount = count(*)
from #tablesNeedingCopy
end -- end while
select @remainingTableCount = count(*)
from #tablesNeedingCopy
if (@remainingTableCount > 0)
begin
select Table_Name as InsertTableNames
from #tablesNeedingCopy
end
drop table #tablesNeedingCopy