SQL Change Automation 4

Configure database files and filegroups

Notable points to remember about pre-deployment and post-deployment scripts

  • Pre and post deployment scripts must be re-runnable as they will be executed as part of every deployment. 
  • Pre and post deployment scripts are deployed to every environment
  • You need to provide your own error handling as desired in pre and post deployment scripts - the examples below use a TRY/CATCH pattern
  • See the main page regarding ordering, transaction management, and more

Considerations when using data virtualization

If you are using a SQL Clone image as a baseline, existing database files and filegroups will automatically be carried through to development and build environments via the clone. In this case, there is no requirement for you to add scripts which mimic this configuration.

If you wish to deploy changes to files and filegroups via your automated pipeline while using clone as baseline, at that point it may make sense to use a pre-deployment script.

Pre-deployment script to add a filegroup and a file using a static name

In this example:

Pre-deployment script to add a filegroup and a file Toggle source code

  1. /*
  2. Pre-Deployment Script Example
  3. --------------------------------------------------------------------------------------
  4. This file contains SQL statements that will be prepended to the build script.
  5. This example requires SQLCMD syntax
  6. For more information, see https://documentation.red-gate.com/display/sca4/Pre-deployment+and+post-deployment+scripts
  7. --------------------------------------------------------------------------------------
  8. */
  9.  
  10. /* This may be be uncommented and edited to use a SQLCMD variable for testing in SSMS
  11. under SQLCMD mode (Query menu -> SQLCMD), but remove or comment this out before committing. */
  12. --:setvar DatabaseName YourDatabaseName
  13.  
  14.  
  15. SET XACT_ABORT, NOCOUNT ON;
  16. GO
  17.  
  18. USE [$(DatabaseName)];
  19. GO
  20.  
  21. BEGIN TRY
  22.  
  23. DECLARE @fg sysname = N'ExampleFileGroup',
  24. @filename NVARCHAR(1000) = N'',
  25. @path NVARCHAR(1000) = N'C:\MSSQL\DATA\',
  26. @msg NVARCHAR(2048) = N'',
  27. @physicalName NVARCHAR(1000) = N'',
  28. @dynamicSQL NVARCHAR(MAX) = N'';
  29.  
  30. /* Guard clauses like this may prevent or allow deployment if needed */
  31. IF @@SERVERNAME = N'SERVERNAME-TO-BLOCK'
  32. BEGIN
  33. SET @msg = N'SERVERNAME is ' + @@SERVERNAME + N', no action taken.';
  34. RAISERROR(@msg, 0, 0) WITH NOWAIT;
  35. END;
  36. ELSE
  37. BEGIN
  38.  
  39. /* It can be useful to uniquify filenames to enable concurrent builds, etc*/
  40. SET @filename = N'ExampleFileName' + N'$(DatabaseName)';
  41. SET @physicalName = @path + @filename + N'.ndf';
  42.  
  43. IF EXISTS (SELECT * FROM sys.filegroups WHERE name = @fg)
  44. BEGIN
  45. SET @msg = N'Filegroup ' + @fg + N' exists in [$(DatabaseName)], no action taken.';
  46. RAISERROR(@msg, 0, 0) WITH NOWAIT;
  47. END;
  48. ELSE
  49. BEGIN
  50. SET @msg = N'Adding filegroup ' + @fg + N' to database [$(DatabaseName)].';
  51. RAISERROR(@msg, 0, 0) WITH NOWAIT;
  52.  
  53. SET @dynamicSQL = N'ALTER DATABASE CURRENT ADD FILEGROUP ' + QUOTENAME(@fg);
  54. --PRINT @dynamicSQL;
  55. EXEC (@dynamicSQL);
  56. END;
  57.  
  58. /*For simplicity we're checking only for existence of the file by physical name, not
  59. looking at filename and/or what filegroup it is in. You may chose to make this more thorough. */
  60. IF EXISTS (SELECT * FROM sys.database_files WHERE name = @filename)
  61. BEGIN
  62. SET @msg = N'File with name ' + @filename + N' exists in database ' + N'$(DatabaseName), no action taken.';
  63. RAISERROR(@msg, 0, 0) WITH NOWAIT;
  64. END;
  65. ELSE
  66. BEGIN
  67. SET @msg = N'Adding file with name ' + @filename + N' to ' + N'$(DatabaseName).';
  68. RAISERROR(@msg, 0, 0) WITH NOWAIT;
  69.  
  70.  
  71. SET @dynamicSQL
  72. = N'
  73. ALTER DATABASE CURRENT
  74. ADD FILE
  75. (
  76. NAME = ''' + @filename + N''',
  77. FILENAME = ''' + @physicalName + N'''
  78. )
  79. TO FILEGROUP ' + QUOTENAME(@fg);
  80.  
  81. --PRINT @dynamicSQL;
  82. EXEC (@dynamicSQL);
  83. END;
  84. END;
  85.  
  86. END TRY
  87. BEGIN CATCH
  88. IF @@trancount > 0
  89. ROLLBACK TRANSACTION;
  90. SET @msg = ERROR_MESSAGE();
  91. RAISERROR(@msg, 16, 1);
  92.  
  93. END CATCH;
  94.  

