Question

I know this is bit of a strange one but if anyone had any help that would be greatly appreciated.

The scenario is that we have a production database at a remote site and a developer database in our local office. Developers make changes directly to the developer db and as part of the deployment process a C# application runs and produces a series of .sql scripts that we can execute on the remote side (essentially delete *, insert) but we are looking for something a bit more elaborate as the downtime from the delete * is unacceptable. This is all reference data that controls menu items, functionality etc of a major website.

I have a sproc that essentially returns a diff of two tables. My thinking is that I can insert all the expected data in to a tmp table, execute the diff, and drop anything from the destination table that is not in the source and then upsert everything else.

The question is that is there an easy way to do this without using a cursor? To illustrate the sproc returns a recordset structured like this:

TableName Col1 Col2 Col3 Dest Src

Anything in the recordset with TableName = Dest should be deleted (as it does not exist in src) and anything in Src should be upserted in to dest. I cannot think of a way to do this purely set based but my DB-fu is weak.

Any help would be appreciated. Apologies if the explanation is sketchy; let me know if you need anymore details.

Was it helpful?

Solution

Yeah, that sproc would work. Use a FULL JOIN with that table and add a column to indicate insert, update or delete. Then create separate SQL statements for them based on the column indicator. Set based.


Sorry not a FULL JOIN, you'll need to break them down to separate LEFT and RIGHT JOINS. Did this in NotePad, so apologies if it doesn't work:

INSERT INTO tempDeployData(ID,IUDType)
SELECT ed.id, 'D'
FROM    tmpDeployData td
    RIGHT JOIN existingData ed ON td.id = ed.id
WHERE td.id IS NULL     


UPDATE td
SET td.IUDType = CASE WHEN ed.id IS NULL THEN
                         'I'
                         ELSE
                         'U'
                         END
FROM    tmpDeployData td
    LEFT JOIN existingData ed ON td.id = ed.id


INSERT INTO existingData(ID,a,b,c)
SELECT td.ID,td.a,td.b,td.c
FROM tmpDeployData td
WHERE td.IUDType = 'I'

DELETE ed
FROM existingData ed
    INNER JOIN tmpDeployData td ON ed.ID = td.ID
WHERE td.IUDType = 'D'

UPDATE  ed
SET     ed.a = td.a,
        ed.b = td.b,
        ed.c = td.c
FROM existingData ed
INNER JOIN tmpDeployData td ON ed.ID = td.ID
WHERE td.IUDType = 'U' 

Just realized you're pulling info into the temptable as a staging table, not the source of the data. In that case you can use the FULL JOIN:

INSERT INTO tmpDeployData(ID,a,b,c,IUDType)
SELECT  sd.ID, 
        sd.a, 
        sd.b, 
        sd.c
        'IUDType' = CASE WHEN ed.id IS NULL THEN
                         'I'
                         WHEN sd.id IS NULL THEN
                         'D'
                         ELSE
                         'U'
                         END
FROM    sourceData sd
    FULL JOIN existingData ed ON sd.id = ed.id

Then same DML statements as before.

OTHER TIPS

There's a much, much easier way to do this assuming you're using SQL Server 2008: The MERGE statement.

Migrating all changes from one table to another is as simple as:

MERGE DestinationTable d
USING SourceTable s
    ON d.Id = s.Id
WHEN MATCHED THEN UPDATE
    SET d.Col1 = s.Col1, d.Col2 = s.Col2, ...
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Id, Col1, Col2, ...)
    VALUES (s.Id, s.Col1, s.Col2, ...)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;

That's it. DestinationTable will be identical to SourceTable after that.

took at tablediff

tables do not need to participate in replication to run the utility. there's a wonderful -f switch to generate t-sql to put the tables 'in-sync':

Generates a Transact-SQL script to bring the table at the destination server into convergence with the table at the source server. You can optionally specify a name and path for the generated Transact-SQL script file. If file_name is not specified, the Transact-SQL script file is generated in the directory where the utility runs.

Why don't you just take a backup of the production database and restore it over your development database? You should have change scripts for all ddl differences from the production database that you can run on the database after the restore and it would test the deployment to production.

edit: Sorry, just re-read your question, it looks like you are storing your configuration info in your development db and generating your change scripts from that so this wouldn't work.

I would recommend creating change scripts by hand and storing them in source control. Then use sqlcmd or osql and a batch file to run your change scripts on the database.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top