views:

568

answers:

3

I have an application that (currently) needs to uses DBs served by both SQL Server 2000 and SQL Server 2005.

I'm trying to move a DB programmatically from one server to the other. All of the tables I use each have a primary key that is autoincremented. Since these autoincremented keys are used as foreign keys in other tables, my plan of attack for copying the tables was to temporarily turn off the auto-increment on the destination table, INSERT the records verbatim (with the original primary keys) and then turn auto-increment back on.

If I understand correctly, I can "turn auto-increment on/off" one table at a time by executing

SET IDENTITY_INSERT tbl_Test ON;
...
SET IDENTITY_INSERT tbl_Test OFF;

My implementation is in VB 2005, using System.Data.SqlClient.

For brevity's sake, I'll only show the lines of code related to the construction of the SQL queries.

To create the table:

    Dim createTableStatement As String = _
        "CREATE TABLE tbl_Test (" & _
            "ID_Test INTEGER PRIMARY KEY IDENTITY," & _
            "TestData INTEGER" & _
            ")"
    Dim createTableCommand As New SqlCommand(createTableStatement, connection)

    createTableCommand.ExecuteNonQuery()

To turn IDENTITY_INSERT on or off:

    Dim turnOffIdentInsertStatement As String = _
        "SET IDENTITY_INSERT tbl_Test OFF;"
    Dim turnOffIdentInsertCommand As New SqlCommand(turnOffIdentInsert, connection)
    turnOffIdentInsertCommand.ExecuteNonQuery()

    Dim turnOnIdentInsertStatement As String = _
        "SET IDENTITY_INSERT tbl_Test ON;"
    Dim turnOnIdentInsertCommand As New SqlCommand(turnOnIdentInsert, connection)
    turnOnIdentInsertCommand.ExecuteNonQuery()

To insert a new record normally, without specifying primary key:

    Dim insertRegularStatement As String = _
        "INSERT INTO tbl_Test (TestData) VALUES (42);"
    Dim insertRegularCommand As New SqlCommand(insertRegularStatement, connection)
    insertRegularCommand.ExecuteNonQuery()

To insert a new record with an explicit primary key:

    Dim insertWithIDStatement As String = _
        "INSERT INTO tbl_Test (ID_Test, TestData) VALUES (20, 42);"
    Dim insertWithIDCommand As New SqlCommand(insertWithIDStatement, connection)
    insertWithIDCommand.ExecuteNonQuery()

So, my plan is to call the code that turns IDENTITY_ INSERT on, add the records with the explicit primary keys, then turn IDENTITY_ INSERT off.

This method appears to work fine when I'm talking to SQL Server 2000, but I run into an exception when I try the exact same code against SQL Server 2005:

"Cannot insert explicit value for identity column in table 'tbl_Test' when IDENTITY_INSERT is set to OFF."

Any hints? Thanks in advance as always.

+1  A: 

You might have to insert a GO statement after your SET IDENTITY ... ON statement.

David Andres
He's not using Query Analyzer so that doesn't apply...
DJ
Thanks for taking the time to respond David!
John at CashCommons
+1  A: 

In a SQL query analyser session I would issue a GO after the IDENTITY_INSERT and before the actual insert. Maybe this applies to calling it from in code also?

Henryk
Thanks for taking the time to respond! Much appreciated.
John at CashCommons
+2  A: 

It would be worth looking at SqlBulkCopy as it is optimized at importing bulk data.

Also worth looking into depending on the scenario would be to use an SSIS task to bulk copy the data (also uses the bulk copy API) or linked servers with a BULK INSERT statement (again, bulk copy API).

As for setting the identity are the set identity_insert statements executed on the same connection as the insert? As SQL Server 2005 only persists that setting on the current connection.

As the following worked fine for me on SQL Server 2008 Express:

using (var conn = CreateConnection())
{
    conn.Open();
    new SqlCommand("SET IDENTITY_INSERT Foo ON", conn).ExecuteNonQuery();
    new SqlCommand("INSERT INTO Foo(id) VALUES (1)", conn).ExecuteNonQuery();
    new SqlCommand("SET IDENTITY_INSERT Foo OFF", conn).ExecuteNonQuery();
}
KeeperOfTheSoul
You found it. I was opening/closing the connection in between the commands. Also thanks for pointing me toward SqlBulkCopy!
John at CashCommons
Ahh...would this be related to implicit transactions by chance?
David Andres
David, possibly. I'm not familiar enough to say for sure though.
John at CashCommons
SET IDENTITY_INSERT doesn't care about transactions, it doesn't participate in them. It does however care about connections, it is only set for the current connection. I haven't tested scope though so I'm unsure on that.
KeeperOfTheSoul