DBA Scripts: Generate Test Databases

🔧Part of the DBA-Tools Project, copy/paste SQL Server scripts and health checks.In: Backups & Recovery

When you’re validating migrations, failover, or automation at scale, you need realistic volume: hundreds or thousands of databases to stress-test backups, restores, log shipping, and copy operations.

This helper automates that setup, it creates batches of small, randomized databases with configurable sizes and naming, so you can reproduce large-scale scenarios quickly and repeatably.


Read before you run

  • It creates real databases on whatever instance you are connected to. As written that is 10 of them, named migdb_*, each with a 25 MB data file and a 10 MB log.
  • Safety: creates objects, impact high. This is not a read-only script.
  • Needs: sysadmin or dbcreator
  • Edit the DECLARE block first. The parameters at the top are the whole interface, and the defaults are a lab’s defaults, not yours.
  • Lab and test instances only. There is no undo and no cleanup step: dropping them afterwards is on you.

/*
Script Name : New-TestDatabases
Category    : dba-lab
Purpose     : Create multiple test databases with randomised names for lab and migration scenarios.
Author      : Peter Whyte (https://sqldba.blog/dba-scripts-generate-test-databases/)
Requires    : sysadmin or dbcreator
Notes       : Edit the DECLARE parameter block before running in SSMS.
              For PowerShell-driven creation use powershell\lab\New-MultipleDatabases.ps1.
              For parameterised execution from a script use powershell\lab\Run-CreateTestDatabases.ps1.
*/
-- WARNING: Creates databases — review @Count and @Prefix before running
-- SAFE:CreatesObjects
-- IMPACT:High
SET NOCOUNT ON;

-- Parameters (edit these values before running)
DECLARE @Count      INT     = 10;
DECLARE @Prefix     SYSNAME = N'migdb';
DECLARE @StartIndex INT     = 1;
DECLARE @SuffixLen  INT     = 8;
DECLARE @IndexWidth INT     = 3;
DECLARE @DataSizeMB INT     = 25;
DECLARE @LogSizeMB  INT     = 10;

DECLARE @i   INT = @StartIndex;
DECLARE @End INT = @i + @Count - 1;
DECLARE @Name SYSNAME;
DECLARE @SQL  NVARCHAR(MAX);

WHILE @i <= @End
BEGIN
    SET @Name = @Prefix + '_'
        + RIGHT(REPLICATE('0', @IndexWidth) + CAST(@i AS VARCHAR(20)), @IndexWidth)
        + '_' + LEFT(REPLACE(CONVERT(VARCHAR(36), NEWID()), '-', ''), @SuffixLen);

    PRINT 'Creating: ' + @Name;

    IF DB_ID(@Name) IS NULL
    BEGIN
        BEGIN TRY
            SET @SQL = N'CREATE DATABASE [' + @Name + N'];';
            EXEC sp_executesql @SQL;

            DECLARE @DataLogical SYSNAME = NULL;
            DECLARE @LogLogical  SYSNAME = NULL;

            SELECT @DataLogical = mf.name FROM sys.master_files mf
            WHERE mf.database_id = DB_ID(@Name) AND mf.type_desc = 'ROWS';

            SELECT @LogLogical = mf.name FROM sys.master_files mf
            WHERE mf.database_id = DB_ID(@Name) AND mf.type_desc = 'LOG';

            IF @DataLogical IS NOT NULL
            BEGIN
                SET @SQL = N'ALTER DATABASE [' + @Name + N'] MODIFY FILE (NAME = N''' + @DataLogical
                    + ''', SIZE = ' + CAST(@DataSizeMB AS NVARCHAR(10)) + N'MB);';
                EXEC sp_executesql @SQL;
            END

            IF @LogLogical IS NOT NULL
            BEGIN
                SET @SQL = N'ALTER DATABASE [' + @Name + N'] MODIFY FILE (NAME = N''' + @LogLogical
                    + ''', SIZE = ' + CAST(@LogSizeMB AS NVARCHAR(10)) + N'MB);';
                EXEC sp_executesql @SQL;
            END
        END TRY
        BEGIN CATCH
            PRINT 'Failed creating ' + @Name + ': ' + ERROR_MESSAGE();
            THROW;
        END CATCH
    END

    SET @i += 1;
END

PRINT 'Done.';

SELECT
    name        AS database_name,
    create_date AS created_at,
    state_desc
FROM sys.databases
WHERE name LIKE @Prefix + N'_%'
ORDER BY create_date DESC;
How this was tested
Tested directly against a local SQL Server 2025 instance before publishing: ran with @Count = 2 and a disposable prefix, both databases created cleanly with correctly resized data and log files, confirmed via the script’s own closing sys.databases query, then dropped immediately after, nothing left behind. The version above is the real, current repo script.

Understanding the Parameters

The DECLARE block at the top is the entire interface. Read it before you run anything, because the defaults decide how many databases appear and what they are called.

@Count
How many databases to create. Ten by default, which is the number people are most often surprised by. Act when you have not read it. Set it to 1 or 2 the first time and confirm the names look the way you expect.
@Prefix
The name stem. Every database is <prefix>_<index>_<random>, so the prefix is also how you find them again to drop them. Act when the default migdb collides with something real on that instance. Pick a stem nothing else uses.
@StartIndex
@IndexWidth
@SuffixLen
The numbering and the random tail. The random suffix is what lets you run it twice without a name collision; the index is what keeps the list readable in Object Explorer.
@DataSizeMB
@LogSizeMB
The size each file is grown to after creation. Small on purpose — the point is database count, not volume. Ten databases at the defaults is roughly 350 MB. Act when you are testing backup or restore duration, where file size is the variable that matters.


Frequently Asked Questions

How do I clean up afterwards?

There is deliberately no cleanup step in the script, because a script that drops databases by prefix is a far more dangerous thing to have lying around than one that creates them. Drop them by hand, or generate the statements first and read them before running: SELECT 'DROP DATABASE [' + name + '];' FROM sys.databases WHERE name LIKE 'migdb!_%' ESCAPE '!';

Is it safe to run twice?

Yes. Each name carries a random suffix, and the script skips any name that already exists, so a second run adds a second batch rather than failing or overwriting. That also means the count grows every time you run it — two runs at the defaults leaves twenty databases, not ten.

Why not just restore the same database ten times?

Because most of what you want to test at scale is per-database overhead rather than data: how long a backup sweep takes across many databases, whether a maintenance job scales, how the instance behaves with hundreds of log files. Small empty databases reproduce that faithfully and cost almost nothing.

Can I run it on production?

No. It is SAFE:CreatesObjects with IMPACT:High, and it creates databases on whatever instance the connection points at. Lab and test instances only.


Where To Go Next

Creating the databases is the setup step. These are what the setup is usually for.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *