Overview and Background
The author wanted a tool to generate SQL statements for those cases not easily supported in SQL Server Management Studio (SSMS). SSMS provides features to:
- Script out schema objects as DDL statements, such as CREATE TABLE.
- Expand SSMS templates by manually entering template values for each template expansion.
- Request SSMS to generate INSERT, DELETE, SELECT, or UDPATE statements for a specific table.
Or, if not supported by SSMS, search for a utility script hardcoded to generate a specific SQL statement, e.g. foreign key constraints or triggers. The two MS tools, sp_msforeachdb and sp_msforeachtable, can be executed; however, these tools are limited to one template variable.
- Simple for SQL programmer to write a template string containing variables.
- Quick to specify variable values for expansion of template variables.
- Easy to process template many times, e.g. for every table in database.
The spExpandUserResultTemplate stored procedure processes a user-defined template string that contains template variables enclosed in curly braces, e.g. {TableName}. The text values to replace the template variables are contained in SQL result set, such as produced by a SELECT statement.
The SQL result set defines the association between the template variable and data value to replace the template variable. (Often, a SELECT statement on INFORMATION_SCHEMA views or catalog views produce the desired result set). Each row in result set is a set of template variable name and value pairs. Column name for the result set row is automatically used as template variable name. The column value is the replacement value for this template variable. Thus, the user's result set defines the complete set of template name/value pairs and each row represents one processing of the user's template.
The result of processing the template string against all rows in the SQL result set is stored in an OUTPUT parameter that can then be PRINT-ed or executed by sp_executesql at the user's option.
Parameters
The parameters and temporary tables expected by the procedure spExpandUserResultTemplate are provided in the table below.
Parameters and Temp Tables | Information |
---|---|
User-Supplied Result Set in Temporary Table, #UserData | Each row in #UserData is a set of template variable name and value pairs. Column name for #UserData table is automatically used as template variable name and column value is the template value for this template variable. |
User-Defined Template, @UserTemplate | @UserTemplate supplies the template string having variable names enclosed in curly braces. For example, "{TableName}" will be replaced by the actual table names returned in result set stored in #UserData. |
Output Parameter of Expanded Templates, @ConcatTemplates | @ConcatTemplates receives the end-result of ALL template expansions for all rows contained in #UserData. After executing the stored procedure, you can PRINT @ConcatTemplates to inspect the generated text for template expansions. Or you can excute sp_executesql call on the value of the @ConcatTemplates OUTPUT parameter. |
Quoting Character for Replacement Values: @DefaultLeftQuote, @DefaultRightQuote | Two parameters specify the characters to surround a variable's value after text replacement is done. Example: Place square brackets around the template values in the expanded template text. Default to '[' and ']'. |
Override Quoting Character for Specific Variable Name in Temporary Table, #OverrideVarValueQuote | If temporary table, #OverrideVarValueQuote, exists, then each row provides override quotes for specific template variable, instead of global default quotes in @DefaultLeftQuote and @DefaultRightQuote. |
More details about parameters and processing notes can be found in the comment header for the stored procedure spExpandUserResultTemplate.
Examples
The examples below provide more insight into how to invoke this stored procedure by examining the actual usage to produce SQL statements for different purposes. The expanded results have been shortened to show the first few rows in the result set generated by stored procedure.
Example - Get Row Counts - Table Names in Square Brackets
/************** Do Row Counts - Table Names in Square Brackets ***************/SET nocount ON; USE Northwind; GO --***** User-Supplied Result Set #UserData: IF OBJECT_ID(N'tempdb..#UserData', N'U') IS NOT NULL BEGIN DROP TABLE #UserData; END; SELECT [TABLE_CATALOG] AS DbName -- User's Template variable name. ,[TABLE_SCHEMA] AS SchemaName -- User's Template variable name. ,[TABLE_NAME] AS TableName -- User's Template variable name. INTO #UserData -- Save user data into required table for later use. FROM [INFORMATION_SCHEMA].[TABLES] WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY [TABLE_NAME];--***** User-Defined Template: DECLARE @UserTemplate nvarchar(4000) = N' -- Template # {_TemplateId} SELECT count(*) as "{_TemplateId}. {TableName} RowCount" FROM {DbName}.{SchemaName}.{TableName}; '; -- End of user's template string.
DECLARE @ConcatTemplates nvarchar(MAX); EXEC spExpandUserResultTemplate -- Process @UserTemplate for rows in #UserData. @UserTemplate = @UserTemplate, @ConcatTemplates = @ConcatTemplates OUTPUT; -- Result of All expanded templates.
PRINT @ConcatTemplates; -- Comment or UN-comment -- Caution PRINT limit is 4K!. PRINT '************************ Start Execution ********************************'; EXECUTE sp_executesql @ConcatTemplates; -- Comment or UN-comment as needed. DROP TABLE #UserData; -- Clean up temporary tables. IF OBJECT_ID(N'tempdb..#OverrideVarValueQuote', N'U') IS NOT NULL DROP TABLE #OverrideVarValueQuote; GO
The expanded results for the template:
-- Template # [1] SELECT count(*) as "[1]. [Employees] RowCount" FROM [Northwind].[dbo].[Employees]; -- Template # [2] SELECT count(*) as "[2]. [Categories] RowCount" FROM [Northwind].[dbo].[Categories]; -- Template # [3] SELECT count(*) as "[3]. [Customers] RowCount" FROM [Northwind].[dbo].[Customers]; -- Template # [4] SELECT count(*) as "[4]. [Shippers] RowCount" FROM [Northwind].[dbo].[Shippers]; ...
After execution by sp_executesql, the partial results are below.
************************ Start Execution ******************************** [1]. [Employees] RowCount ------------------------- 9 [2]. [Categories] RowCount -------------------------- 8 [3]. [Customers] RowCount ------------------------- 91 [4]. [Shippers] RowCount ------------------------ 3 ...
Example - Get Row Counts - Table Names Without Brackets
-- Keep everything same, but change procedure parameters for @DefaultLeftQuote
-- and @DefaultRightQuote to override default values.
SET nocount ON;
USE Northwind;
GO
--***** User-Supplied Result Set #UserData:
IF OBJECT_ID(N'tempdb..#UserData', N'U') IS NOT NULL BEGIN
DROP TABLE #UserData;
END;
SELECT [TABLE_CATALOG] AS DbName -- User's Template variable name.
,[TABLE_SCHEMA] AS SchemaName -- User's Template variable name.
,[TABLE_NAME] AS TableName -- User's Template variable name.
INTO #UserData -- Save user data into required table for later use.
FROM [INFORMATION_SCHEMA].[TABLES]
WHERE TABLE_TYPE = 'BASE TABLE'
ORDER BY [TABLE_NAME];
--***** User-Defined Template:
DECLARE @UserTemplate nvarchar(4000) = N'
-- Template # {_TemplateId}
SELECT count(*) as "{_TemplateId}. {TableName} RowCount"
FROM {DbName}.{SchemaName}.{TableName};
'; -- End of user's template string.
DECLARE @ConcatTemplates nvarchar(MAX);
EXEC spExpandUserResultTemplate -- Process @UserTemplate for rows in #UserData.
@UserTemplate = @UserTemplate,
@ConcatTemplates = @ConcatTemplates OUTPUT, -- Result of All expanded templates.
@DefaultLeftQuote = '', -- Suppress default of square brackets.
@DefaultRightQuote = ''; -- Use no quoting characters on all variables.
PRINT @ConcatTemplates; -- Comment or UN-comment -- Caution PRINT limit is 4K!.
-- PRINT '************************ Start Execution ********************************';
-- EXECUTE sp_executesql @ConcatTemplates; -- Comment or UN-comment as needed.
DROP TABLE #UserData; -- Clean up temporary tables.
IF OBJECT_ID(N'tempdb..#OverrideVarValueQuote', N'U') IS NOT NULL
DROP TABLE #OverrideVarValueQuote;
GO
The expanded results foir the template:
-- Template # 1
SELECT count(*) as "1. Employees RowCount"
FROM Northwind.dbo.Employees;
-- Template # 2
SELECT count(*) as "2. Categories RowCount"
FROM Northwind.dbo.Categories;
-- Template # 3
SELECT count(*) as "3. Customers RowCount"
FROM Northwind.dbo.Customers;
-- Template # 4
SELECT count(*) as "4. Shippers RowCount"
FROM Northwind.dbo.Shippers;
...
Example - Disable ALL CHECK constraints
-- To enable check constraints again, just change keyword NOCHECK CONSTRAINT
-- to CHECK CONSTRAINT in user-defined template @UserTemplate and run again.
SET nocount ON;
USE Northwind;
--***** User-Supplied Result Set #UserData:
IF OBJECT_ID(N'tempdb..#UserData', N'U') IS NOT NULL BEGIN
DROP TABLE #UserData;
END;
SELECT
OBJECT_SCHEMA_NAME(parent_object_id) AS SchemaName
,OBJECT_NAME(parent_object_id) AS TableName
,sc.name AS ColumnName
,ck.[name] AS ConstraintName
,ck.[type_desc] AS ConstraintType
INTO #UserData -- Save user data into required table for later use.
FROM [sys].[check_constraints] AS ck
INNER JOIN [sys].[columns] AS sc
on ck.[parent_column_id] = sc.column_id
and ck.parent_object_id = sc.object_id
ORDER BY TableName, ck.[parent_column_id]
--***** User-Defined Template:
DECLARE @UserTemplate nvarchar(4000) = N'
-- Template # {_TemplateId} - Disable check constraint, column {ColumnName}.
ALTER TABLE {SchemaName}.{TableName}
NOCHECK CONSTRAINT {ConstraintName};
'; -- End of user's template string.
DECLARE @ConcatTemplates nvarchar(MAX);
EXEC spExpandUserResultTemplate -- Process @UserTemplate for rows in #UserData.
@UserTemplate = @UserTemplate,
@ConcatTemplates = @ConcatTemplates OUTPUT -- Result of All expanded templates.
PRINT @ConcatTemplates; -- Comment or UN-comment -- Caution PRINT limit is 4K!.
PRINT '************************ Start Execution ********************************';
PRINT '**** Constraint changes done.';
EXECUTE sp_executesql @ConcatTemplates; -- Comment or UN-comment as needed.
DROP TABLE #UserData; -- Clean up temporary tables.
IF OBJECT_ID(N'tempdb..#OverrideVarValueQuote', N'U') IS NOT NULL
DROP TABLE #OverrideVarValueQuote;
PRINT '**** Check disable/enble for check constraints:';
SELECT
ck.[name] AS ConstraintName
,[is_disabled] AS IsConstraintDisabled
FROM [sys].[check_constraints] AS ck
The expanded results for the template:
-- Template # [1] - Disable check constraint, column [BirthDate]. ALTER TABLE [dbo].[Employees] NOCHECK CONSTRAINT [CK_Birthdate]; -- Template # [2] - Disable check constraint, column [UnitPrice]. ALTER TABLE [dbo].[Products] NOCHECK CONSTRAINT [CK_Products_UnitPrice]; -- Template # [3] - Disable check constraint, column [ReorderLevel]. ALTER TABLE [dbo].[Products] NOCHECK CONSTRAINT [CK_ReorderLevel]; -- Template # [4] - Disable check constraint, column [UnitsInStock]. ALTER TABLE [dbo].[Products] NOCHECK CONSTRAINT [CK_UnitsInStock]; ...
After execution by sp_executesql, the partial results are below.
************************ Start Execution ******************************** **** Constraint changes done. **** Check disable/enble for check constraints: ConstraintNameIsConstraintDisabled
-----------------------------------------------------------------
CK_Birthdate1
CK_Products_UnitPrice1
CK_ReorderLevel1
CK_UnitsInStock1
...
Example - Disable Check Constraints - Override default quotes for specific template variable
/**** Override quoting characters for template variable {ConstraintName}. ****/SET nocount ON;
USE Northwind;
--***** User-Supplied Result Set #UserData:
IF OBJECT_ID(N'tempdb..#UserData', N'U') IS NOT NULL BEGIN
DROP TABLE #UserData;
END;
SELECT
OBJECT_SCHEMA_NAME(parent_object_id) AS SchemaName
,OBJECT_NAME(parent_object_id) AS TableName
,sc.name AS ColumnName
,ck.[name] AS ConstraintName
,ck.[type_desc] AS ConstraintType
INTO #UserData -- Save user data into required table for later use.
FROM [sys].[check_constraints] AS ck
INNER JOIN [sys].[columns] AS sc
on ck.[parent_column_id] = sc.column_id
and ck.parent_object_id = sc.object_id
ORDER BY TableName, ck.[parent_column_id]
--***** User-Defined Template:
DECLARE @UserTemplate nvarchar(4000) = N'
-- Template # {_TemplateId} - Disable check constraint, column {ColumnName}.
ALTER TABLE {SchemaName}.{TableName}
NOCHECK CONSTRAINT {ConstraintName};
'; -- End of user's template string.
-- ***** Override Quoting Char for Specific Template Variable, {ConstraintName},
-- ***** instead of using global default quotes for variable's text replacement.
IF OBJECT_ID(N'tempdb..#OverrideVarValueQuote', N'U') IS NOT NULL
DROP TABLE #OverrideVarValueQuote;
CREATE TABLE #OverrideVarValueQuote (
OverrideVarName sysname NOT NULL PRIMARY KEY,
OverrideLeftQuote nvarchar(10) NOT NULL,
OverrideRightQuote nvarchar(10) NULL
);
INSERT INTO #OverrideVarValueQuote (OverrideVarName,
OverrideLeftQuote,
OverrideRightQuote)
VALUES
-- ('ConstraintName', N'', N'') -- Override default quotes for specific variable..
('ConstraintName', N'''', N'''') -- Specify single quotes in text replacement.
;
DECLARE @ConcatTemplates nvarchar(MAX);
EXEC spExpandUserResultTemplate -- Process @UserTemplate for rows in #UserData.
@UserTemplate = @UserTemplate,
@ConcatTemplates = @ConcatTemplates OUTPUT -- Result of All expanded templates.
PRINT @ConcatTemplates; -- Comment or UN-comment -- Caution PRINT limit is 4K!.
-- PRINT '************************ Start Execution ********************************';
-- PRINT '**** Constraint changes done.';
-- EXECUTE sp_executesql @ConcatTemplates; -- Comment or UN-comment as needed.
DROP TABLE #UserData; -- Clean up temporary tables.
IF OBJECT_ID(N'tempdb..#OverrideVarValueQuote', N'U') IS NOT NULL
DROP TABLE #OverrideVarValueQuote;
-- Template # [1] - Disable check constraint, column [BirthDate]. ALTER TABLE [dbo].[Employees] NOCHECK CONSTRAINT 'CK_Birthdate
'; -- Template # [2] - Disable check constraint, column [UnitPrice]. ALTER TABLE [dbo].[Products] NOCHECK CONSTRAINT 'CK_Products_UnitPrice
'; -- Template # [3] - Disable check constraint, column [ReorderLevel]. ALTER TABLE [dbo].[Products] NOCHECK CONSTRAINT 'CK_ReorderLevel
'; -- Template # [4] - Disable check constraint, column [UnitsInStock]. ALTER TABLE [dbo].[Products] NOCHECK CONSTRAINT 'CK_UnitsInStock
'; ...
Concluding Thoughts and Suggestions
Be sure to create the stored procedure, spExpandUserResultTemplate, in the correct database. Change the "USE Northwind" statement in the stored procedure script.
The internal template variable, {_TemplateId}, can be useful to track the PRINT-ed SQL statements and the results of the executed SQL statements.
Be creative in how you build the result set in temporary table #UserData. INFORMATION_SCHEMA views or catalog views are handy sources of metadata.
The EXAMPLEs show template expansions for SQL statements. Using the @UserTemplate parameter to hold a generalized text template (non-SQL) would be an interesting to explore.