Technical Article

Add files to filegroups and log for many dbs

,

Purpose: This SP is used to add a logfile and a datafile to each file group in a database or all databases. This is handy when you have a lot of databases and you want to add a new big SAN-disk and stop db expansion on the old disks.
Consider: Avoid running any large updates when you use this SP. I freeze expansion of old databasefiles while adding another 10 MB extra for safety when I set max size (current size plus 10 MB), if someone is adding data anyway.
Performance: Intel 1000 MHz x 2, 2 GB RAM, StorageTek SAN- disks, 500 databases with a total of 220 GB. Added another SAN-disk with 290 GB. Freezing the old files for the 500 databases and adding a logfile, a file for each of the two filegroups took 25 minutes in daytime on a heavily used system.

use Filer
go
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

IF EXISTS (SELECT name FROM sysobjects WHERE name = 'spF_add_files_group' AND type = 'P' )
    DROP PROC spF_add_files_group
GO
CREATE PROC spF_add_files_group 
@seldb varchar(100) = '',--- blank expands all databases
@mydisk varchar(100)--- exemple 'U:\TestDB\'
AS
/*
Created by:Lennart Gerdvall
E-mail:lge@faktab.se
Company:Faktab Finans AB, S:t Hansplan 1, SE-62188 Visby, Sweden
Phone:+46(0)498 20 20 00
Fax:+46(0)498 20 20 90
Created:2003-02-20
Altered:2003-03-06 by Lennart Gerdvall (revised the English text)
Purpose:This SP is used to add a logfile and a datafile to each file group
in a database or all databases.
Consider:Avoid running any large updates when you use this SP. I freeze expansion
of old databasefiles while adding another 10 MB extra for safety when I set
max size (current size plus 10 MB), if someone is adding data anyway.
Performance:Intel 1000 MHz x 2, 2 GB RAM, StorageTek SAN- disks, 500 databases with
a total of 220 GB. Added another SAN-disk with 290 GB. Freezing the old
files for the 500 databases and adding a logfile, a file for each of the
two filegroups took 25 minutes in daytime on a heavily used system. 
*//*
Must first run this table script in a database used for administrative purposes (Filer) to create the nessecary table!
*//*
USE Filer
CREATE TABLE [dbo].[dbfilesgroups] (
[dbname] [varchar] (30) NOT NULL,
[fileid] [smallint] NOT NULL,
[size][int] NOT NULL,
[maxsize] [int] NOT NULL,
[growth] [int] NOT NULL,
[name] [nvarchar](128) NOT NULL,
[filename] [nvarchar](260) NOT NULL,
[groupname] [sysname] NULL 
) ON [PRIMARY]
*/---Populate the dbfilesgroups table with original file information
---use Filer  -- Select database where above table (dbfilesgroups) is created. DDL is above.
set nocount on
declare @dbasename varchar (100),
 @mydb varchar (100),
 @mygroup varchar (100),
 @mycount int,
 @sqlstmt varchar(4000),
 @myattach varchar(8000),
 @mydetach varchar(1000),
 @mynewfile varchar(1000),
 @mymove varchar(500),
 @filetomove varchar(500),
 @movecommand varchar(4000),
@killcommand varchar(500),
@singlecommand varchar(500),
@multicommand varchar(500),
 @groupcounter NUMERIC,
 @filegroupcounter NUMERIC,
 @maxfileid NUMERIC,
@filecounter NUMERIC,
 @globalcounter NUMERIC

set @mycount = 0
truncate table dbfilesgroups -- Empty all records in dbfilesgroups table

---Open cursor with all or one user db name
BEGIN
IF @seldb <> '' -- A named database has been specified
declare cur_databasetab cursor for select name from master..sysdatabases
where name like ('"' + @seldb + '%"') for read only
ELSE---Use all databases but system dbs and demo dbs
declare cur_databasetab cursor for select name from master..sysdatabases
where name not in ('master','model','msdb','pubs','northwind', 'tempdb') 
order by name for read only
END

open cur_databasetab 
fetch next from cur_databasetab into @dbasename
WHILE (@@FETCH_STATUS = 0) 
BEGIN 
select @sqlstmt = ('insert into dbfilesgroups select ' + 
"'"+@dbasename + "',"+ ' fileid, [size], maxsize, growth, ltrim(rtrim(name)), 
ltrim(rtrim(filename)),  ltrim(rtrim(groupname))' + 
'from ' + "["+ @dbasename +"]"+ '..sysfiles LEFT OUTER JOIN ' +
"["+ @dbasename +"]"+ '..sysfilegroups ON ' + 
"["+ @dbasename +"]"+ '..sysfiles.groupid = ' + 
"["+ @dbasename +"]"+ '..sysfilegroups.groupid')
exec (@sqlstmt)
fetch next from cur_databasetab into @dbasename
END
close cur_databasetab
deallocate cur_databasetab

