Click here to Skip to main content
Click here to Skip to main content

Database Resetter

By , 5 Oct 2004
 

Sample Image - Database_Resetter.gif

Introduction

This article describes a small utility that can be used to reset a database to a clean state. During development of applications that use databases, this may be needed quite frequently.

This utility was built quickly and will therefore not win any style points, but it works for me!

Background

The idea for this utility was born out of pure frustration. In a number of projects, we were working with a database in which both the structure and the default data changed very often. The changes were mostly minor, but because of these small changes, it became increasingly difficult to keep up an efficient work process. Changes in both structure and default data had to be made in two places. A bulk import of either test data or actual data had to be done as well. All of this opened the door for small (and bigger) mistakes.

This utility - that should have been written over a year ago - relies on certain characteristics of a database to remove all non-default data from it and clean up the autonumber (or identity) values so that new data can be inserted nicely without nasty gaps in IDs.

Using the code

The code that does all the work is contained in a separate library, so it can be reused by different projects. There is a standalone application which can be used to reset a database in a matter of seconds. The library could also be included in an import tool enabling that tool to reset the database before importing the data.

The basic idea behind this utility is to mark all default data as such, and delete anything that is not marked. To mark the default data, a specific column is added to those tables containing default data. In the example project, this column is called IsStatic. It is of type bit and should have the value of 1 if the record should be retained. Any record where IsStatic is set to 0 will be deleted. Any table that doesn't contain an IsStatic column will be emptied in its entirety.

The library can be used in two ways. It can be used to bulk-erase the database, or it can be used to erase the database on a per-table basis. The latter allows for feedback on the progress.

To use the code, simply instantiate the class, supply it a connection string and the name of the column to test for. Using the ResetAllTables function results in a bulk-erase of the entire database.

Dim resetter As PhoenixConsultancy.Utilities.Database.Reset = _
    New PhoenixConsultancy.Utilities.Database.Reset

resetter.StaticColumnName = "IsStatic"
resetter.ConnectionString = connectionString

resetter.Initialize()
resetter.ResetAllTables()
TextBoxResults.Text &= "Bulk reset completed." & vbCrLf
resetter.DeInitialize()

In order to reset the tables one by one, a list of tables can be received and looped over. The list of tables also contains information on the number of records deleted and whether or not the table has been reset. This list can also be obtained after a bulk-reset in order to supply status information.

Dim resetter As PhoenixConsultancy.Utilities.Database.Reset = _
    New PhoenixConsultancy.Utilities.Database.Reset
Dim tables As PhoenixConsultancy.Utilities.Database.TableInfo()
Dim table As PhoenixConsultancy.Utilities.Database.TableInfo

resetter.StaticColumnName = "IsStatic"
resetter.ConnectionString = connectionString

resetter.Initialize()
tables = resetter.Tables
For Each table In tables
    If table.ResetDone Then
    TextBoxResults.Text &= "Table " & table.Name & _
        " was already reset, " & table.RecordsDeleted & _
        " records were deleted" & vbCrLf
    Else
    resetter.ResetTable(table)
    If table.ResetDone Then
    TextBoxResults.Text &= "Table " & table.Name & _
        " has been reset, " & table.RecordsDeleted & _
        " records were deleted" & vbCrLf
    Else
    TextBoxResults.Text &= "Table " & table.Name & _
        " has *not* been reset, " & table.RecordsDeleted & _
        " records were deleted" & vbCrLf
    End If
    End If
Next
resetter.DeInitialize()

The downloadable zip file contains both source and binaries for the library and a demo application. Be careful when using this utility. Make a backup of your database before testing it! Review the source if you're not sure what this utility does!

Points of Interest

The two main issues that needed to be solved were the inter-table dependencies and resetting the identity values.

To solve the dependency issue, I made use of the system tables in SQL Server. Using these tables, I can (quite simply, once you know how) find out which table relies on which other table. Using a recursive function, I traverse through the tree of dependencies and reset the tables without dependencies first. After a table has been reset, a flag is set so the same table will not be reset again. The query used to find related tables looks like this:

