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.
- It creates real databases on whatever instance you are connected to. As written that is
10of them, namedmigdb_*, 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
DECLAREblock 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;
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.
@Count1 or 2 the first time and confirm the names look the way you expect.@Prefix<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@DataSizeMB@LogSizeMBFrequently 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.
Creating the databases is the setup step. These are what the setup is usually for.
- Database Summary, confirm what you just created, and spot the batch you forgot to drop.
- Backup Coverage, the check these test databases are usually created to exercise.
- Generate Backup and Restore Scripts, drive a restore sweep across the batch once it exists.
- DBA Scripts: The Complete Guide, every script on the site, with its safety class.
Leave a Reply