---Loop through the databases
---Open cursor with all or one user db name
BEGIN
IF @seldb <> '' -- A named database has been specified
declare cur_selecteddbs cursor local for select name from master..sysdatabases
where name like ('"' + @seldb + '%"') for read only
ELSE---Use all databases but system dbs and demo dbs
declare cur_selecteddbs cursor local for select name from master..sysdatabases
where name not in ('master','model','msdb','pubs','northwind', 'tempdb') 
order by name for read only
END

open cur_selecteddbs 
fetch next from cur_selecteddbs
into @mydb
WHILE (@@FETCH_STATUS = 0) 
BEGIN 
---Reset counter for files in the regular filegrous (groupname inte NULL) for the current database
select @filegroupcounter = 0
---Reset counter for number of files in a filegroup
select @groupcounter = 1
---Reset counter for number of files in a database
select @filecounter = 1
---Reset counter for max fileid in a database
select @maxfileid = 1

---Create a new file for every filegroup including the NULL filegroup (which is the log filegroup)
declare cur_selectgroups cursor local for select groupname from dbfilesgroups
where dbname = @mydb group by groupname for read only
open cur_selectgroups 
fetch next from cur_selectgroups into @mygroup
WHILE (@@FETCH_STATUS = 0) 
BEGIN 
---Check if filegroup is a normal group or a NULL-group (which is the log filegroup)
IF @mygroup IS NULL
BEGIN
---How many files are there in the log filegroup for the current database?
select @groupcounter = count(fileid) from dbfilesgroups  where dbname = @mydb and groupname IS NULL
---Add a new file on the new disk to the log filegroup for the current database!
select @groupcounter = @groupcounter + 1
select @mynewfile = 'ALTER DATABASE ' + '[' + @mydb + ']' + ' ADD LOG FILE ' + char(13) +
'( NAME = ' + @mydb + '_GenLog' + CAST(@groupcounter AS varchar(3)) + ',' + char(13) +
'  FILENAME = ' + CHAR(39) + @mydisk + @mydb + '_GenLog' + CAST(@groupcounter AS varchar(3)) + '.LDF' + CHAR(39) + ',' + char(13) +
'  SIZE = 10MB, MAXSIZE = UNLIMITED, FILEGROWTH = 10MB)'
exec (@mynewfile)
END

ELSE
BEGIN
select @filegroupcounter = @filegroupcounter + 1
---How many files are there in the current filegroup for the current database?
select @groupcounter = count(fileid) from dbfilesgroups  where dbname = @mydb and groupname IS NOT NULL
---Add a new file on the new disk to the current filegroup for the current database!
select @groupcounter = @groupcounter + @filegroupcounter
select @mynewfile = 'ALTER DATABASE ' + '[' + @mydb + ']' + ' ADD FILE ' + char(13) +
'( NAME = ' + @mydb + '_GenData' + CAST(@groupcounter AS varchar(3)) + ',' + char(13) +
'  FILENAME = ' + CHAR(39) + @mydisk + @mydb + '_GenData' + CAST(@groupcounter AS varchar(3)) + '.NDF' + CHAR(39) + ',' + char(13) +
'  SIZE = 10MB, MAXSIZE = UNLIMITED, FILEGROWTH = 10MB)' + char(13) + 'TO FILEGROUP [' + @mygroup + ']'
exec (@mynewfile)
END

fetch next from cur_selectgroups into @mygroup
END
close cur_selectgroups
deallocate cur_selectgroups

BEGIN
--- Create ALTER DATABASE FILE STATEMENTS FOR EXISTING DATABASE FILES TO STOP FURTHER GROWTH
select @mycount = count(fileid) from dbfilesgroups where dbname = @mydb
SELECT @globalcounter = 1

WHILE (@globalcounter <= @mycount)
BEGIN
select @mymove = 'ALTER DATABASE ' + '[' + @mydb + ']' + char(13) + ' MODIFY FILE ' + char(13) + '(NAME = '
select @filetomove = (select b.name from dbfilesgroups b where b.fileid = @globalcounter and a.dbname = b.dbname) + ',' + char(13)
from dbfilesgroups a where dbname = @mydb and fileid = 1 --so records are not listed twice
select @movecommand = @mymove + @filetomove ---+ ' ' + @filetomove +  + CHAR(39)

/*Create the MAXSIZE statement*/select @myattach = 'MAXSIZE = ' + 
(select CAST(((b.size * 8/1024) + 10) AS varchar(10)) from dbfilesgroups b where b.fileid = @globalcounter and a.dbname = b.dbname)+ 'MB)' +
char(13)
from dbfilesgroups a where dbname = @mydb and fileid = 1 --so records are not listed twice
select @myattach = @movecommand + @myattach
exec (@myattach)
SELECT @globalcounter = @globalcounter + 1
END
END
fetch next from cur_selecteddbs into @mydb
END
close cur_selecteddbs
deallocate cur_selecteddbs
GO

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating