Re: Sync production db with test
- From: Carl Ganz <seton.software@xxxxxxxxxxx>
- Date: Thu, 18 Jun 2009 16:07:19 -0700 (PDT)
On Jun 18, 12:21 pm, samalex <sama...@xxxxxxxxx> wrote:
On Thu, 18 Jun 2009 10:26:02 -0500, Carl Ganz <seton.softw...@xxxxxxxxxxx>
wrote:
On Jun 18, 9:35 am, samalex <sama...@xxxxxxxxx> wrote:
On Wed, 17 Jun 2009 15:13:49 -0500, Carl Ganz
<seton.softw...@xxxxxxxxxxx>
wrote:
On Jun 17, 11:57 am, samalex <sama...@xxxxxxxxx> wrote:
On Wed, 17 Jun 2009 10:22:09 -0500, SetonSoftware
existing<seton.softw...@xxxxxxxxxxx> wrote:
I'd like to set up a process so that every week our Production data
copies to our Test database. I only want the data to come over. The
table structures, indices, stored procs, UDFs, etc. currently
in the Test database should remain as they are.
What is the best/recommended/least painful approach to doing this?
Thanks
Carl
Hi Carl,
haveI actually have an SSIS package that does this between our Prod and
Test
systems, but it only does it for a few tables and not all. If you
plusa
TON of tables to sync-up than this could take some time to update
it
wouldn't be dynamic, so any schema changes on either table would
require
updating the SSIS package.
Another option is to write a procedure to loop through
information_schema.Tables, for example using this as the base:
SELECT *
FROM information_schema.Tables
where Table_Type = 'BASE TABLE'
Insertand for each record truncate the table in the Test Database and
anInto from Prod Database. The only catcher will be for tables with
tooIdentity field TSQL will force you to set Identity_Insert to ON and
also
have all the column names in the Insert statement. It wouldn't be
butdifficult to piece together a procedure to create this list from
information_schema.Columns and put it together using dynamic SQL,
the
code will be tricky.
Test,Another option is to just do a backup/restore from Production to
but
this will overwrite any test objects yet to be deployed to Prod. Is
this
the reason you're not doing a backup/restore from Prod to Test?
togetherThe script I described above is something I've wanted to put
itfor
some time now, so if I have time later this week I'll do so and add
to
this thread.
Take care --
Sam
Sam
I figured this would be the needed approach. I've now got the script
to do this largely worked up - setting SET_IDENTITY, disabling the
constraints, deleting the existing data, and doing the INSERT..SELECT.
It largely seems to be working and I'll post this when I'm done.
Probably tomorrow.
Thanks
Carl
Hi Carl,
This morning I finished-up that script and I figured I'd post it incase
others can use it. It's pretty basic and it could surely be modified
to
add functionality, but all and all as long as the schemas are the same
between the two databases this should do the trick:
------------[ Start Code ]------------
-- Set Variables
Declare @strTableSchema Nvarchar(4000)
Declare @strTableName Nvarchar(50)
Declare @strSourceDB nvarchar(50)
Declare @strSQL nvarchar(4000)
Declare @bitIdentity bit
-- Set Source Database
Set @strSourceDB = 'SamsTestDB'
-- Get table Listing into Temp Table
select name,
(Select count(*)
from Information_Schema.Columns
where COLUMNPROPERTY(object_id(TABLE_NAME),
COLUMN_NAME, 'IsIdentity')
= 1 and
TABLE_NAME = name ) as BitIdentity
Into #tmpTables
from sysobjects where type = 'U'
-- Loop through tables
While (Select count(*) from #tmpTables) > 0
Begin
-- Get first table name
Select Top 1 @strTableName = name, @bitIdentity =
BitIdentity from
#tmpTables
-- Reset variables
set @strTableSchema = ''
Set @strSQL =
'Truncate Table #TableName#; #IdentityOn#
Insert into #TableName#
(#TableSchema#) Select * from #SourceDB#..#TableName#; #IdentityOff#'
-- get Table Schema
select
@strTableSchema = @strTableSchema + '[' +
Column_NAME + '],'
from Information_Schema.Columns
WHERE TABLE_NAME = @strTableName
ORDER BY Ordinal_Position
-- Drop leading comma from @strTableSchema
SET @strTableSchema =
Left(@strTableSchema,Len(@strTableSchema)-1)
--@TableSchema + '_EOF'
-- If Identity than account for this in @strTableSchema
If @bitIdentity = 1
Begin
set @strSQL =
replace(@strSQL,'#IdentityOn#','Set Identity_Insert
#TableName# On;')
set @strSQL =
replace(@strSQL,'#IdentityOff#','Set Identity_Insert
#TableName# Off;')
End
Else
Begin
set @strSQL =
replace(@strSQL,'#IdentityOn#','')
set @strSQL =
replace(@strSQL,'#IdentityOff#','')
End
-- Set other variables
set @strSQL =
replace(@strSQL,'#TableName#',@strTableName)
Set @strSQL =
replace(@strSQL,'#TableSchema#',@strTableSchema)
Set @strSQL = replace(@strSQL,'#SourceDB#',@strSourceDB)
-- Execute string
exec(@strSQL)
-- Delete this table from #tmpTables since it's already
processed
Delete from #tmpTables where name = @strTableName
End
-- Cleanup
drop table #tmpTables
------------[ End Code ]------------
Set the SourceDB variable to the Production database and run this from
the
Test database to copy all data from Prod to Test. It also takes into
account tables with Identity fields.
I may further modify this script to exclude tables that don't have the
same schemas between Source and Destination since we often have new
tables
or modified tables in Test that haven't been rolled out to Prod. Also
logging could be added to tell the user what tables were updated and
how
many rows... but this was quick and dirty :)
Take care --
Sam- Hide quoted text -
- Show quoted text -
Sam
You may have a problem with TRUNCATE as table with referential
integrity constraints can't be truncated. I'm disabling all the
constraints first (then re-enabling them when done). Your code
reminded me that I can use TRUNCATE since I'm doing this so I'll
modify mine.
Thanks
Carl
Hi Carl,
Ahh, we've gotten away from using foreign keys or other referential
integrity in our current system, so this slipped my mind as being a
consideration. Your method of disabling and re-enabling the constraints
should do the trick though, so awesome suggestion there.
Sam- Hide quoted text -
- Show quoted text -
Sam
Here's the final and fully commented version. I was having problems
executing SET IDENTITY_INSERT ON, the SELECT...INSERT, and SET
IDENTITY_INSERT OFF as separate statements. When I batched them
together in one semi-colon delimited command it works fine.
Thanks
Carl
ALTER PROCEDURE [dbo].[spc_CopyProd2Test]
AS
/*
DROP TABLE SyncLog
SELECT * FROM SyncLog
TRUNCATE TABLE SyncLog
This stored procedure pulls the contents of a database on one server
into the table structures of
a database on another server. You can change the source
server.database by doing a global replace of
this string:
[GBM-TEST].[Contracts]
with the appropriate servername.databasename
The commands it issues for each table are at follows:
ALTER TABLE [dbo].MyTable NOCHECK CONSTRAINT PK_MyConstraint
TRUNCATE TABLE dbo.[MyTable]
SET IDENTITY_INSERT dbo.[MyTable] ON (if applicable)
INSERT INTO dbo.[MyTable] ([MyColumn1],[MyColumn2]) SELECT [MyColumn1],
[MyColumn2] FROM [MyServer].[MyDataBase].dbo.[MyColumn1]
SET IDENTITY_INSERT dbo.[MyTable] OFF (if applicable)
ALTER TABLE [dbo].MyTable CHECK CONSTRAINT PK_MyConstraint
This procedure will remove all constraints, SET IDENTITY_INSERT ON for
all tables which have identity
columns, delete the contents of the table, INSERT..SELECT the data
from the source table to the target,
SET IDENTITY_INSERT OFF as appropriate, and re-enable the constraints.
*/
DECLARE @SQL varchar(max)
DECLARE @SQLTemplate varchar(max)
DECLARE @Columns varchar(max)
DECLARE @SQLError varchar(max)
DECLARE @TableSchemaOrig varchar(100)
DECLARE @TableSchema varchar(100)
DECLARE @TableNameOrig varchar(100)
DECLARE @TableName varchar(100)
DECLARE @ColumnName varchar(100)
DECLARE @Cnt int
DECLARE @x int
--Create a table to store error messages
IF NOT EXISTS (SELECT 1 FROM dbo.sysobjects WHERE id = object_id
('SyncLog'))
BEGIN
CREATE TABLE SyncLog
(
ErrorNumber int,
Message varchar(max),
SQL varchar(max),
ErrorDate datetime
)
END
INSERT INTO SyncLog
(ErrorNumber, Message, SQL, ErrorDate)
VALUES
(Null, 'Migration begun', Null, GETDATE())
--This will hold the SQL statements that are created
CREATE TABLE #SQLtemp
(
ID int identity(1,1),
SQL varchar(max)
)
--This will hold the structures of the tables
CREATE TABLE #Tabletemp
(
ID int identity(1,1),
TABLE_SCHEMA varchar(100),
TABLE_NAME varchar(100),
COLUMN_NAME varchar(100)
)
--Create SQL to turn off all the constraints
INSERT INTO #SQLtemp
SELECT 'ALTER TABLE [' + SCHEMA_NAME(schema_id) + '].' + OBJECT_NAME
(parent_object_id) +
' NOCHECK CONSTRAINT ' + OBJECT_NAME(OBJECT_ID)
FROM sys.objects
WHERE type_desc LIKE '%CONSTRAINT'
--Create SQL to truncate the tables
INSERT INTO #SQLtemp
SELECT 'TRUNCATE TABLE ' + TABLE_SCHEMA + '.[' + TABLE_NAME + ']'
FROM INFORMATION_SCHEMA.tables
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME <> 'SyncLog'
ORDER BY TABLE_NAME
--Create SQL to insert data for those table and columns which exist in
both the source and target databases
INSERT INTO #Tabletemp
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
FROM [GBM-TEST].[Contracts].INFORMATION_SCHEMA.columns
WHERE TABLE_NAME IN (SELECT TABLE_NAME
FROM [GBM-TEST].
[Contracts].INFORMATION_SCHEMA.tables
WHERE TABLE_TYPE = 'BASE TABLE')
AND TABLE_NAME + COLUMN_NAME IN (SELECT TABLE_NAME + COLUMN_NAME
FROM INFORMATION_SCHEMA.columns)
AND TABLE_NAME <> 'SyncLog'
ORDER BY TABLE_NAME, ORDINAL_POSITION
SET @Cnt = @@ROWCOUNT
SET @x = 1
SET @TableSchemaOrig = ''
SET @TableNameOrig = ''
SET @Columns = ''
--This is the SQL template for the INSERT...SELECT. The strings
delimited by percent signs will be
--replaced with actual values
SET @SQLTemplate = 'INSERT INTO %Table% (%Columns%) SELECT %Columns%
FROM [GBM-TEST].[Contracts].%Table%'
SET @SQL = @SQLTemplate
--For each table/column entry in #Tabletemp
WHILE @x <= @Cnt
BEGIN
--Pull the information for a table/column entry
SELECT @TableSchema = TABLE_SCHEMA,
@TableName = TABLE_NAME,
@ColumnName = COLUMN_NAME
FROM #Tabletemp
WHERE ID = @x
IF @TableSchemaOrig = ''
SET @TableSchemaOrig = @TableSchema
IF @TableNameOrig = ''
SET @TableNameOrig = @TableName
--Build the column list for the table
IF @TableNameOrig = @TableName
SET @Columns = @Columns + '[' + @ColumnName + '],'
--When all the table's column have been retrieved create SET
IDENTITY_INSERT ON/OFF
--statements for those that have identity columns
IF @TableNameOrig <> @TableName
BEGIN
--The SET IDENTITY_INSERT <tablename> ON/OFF is appended
to the INSERT..SELECT SQL
--as executng them individually was causing problems.
Therefore, the commands are
--executed as a semi-colon separated batch like this:
--SET IDENTITY_INSERT dbo.[MyTable] ON (if applicable);
--INSERT INTO dbo.[MyTable] ([MyColumn1],[MyColumn2])
SELECT [MyColumn1],[MyColumn2] FROM [MyServer].[MyDataBase].dbo.
[MyColumn1];
--SET IDENTITY_INSERT dbo.[MyTable] OFF (if applicable)
IF EXISTS (select TABLE_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = @TableNameOrig
and COLUMNPROPERTY(object_id(TABLE_NAME),
COLUMN_NAME, 'IsIdentity') = 1)
BEGIN
SET @SQL = 'SET IDENTITY_INSERT ' + @TableSchema + '.
[' + @TableNameOrig + '] ON;' + @SQL
END
--Remove the trailing comma
SET @Columns = SUBSTRING(@Columns, 1, LEN(@Columns) - 1)
--Replace the placeholders with the table name and comma-
delimited field list
SET @SQL = REPLACE(@SQL, '%Table%', @TableSchema + '.[' +
@TableNameOrig + ']')
SET @SQL = REPLACE(@SQL, '%Columns%', @Columns)
IF EXISTS (select TABLE_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = @TableNameOrig
and COLUMNPROPERTY(object_id(TABLE_NAME),
COLUMN_NAME, 'IsIdentity') = 1)
BEGIN
SET @SQL = @SQL + ';SET IDENTITY_INSERT ' +
@TableSchema + '.[' + @TableNameOrig + '] OFF'
END
INSERT INTO #SQLtemp (SQL) VALUES (@SQL)
SET @SQL = @SQLTemplate
SET @Columns = ''
SET @TableNameOrig = @TableName
END
ELSE
SET @x = @x + 1
END
DROP TABLE #Tabletemp
--Create the SQL to re-enable the constraints
INSERT INTO #SQLtemp
SELECT 'ALTER TABLE [' + SCHEMA_NAME(schema_id) + '].' + OBJECT_NAME
(parent_object_id) +
' CHECK CONSTRAINT ' + OBJECT_NAME(OBJECT_ID)
FROM sys.objects
WHERE type_desc LIKE '%CONSTRAINT'
--Now that the SQL statements needed for the migration are ready and
in the required order,
--execute each one
SELECT @Cnt = MAX(ID) FROM #SQLtemp
SET @x = 1
WHILE @x <= @Cnt
BEGIN
SELECT @SQL = SQL
FROM #SQLtemp
WHERE ID = @x
BEGIN TRY
EXEC(@SQL)
INSERT INTO SyncLog
(ErrorNumber, Message, SQL, ErrorDate)
VALUES
(Null, 'OK', @SQL, GETDATE())
END TRY
BEGIN CATCH
SET @SQLError = @@ERROR
--If an error was found, write it to the SyncLog table.
--One of the most common errors will be from trying to insert
a value from a larger column
--into that of a smaller column. This will happen if you
reduced the size of a column in
--your target to less than that of your source. In other
cases, the data type may have changed
--and this will throw an error as well.
IF @SQLError <> 0
INSERT INTO SyncLog
(ErrorNumber, Message, SQL, ErrorDate)
VALUES
(@SQLError, Error_Message(), @SQL, GETDATE())
END CATCH
SET @x = @x + 1
END
INSERT INTO SyncLog
(ErrorNumber, Message, SQL, ErrorDate)
VALUES
(Null, 'Migration completed', Null, GETDATE())
--SELECT * FROM #SQLtemp ORDER BY ID
DROP TABLE #SQLtemp
.
- References:
- Sync production db with test
- From: SetonSoftware
- Re: Sync production db with test
- From: samalex
- Re: Sync production db with test
- From: Carl Ganz
- Re: Sync production db with test
- From: samalex
- Re: Sync production db with test
- From: Carl Ganz
- Re: Sync production db with test
- From: samalex
- Sync production db with test
- Prev by Date: Re: capturing and returning system errors to client application
- Next by Date: Re: Convert varchar to date
- Previous by thread: Re: Sync production db with test
- Next by thread: Re: Sync production db with test
- Index(es):
Relevant Pages
|