Technical Article

usp_PartitionDatabase

,

EXEC usp_PartitionDatabase '<dbname>', '<right_or_left>', '<date_or_numeric>', '<interval>', '<set_max_value>'

For <interval> the options are:
If datetime key, interval may be: month, quarter, semester, year, between single quotes
If datetime key, set_max_value is a maximum date in dd-mm-yyyy format between single quotes
If numeric key, interval may be: hundreds, thousands, t_thousands (tens of thousand), h_thousands (hundreds of thousand), millions, between single quotes
If numeric key, set_max_value is a maximum number, between single quotes

Example with numeric key:
EXEC usp_PartitionDatabase 'database', 'left', 'numeric', 'h_thousands', '10000000'

Example with datetime key:
EXEC usp_PartitionDatabase 'database', 'right', 'date', 'quarter', '01-01-2011'

CREATE PROC usp_PartitionDatabase @dbname VARCHAR(50) = NULL, @type NCHAR(5) = NULL, @dateornumeric NVARCHAR(8) = NULL, @interval NVARCHAR(50) = NULL, @setmax NVARCHAR(10) = NULL
AS
--
-- usp_PartitionDatabase.sql - Database Partitioning statement creation.
--
-- For <interval> the options are:
-- If datetime key, interval may be: month, quarter, semester, year, between single quotes
-- If datetime key, set_max_value is a maximum date in dd-mm-yyyy format between single quotes
-- If numeric key, interval may be: hundreds, thousands, t_thousands (tens of thousand), h_thousands (hundreds of thousand), millions, between single quotes
-- If numeric key, set_max_value is a maximum number, between single quotes
--
-- EXEC usp_PartitionDatabase '<dbname>', '<right_or_left>', '<date_or_numeric>', '<interval>', '<set_max_value>'
-- 
-- 2008-12-09 Pedro Lopes (NovaBase) pedro.lopes@novabase.pt
--
-- Example with numeric key:
-- EXEC usp_PartitionDatabase 'database', 'left', 'numeric', 'h_thousands', '10000000'
--
-- Example with datetime key:
-- EXEC usp_PartitionDatabase 'database', 'right', 'date', 'quarter', '01-01-2011'
--
SET NOCOUNT ON;
SET DATEFORMAT DMY;

IF (@dbname IS NULL OR @dateornumeric IS NULL OR @interval IS NULL OR @type IS NULL) AND @interval NOT IN ('left', 'right') AND @dateornumeric NOT IN ('date', 'numeric')
	BEGIN
		SELECT 'Usage: EXEC usp_PartitionDatabase ''<dbname>'', ''<right_or_left>'', ''<date_or_numeric>'', ''<interval>'', ''<set_max_value>''' AS 'WARNING - SYNTAX ERROR!'
		RETURN
	END
	
--Common setup
DECLARE @sqlcmdfg NVARCHAR(100), @sqlcmdfile NVARCHAR(1000), @filepath NVARCHAR(500), @filepathcmd NVARCHAR(150)
DECLARE @PFunc NVARCHAR(max), @SFunc NVARCHAR(max), @setmaxdate DATETIME, @setmaxnum bigint, @i int
IF LOWER(@dateornumeric) = 'numeric'
BEGIN
	SELECT @setmaxnum = CONVERT(bigint, @setmax)
END
IF LOWER(@dateornumeric) = 'date'
BEGIN
	SELECT @setmaxdate = CONVERT(DATETIME, @setmax)
END

DECLARE @tblpath TABLE (datapath NVARCHAR(500))
CREATE TABLE ##tblfiles (FG int, FN int)
DECLARE @exfg int, @exfn int, @excmd NVARCHAR(500)
SET @interval = RTRIM(UPPER(@interval))
SET @type = RTRIM(@type)
SET @excmd = 'INSERT INTO ##tblfiles SELECT (SELECT COUNT(DISTINCT name) FROM ' + @dbname + '.sys.filegroups), (SELECT COUNT(DISTINCT name) FROM ' + @dbname + '.sys.sysfiles)'
EXECUTE sp_executesql @excmd
SET @exfg = (SELECT FG FROM ##tblfiles)
SET @exfn = (SELECT FN FROM ##tblfiles)

SET @filepathcmd = 'SELECT TOP 1 LEFT(filename, LEN(filename)-PATINDEX(''%\%'', REVERSE(filename))) FROM ' + @dbname + '.sys.sysfiles WHERE [groupid] = 1'
INSERT INTO @tblpath
EXEC sp_executesql @filepathcmd
SET @filepath = (SELECT datapath FROM @tblpath)

IF LOWER(@dateornumeric) = 'date'
BEGIN
	--Setup
	DECLARE @NewFGDate NCHAR(8), @ddiff int, @NewPFDate NCHAR(8), @NewPSDate NCHAR(8)
	DECLARE @tbldates TABLE (mdate CHAR(8), pfdate CHAR(8))
	IF LOWER(@interval) = 'month'
	BEGIN
		SELECT @ddiff = DATEDIFF(mm, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0), @setmaxdate)
		INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),105),'-',''),
			REPLACE(CONVERT(VARCHAR(10),(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),126),'-',''))
		SET @i = 1
		WHILE @i <> @ddiff
		BEGIN
			INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(mm, DATEDIFF(mm, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i, 0)),105),'-',''),
				REPLACE(CONVERT(VARCHAR(10),(DATEADD(mm, DATEDIFF(mm, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i, 0)),126),'-',''))
			SET @i = @i + 1
		END
	END
	IF LOWER(@interval) = 'quarter'
	BEGIN
		SELECT @ddiff = DATEDIFF(qq, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0), @setmaxdate)
		INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),105),'-',''),
			REPLACE(CONVERT(VARCHAR(10),(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),126),'-',''))
		SET @i = 1
		WHILE @i <> @ddiff
		BEGIN
			INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(qq, DATEDIFF(qq, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i, 0)),105),'-',''),
				REPLACE(CONVERT(VARCHAR(10),(DATEADD(qq, DATEDIFF(qq, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i, 0)),126),'-',''))
			SET @i = @i + 1
		END
	END
	IF LOWER(@interval) = 'semester'
	BEGIN
		SELECT @ddiff = (DATEDIFF(qq, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0), @setmaxdate))/2
		INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),105),'-',''),
			REPLACE(CONVERT(VARCHAR(10),(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),126),'-',''))
		SET @i = 1
		WHILE @i <> @ddiff
		BEGIN
			INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(qq, DATEDIFF(qq, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i*2, 0)),105),'-',''),
				REPLACE(CONVERT(VARCHAR(10),(DATEADD(qq, DATEDIFF(qq, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i*2, 0)),126),'-',''))
			SET @i = @i + 1
		END
	END
	IF LOWER(@interval) = 'year'
	BEGIN
		SELECT @ddiff = DATEDIFF(yy, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0), @setmaxdate)
		INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),105),'-',''),
			REPLACE(CONVERT(VARCHAR(10),(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)),126),'-',''))
		IF @ddiff > 1
		BEGIN
			SET @i = 1
			WHILE @i <> @ddiff
			BEGIN
				INSERT INTO @tbldates VALUES (REPLACE(CONVERT(VARCHAR,(DATEADD(yy, DATEDIFF(yy, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i, 0)),105),'-',''),
				REPLACE(CONVERT(VARCHAR(10),(DATEADD(yy, DATEDIFF(yy, 0, (DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))) + @i, 0)),126),'-',''))
				SET @i = @i + 1
			END
		END
	END
	--Filegroups and Files
	IF (SELECT COUNT(mdate) + @exfg FROM @tbldates) > 32767 
	BEGIN
		SELECT 'Maximum number of filegroups per database may only be 32767. Please change the input parameters...' AS 'WARNING - FATAL ERROR!'
		RETURN		
	END
	IF (SELECT COUNT(mdate) + @exfn FROM @tbldates) > 32767 
	BEGIN
		SELECT 'Maximum number of files per database may only be 32767. Please change the input parameters...' AS 'WARNING - FATAL ERROR!'
		RETURN		
	END
	DECLARE cdate CURSOR FOR SELECT mdate FROM @tbldates
	OPEN cdate
	FETCH NEXT FROM cdate INTO @NewFGDate
	WHILE @@FETCH_STATUS = 0
	BEGIN
		SET @sqlcmdfg = 'ALTER DATABASE [' + @dbname + '] ADD FILEGROUP FG' + @NewFGDate + ';'
		PRINT @sqlcmdfg
		SET @sqlcmdfile = 'ALTER DATABASE [' + @dbname + '] ADD FILE (NAME = ' + @dbname + '_Data_' + @NewFGDate + ', FILENAME = ''' + @filepath + '\' + @dbname + '_Data_' + @NewFGDate + '.NDF'', SIZE = 100MB, FILEGROWTH = 250MB) TO FILEGROUP FG' + @NewFGDate + ';'
		PRINT @sqlcmdfile
		FETCH NEXT FROM cdate INTO @NewFGDate
	END
	CLOSE cdate
	DEALLOCATE cdate;

	--Partition Function and Partition Scheme
	SET @PFunc = 'IF EXISTS (SELECT * FROM sys.partition_functions WHERE name = ''pf' + LOWER(@interval) + ''')
DROP PARTITION FUNCTION [pf' + LOWER(@interval) + '];
CREATE PARTITION FUNCTION [pf' + LOWER(@interval) + '] (datetime) AS RANGE ' + @type + ' FOR VALUES ('
	SET @SFunc = 'IF EXISTS (SELECT * FROM sys.partition_schemes WHERE name = ''ps' + LOWER(@interval) + 'Range'')
DROP PARTITION SCHEME ps' + LOWER(@interval) + 'Range;
CREATE PARTITION SCHEME ps' + LOWER(@interval) + 'Range AS PARTITION pf' + LOWER(@interval) + ' TO ([PRIMARY],'
	DECLARE cdate CURSOR FOR SELECT mdate, pfdate FROM @tbldates
	OPEN cdate
	FETCH NEXT FROM cdate INTO @NewPSDate, @NewPFDate
	WHILE @@FETCH_STATUS = 0
	BEGIN
		SET @PFunc = @PFunc + CHAR(39) + @NewPFDate + CHAR(39) + ','
		SET @SFunc = @SFunc + CHAR(91) + 'FG' + @NewPSDate + CHAR(93) + ','
		FETCH NEXT FROM cdate INTO @NewPSDate, @NewPFDate
	END
	CLOSE cdate
	DEALLOCATE cdate;
	SET @PFunc = LEFT(@PFunc, LEN(@PFunc)-1) + ');'
	SET @SFunc = LEFT(@SFunc, LEN(@SFunc)-1) + ');'
	PRINT @PFunc
	PRINT @SFunc

	PRINT '-- Table creation example:
-- CREATE TABLE [schema].[tablename](
-- [pkcolumn] int,
-- [pkcolumn2] int,
-- [somecolumns] NVARCHAR(10),
-- [creationDate] [datetime])
-- ON ps' + LOWER(@interval) + 'Range (creationDate)
--
-- Constraint creation example:
-- ALTER TABLE [schema].[tablename] ADD CONSTRAINT [PK_tablename] PRIMARY KEY NONCLUSTERED 
-- (
--	[pkcolumn] ASC,
--	[pkcolumn2] ASC
-- )
-- WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
-- ON ps' + LOWER(@interval) + 'Range (creationDate)'

END
ELSE IF LOWER(@dateornumeric) = 'numeric'
BEGIN
	IF @setmax IS NULL
		BEGIN
			SELECT 'Usage: EXEC usp_PartitionDatabase ''<dbname>'', ''<right_or_left>'', ''<date_or_numeric>'', ''<interval>'', ''<set_max_value>''' AS 'WARNING - SYNTAX ERROR!'
			RETURN
		END
	--Setup
	DECLARE @divisions int, @intervalint int, @PFuncRange NVARCHAR(max), @SFuncRange NVARCHAR(max)
	SELECT @intervalint = CASE WHEN @interval = 'hundreds' THEN 100 WHEN @interval = 'thousands' THEN 1000 WHEN @interval = 't_thousands' THEN 10000 WHEN @interval = 'h_thousands' THEN 100000 WHEN @interval = 'millions' THEN 1000000 END
	SET @divisions = @setmaxnum/@intervalint
	IF @divisions + @exfg > 32767 
	BEGIN
		SELECT 'Maximum number of filegroups per database may only be 32767. Please change the input parameters...' AS 'WARNING - FATAL ERROR!'
		RETURN		
	END
	IF @divisions + @exfn > 32767 
	BEGIN
		SELECT 'Maximum number of files per database may only be 32767. Please change the input parameters...' AS 'WARNING - FATAL ERROR!'
		RETURN		
	END
	SET @i = 1
	WHILE @i <> @divisions
		BEGIN
			--Filegroups and Files
			SET @sqlcmdfg = 'ALTER DATABASE [' + @dbname + '] ADD FILEGROUP FG' + CONVERT(NVARCHAR, @i*@intervalint) + ';'
			PRINT @sqlcmdfg
			SET @sqlcmdfile = 'ALTER DATABASE [' + @dbname + '] ADD FILE (NAME = ' + @dbname + '_Data_' + CONVERT(NVARCHAR, @i*@intervalint) + ', FILENAME = ''' + @filepath + '\' + @dbname + '_Data_' + CONVERT(NVARCHAR, @i*@intervalint) + '.NDF'', SIZE = 100MB, FILEGROWTH = 250MB) TO FILEGROUP FG' + CONVERT(NVARCHAR, @i*@intervalint) + ';'
			PRINT @sqlcmdfile
			SET @i = @i + 1
		END
	SET @i = 1
	WHILE @i <> @divisions
		BEGIN
			--Partition Function
			IF @i = 1
			BEGIN
				SET @PFuncRange = CONVERT(NVARCHAR, @i*@intervalint)
			END
			IF @i > 1
			BEGIN
				SET @PFuncRange = @PFuncRange + ',' + CONVERT(NVARCHAR, @i*@intervalint)
			END
			SET @i = @i + 1
		END
	SET @PFunc = 'IF EXISTS (SELECT * FROM sys.partition_functions WHERE name = ''pf' + LOWER(@interval) + ''')
DROP PARTITION FUNCTION [pf' + LOWER(@interval) + '];
CREATE PARTITION FUNCTION [pf' + LOWER(@interval) + '] (int) AS RANGE ' + @type + ' FOR VALUES (' + @PFuncRange + ');'
	PRINT @PFunc
	SET @i = 1
	WHILE @i <> @divisions
		BEGIN
			--Partition Scheme
			IF @i = 1
			BEGIN
				SET @SFuncRange = CHAR(91) + 'FG' + CONVERT(NVARCHAR, @i*@intervalint) + CHAR(93)
			END
			IF @i > 1
			BEGIN
				SET @SFuncRange = @SFuncRange + ',' + CHAR(91) + 'FG' + CONVERT(NVARCHAR, @i*@intervalint) + CHAR(93)
			END
			SET @i = @i + 1
		END
	SET @SFunc = 'IF EXISTS (SELECT * FROM sys.partition_schemes WHERE name = ''ps' + LOWER(@interval) + 'Range'')
DROP PARTITION SCHEME ps' + LOWER(@interval) + 'Range;
CREATE PARTITION SCHEME ps' + LOWER(@interval) + 'Range AS PARTITION pf' + LOWER(@interval) + ' TO ([PRIMARY],' + @SFuncRange + ';'
	PRINT @SFunc

	PRINT '-- Table creation example:
-- CREATE TABLE [schema].[tablename](
-- [pkcolumn] int,
-- [pkcolumn2] int,
-- [somecolumns] NVARCHAR(10),
-- [serialnumber] [int])
-- ON ps' + LOWER(@interval) + 'Range (serialnumber)
--
-- Constraint creation example:
-- ALTER TABLE [schema].[tablename] ADD CONSTRAINT [PK_tablename] PRIMARY KEY NONCLUSTERED 
-- (
--	[pkcolumn] ASC,
--	[pkcolumn2] ASC
-- )
-- WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
-- ON ps' + LOWER(@interval) + 'Range (serialnumber)'

END
ELSE
BEGIN
	SELECT 'Usage: EXEC usp_PartitionDatabase ''<dbname>'', ''<right_or_left>'', ''<date_or_numeric>'', ''<interval>'', ''<set_max_value>''' AS 'WARNING - SYNTAX ERROR!'
	RETURN
END

DROP TABLE ##tblfiles

Rate

4.67 (6)

You rated this post out of 5. Change rating

Share

Share

Rate

4.67 (6)

You rated this post out of 5. Change rating