SELECT  sysobjects.name AS RelatedTable
FROM    sysobjects
INNER JOIN  syscolumns ON sysobjects.id = syscolumns.id
INNER JOIN  sysforeignkeys ON syscolumns.id = sysforeignkeys.fkeyid
   AND  syscolumns.colid = sysforeignkeys.fkey
INNER JOIN  syscolumns syscolumns2 
   ON sysforeignkeys.rkeyid = syscolumns2.id
   AND  sysforeignkeys.rkey = syscolumns2.colid
INNER JOIN  sysobjects sysobjects2 ON syscolumns2.id = sysobjects2.id
WHERE   sysobjects2.name = 'TableName'

Resetting the identity values is (again, once you know how) not difficult at all. According to Microsoft's documentation, there are two ways of doing this. I chose the way in which SQL Server figures it out for itself. First, I reset the identity to the lowest possible value. This will result in errors if the table contains data and you try an insert.

DBCC CHECKIDENT('TableName', RESEED, 0)

To straighten things out again, I issue the same query with one less parameter. Now SQL Server figures out what the highest ID is that's currently in use and adjusts the identity value accordingly. The result is a table in which new inserts are nicely in order.

DBCC CHECKIDENT('TableName', RESEED)

History

I found and fixed a minor problem with resetting the identities. When setting the new seed to 1 (as I did at first) any new insert will begin at ID 2. This is annoying when the table is completely empty. It might even break code that relies on ID 1 to exist.

Apart from the above there isn't much history yet. There is some work to do though. Some of the features I'd like to add are:

  • An IsStatic column remover for production / deployment databases
  • An IsStatic column addition tool to make it easier to use the utility on databases that haven't been prepared yet.

These features are not difficult to implement, but time is a precious good...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Michiel de Rond
Web Developer
Netherlands Netherlands
Michiel de Rond has been programming since he was about 10 years old. In the old days he developed DOS and Windows based applications. Later the focus has shifted towards Web development. Nowadays Client/Server applications employing current technology are his thing. Michiel is based in Amsterdam and works as an independent consultant/architect/developer. Next to his development work he also writes technical books (in dutch for now). In his precious spare time he loves to spend time with his wife and daughter.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalbackup and restorememberHåkan Nilsson8-Oct-04 4:43 
Hello Michiel

 
/Håkan N.
------------------------------------------------
- Bachelor in systems analysis 1991
- Systems developer, C#, Java, C, VB3-6
- DBA, database design, SQL, admin
------------------------------------------------
GeneralRe: backup and restore PinmemberMichiel de Rond8-Oct-04 4:50 
Hi Håkan,
 
Your message is kind of brief Wink | ;-) . From your subject I assume you'd like to see some form of backup and restore functionality in the program as well.
 
I have been thinking about that and I'd like to be able to do a backup before I reset the database. Afterwards I'd like to be able to restore the backup in case the reset (or anything you do afterwards, like an import) fails.
 
Unfortunately I haven't looked into backing up databases through code yet, but I gather it can be a complex process. Especially making sure it always backs up the database might be difficult.
 
If you have any thoughts on this I'd like to hear them (in the body of a message Smile | :) ).
 
Kind Regards,
 
Michiel
GeneralRe: backup and restore Pinmemberranju. v10-Oct-04 1:06 
Hi,
 
Database backup via code is actually somewhat trivial. I mean, the code per se, isn't all that difficult to figure out. The challenge perhaps will be in figuring out where to put the backed up file because it must obviously be a place that SQL server can write to. The answer is to use SQL DMO (SQL Distributed Management Objects) - Microsoft's answer to programmatic access to the database management.
 
In fact much of the schema querying that you are currently doing by crawling through sysobjects can alternatively be done via DMO. The downside of course is that you'll need to interop with the SQL DMO COM component to get things done. The following C# code snippet gets a database backed up using DMO.
 

using SQLDMO;
 
void BackItUp( server, userID, password, databaseName )
{
   //
   // connect to sql server
   //
   SQLServerClass sqlServer = new SQLServerClass();
   sqlServer.LoginTimeout = 5;
   sqlServer.Connect( server, userID, password );
 
   //
   // back the database up
   //
   BackupClass sqlBackup = new BackupClass();
   sqlBackup.Action = SQLDMO_BACKUP_TYPE.SQLDMOBackup_Database;
   sqlBackup.Database = databaseName;
   sqlBackup.Files = databaseName + ".bak";
   sqlBackup.SQLBackup( sqlServer );
}
 

 
There, that's it. If you'd like to receive notification of just how much of the database has been backed up so far (this is one thing you can't do by just executing a plain BACKUP DATABASE TO DISK ... command) then all you've got to do is register a handler for the event PercentComplete on the BackupClass object. Like so.
 

...
sqlBackup.PercentCompleteNotification = 1;
sqlBackup.PercentComplete += new BackupSink_PercentCompleteEventHandler( OnBackupPercentComplete );
...

 
And the handler would look like so,
 

private void OnBackupPercentComplete( string strMsg, int iPercent )
{
   //
   // handle this by updating one of 'em handy progress bars maybe?
   //
}

 
Hope this helps!
 
--
Ranju. V
http://www.geocities.com/cool_ranju/
--
GeneralRe: backup and restore PinmemberMichiel de Rond10-Oct-04 4:32 
Hi Ranju,
 
Thanks for your extensive post. I think that it will, indeed, help. It also puts my code in a whole new perspective. I suppose I will be toying around with DMO a bit in the near future.
 
Thanks a lot!
 
Michiel
GeneralRe: backup and restore PinmemberHåkan Nilsson10-Oct-04 21:07 
Hello Michiel,
 
SQLDMO is a COM object which only works with SQL Server. You must make a wrapper in dotnet to use it. Microsoft will deliver new assemblies providing this functionality in dotnet later on. I don't know if or when they are ready with this.
 
A bigger challenge would be to build a testdata tool with support for many database environments (SQL Server, Oracle, mySQL and DB2...). I don't know how the market looks for such tools, but I do know there are many companies out there which needs good support for testdata and automatic testing.
 
Gretings Smile | :)
 
/Håkan N.
------------------------------------------------
- Bachelor in systems analysis 1991
- Systems developer, C#, Java, C, VB3-6
- DBA, database design, SQL, admin
------------------------------------------------
GeneralRe: backup and restore PinmemberRami Saad8-Jan-05 15:25 
Hello all,
 
so how can you restore a database?
I managed to backup the database easily using sql commands:
BACKUP DATABASE { database_name | @database_name_var }
TO < backup_device > [ ,...n ].
Similar command is found to restore the database. But it always give me the error that it can't restore a database already in use (which is true, since i'm opening a connection to run a command on the sql server).
 
So anybody know how to restore a sql server database?
 
Thanks,
Rami

GeneralRe: backup and restore PinmemberHåkan Nilsson10-Oct-04 20:50 
Hello Michiel,
 
I already got the tools I need to perform these things in my dev. environment: SQL Server 2000, Visual Source Safe, Visual Studio 2003.
 
We always handling versions of all code in VSS, including stored procs. And we also got database scripts for the rest of the database objects for each versions. This is all about securing quality, it's rather unsafe not version handling some part of the code.
 
Greetings Smile | :)
 
/Håkan N.
------------------------------------------------
- Bachelor in systems analysis 1991
- Systems developer, C#, Java, C, VB3-6
- DBA, database design, SQL, admin
------------------------------------------------
GeneralRe: backup and restore PinmemberRami Saad8-Jan-05 15:24 
Hello all,
 
so how can you restore a database?
I managed to backup the database easily using sql commands:
BACKUP DATABASE { database_name | @database_name_var }
TO < backup_device > [ ,...n ].
Similar command is found to restore the database. But it always give me the error that it can't restore a database already in use (which is true, since i'm opening a connection to run a command on the sql server).
 
So anybody know how to restore a sql server database?
 
Thanks,
Rami

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130617.1 | Last Updated 5 Oct 2004
Article Copyright 2004 by Michiel de Rond
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid