views:

73

answers:

4

Hey,

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.

+3  A: 

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.

TyT
It's the 'creating separate sql statements for them based on the column indicator' that I'm having a problem with. I should of noted I'm trying to approach it generically as it needs to be executed entirely at the remote site and there is no access to remote/prod data prior to deployment.Would you be able to provide a sample?
Joshua Atkins
That's perfect. Thanks a lot for the detailed examples.
Joshua Atkins
+1  A: 

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.

Aaronaught
I wish I was. We are stuck on 2005 for now.I'm forced to recreate merge, essentially.
Joshua Atkins
@Joshua: I see you already have a working answer, but in the future, you should mention your version in the question so people are aware of your constraints. Better yet, add the version-specific `sql-server-2005` tag.
Aaronaught
Thanks a lot Aaronaught--I missed it. Will get it in the future!
Joshua Atkins
+1  A: 

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.

Nick Kavadias
That looks awesome. Does it take in do deletes/upserts or just schema changes? It's not very specific.Regardless, I'm forced to reinvent the wheel as we don't have direct access to production's SQL server (we provide them scripts to execute). I doubt I could convince them to let us run this.
Joshua Atkins
Nick Kavadias
A: 

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.

Decker97
Hi Decker--that is essentially what we do. The problem is that we do not have access to the production server until the scripts are run on it so generating change scripts by comparing dev/prod is not possible. The change scripts essentially need to be generated dynamically while executed on the production server.
Joshua Atkins