Since I appear to be on a roll with my blog this morning, I figured that I should write one more post about SQL Server before I get back to my "day job."

I typically use SQL Server Management Studio to configure and schedule database backups, because the Maintenance Plan Wizard makes it very quick and easy to click through a few screens and select the appropriate options. However, on some of the servers running in the "Jameson Datacenter" (a.k.a. my basement), I only have SQL Server Express installed, not the full SQL Server product. For example, on my server that runs Windows Server Update Services (WSUS), I use SQL Server Express as the "backend" storage solution, because for this particular scenario, I want to keep the database local instead of relying on a separate server. However, I still want to ensure that I have periodic backups of the databases.

In order to make this as painless as possible, I wrote the following script:

USE [Tools]
GO
/****** Object:  StoredProcedure [dbo].[BackupUserDatabases]    Script Date: 03/15/2007 07:55:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[BackupUserDatabases]
(
    @backupType VARCHAR(15)
)
AS

DECLARE @backupFolder VARCHAR(255)

SET @backupFolder = 'C:\BackedUp\Microsoft SQL Server\MSSQL.1\MSSQL\Backup\' + @backupType

DECLARE @timestamp DATETIME
DECLARE @dateString VARCHAR(8)
DECLARE @timeString VARCHAR(12)

SELECT @timestamp = GETDATE()

SELECT @dateString = CONVERT(VARCHAR(8), @timestamp, 112)

SELECT @timeString = CONVERT(VARCHAR(12), @timestamp, 14)

-- Remove seconds from timestamp
SET @timeString = LEFT(REPLACE(@timeString,':',''), 4)

DECLARE @databases TABLE
(
    ID INT IDENTITY ( 1, 1 )
    , DatabaseName SYSNAME
)

INSERT INTO
    @databases 
    SELECT name
    FROM master.dbo.sysdatabases 
    WHERE name NOT IN ( 'master', 'model', 'msdb', 'tempdb' )

DECLARE @id TINYINT
SELECT @id = MIN ( ID ) FROM @databases

WHILE @id IS NOT NULL BEGIN
    DECLARE @databaseName SYSNAME
    SELECT @databaseName = DatabaseName FROM @databases WHERE ID = @id

    DECLARE @backupFileName VARCHAR(512)
    SELECT @backupFileName = @databaseName + '_backup_' +@dateString + @timeString + '.bak'

    IF @backupType = 'Full'
    BEGIN
        EXEC ('BACKUP DATABASE [' + @databaseName + '] TO DISK =''' + @backupFolder + '\' + @BackupFileName + '''')
    END
    ELSE IF @backupType = 'Differential'
    BEGIN
        EXEC ('BACKUP DATABASE [' + @databaseName + '] TO DISK =''' + @backupFolder + '\' + @BackupFileName + ''' WITH DIFFERENTIAL')
    END
    ELSE IF @backupType = 'Transaction Log'
    BEGIN
        DECLARE @recoveryModel SQL_VARIANT

        SELECT @recoveryModel = DATABASEPROPERTYEX(@databaseName, 'Recovery')

        IF @recoveryModel <> 'SIMPLE'
        BEGIN
            EXEC ('BACKUP LOG [' + @databaseName + '] TO DISK =''' + @backupFolder + '\' + @BackupFileName + '''')
        END
    END
    ELSE
    BEGIN
        RAISERROR ('Invalid backup type', 16, 1)
    END

    DELETE FROM @databases WHERE ID = @id

    SELECT @id = MIN ( ID ) FROM @databases
END

Well, technically, it's a stored procedure, but nevertheless I still keep the script to generate the sproc in my toolbox: BackupUserDatabases.sql. Notice that I create the sproc in a separate database (I arbitrarily chose the name Tools).

I can then schedule full, differential, and transaction log backups using scheduled tasks, as shown below.

Figure 1: Scheduled tasks for backing up databases

Here is the command behind one of the scheduled tasks (you can easily deduce the others):

"C:\Program Files\Microsoft SQL Server\90\Tools\Binn\SQLCMD.EXE" -S .\SQLExpress -d Tools -Q
   "EXEC BackupUserDatabases @backupType='Full'"

Lastly, note that I have a separate server periodically ROBOCOPY the backup files off of this server to another location -- just in case the WSUS server happens to catch on fire or some other act of God completely wipes out the local database backups ;-)