Pre-deployment script to add a filegroup and a file using a dynamic path based on the results of a query

In this example:

  • We use the built-in $(DatabaseName) variable to set the database context and other dynamic logic related to the database name
  • We use the path retrieved from SQL Server's SERVERPROPERTY('InstanceDefaultDataPath') to determine where to place the file on the target SQL Server

Pre-deployment script to add a filegroup and a file using a dynamic path based on the results of a query Toggle source code

  1. /*
  2. Pre-Deployment Script Example
  3. --------------------------------------------------------------------------------------
  4. This file contains SQL statements that will be prepended to the build script.
  5. This example requires SQLCMD syntax
  6. For more information, see https://documentation.red-gate.com/display/sca4/Pre-deployment+and+post-deployment+scripts
  7. --------------------------------------------------------------------------------------
  8. */
  9.  
  10. /* This may be be uncommented and edited to use a SQLCMD variable for testing in SSMS
  11. under SQLCMD mode (Query menu -> SQLCMD), but remove or comment this out before committing. */
  12. --:setvar DatabaseName YourDatabaseName
  13.  
  14.  
  15. SET XACT_ABORT, NOCOUNT ON;
  16. GO
  17.  
  18. USE [$(DatabaseName)];
  19. GO
  20.  
  21. BEGIN TRY
  22.  
  23.  
  24. DECLARE @filename sysname = N'',
  25. @msg NVARCHAR(1000) = N'',
  26. @dsql NVARCHAR(MAX) = N'';
  27. SET @filename = '$(DatabaseName)' + N'_fs';
  28.  
  29.  
  30. /* Check for the existence of a filegroup based on the name. This example uses the SQLCMD scripting variable */
  31. IF EXISTS
  32. (
  33. SELECT *
  34. FROM [$(DatabaseName)].sys.filegroups
  35. WHERE name = '$(DatabaseName)_fg'
  36. )
  37. BEGIN
  38. RAISERROR('Filegroup [$(DatabaseName)_fg] exists in database [$(DatabaseName)], no action taken.', 0, 0) WITH NOWAIT;
  39.  
  40. END;
  41. ELSE
  42. BEGIN
  43. RAISERROR(N'Adding filegroup [$(DatabaseName)_fg] to database [$(DatabaseName)]', 0, 0) WITH NOWAIT;
  44. ALTER DATABASE [$(DatabaseName)] ADD FILEGROUP [$(DatabaseName)_fg];
  45. END;
  46.  
  47.  
  48.  
  49. /* SQLCMD scripting variables don't embed in strings easily because of the way they are processed.
  50. The following pattern helps work around this. */
  51.  
  52.  
  53. IF EXISTS
  54. (
  55. SELECT *
  56. FROM [$(DatabaseName)].sys.database_files
  57. WHERE name = @filename
  58. )
  59. BEGIN
  60. SET @msg = N'File ' + @filename + N' exists in database ' + N'$(DatabaseName), no action taken.';
  61. RAISERROR(@msg, 0, 0) WITH NOWAIT;
  62. END;
  63. ELSE
  64. BEGIN
  65. SET @msg = N'Adding file ' + @filename + N' to database ' + N'$(DatabaseName)_fg';
  66. RAISERROR(@msg, 0, 0) WITH NOWAIT;
  67.  
  68. SET @dsql
  69. = N'
  70. ALTER DATABASE [#DBNAME#]
  71. ADD FILE
  72. (
  73. NAME = ''' + @filename
  74. + N''',
  75. FILENAME = ''#PATH##DBNAME#_fg''
  76. )
  77. TO FILEGROUP [#DBNAME#_fg];
  78. ' ;
  79. SET @dsql = REPLACE(@dsql, '#DBNAME#', '$(DatabaseName)');
  80. SET @dsql = REPLACE(@dsql, '#PATH#', CAST(SERVERPROPERTY('InstanceDefaultDataPath') AS VARCHAR(1000)));
  81.  
  82. --PRINT @dsql;
  83. EXEC (@dsql);
  84.  
  85. END;
  86.  
  87. END TRY
  88. BEGIN CATCH
  89. IF @@trancount > 0
  90. ROLLBACK TRANSACTION;
  91. SET @msg = ERROR_MESSAGE();
  92. RAISERROR(@msg, 16, 1);
  93.  
  94. END CATCH;
  95.  

Didn't find what you were looking for?