Menu
Want me to draw something for you? Click here!
ReverentGeek

Stupid SQL Tricks - Using Computed Columns for Code Generation

November 4, 2011

I recently had a need to drop and recreate all the default constraints on some tables so I could convert the data types of from varchar to nvarchar. The constraints were originally created allowing SQL Server to automatically name them. So, they looked something like: DF__TableName__Colu__2BE6BFCF. Maybe this doesn't bother you. To me, it's an abomination.

SET NOCOUNT ON;

DECLARE @sql nvarchar(MAX), @executeScriptsImmediately bit, @id int;

SET @executeScriptsImmediately = 0; -- Set this to 1 to immediately drop and recreate constraints

DECLARE @tbl TABLE ( Id int IDENTITY(1,1), TableName nvarchar(128) NOT NULL, ColumnName nvarchar(128) NOT NULL,
 DefaultName nvarchar(128) NOT NULL, DefaultDefinition nvarchar(MAX),
 NewDefaultName AS ( N'DF_' + TableName + N'_' + ColumnName ),
 DropText AS (N'IF EXISTS ( SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N''[' + DefaultName + N']'') AND type = ''D'' )
BEGIN
 ALTER TABLE [dbo].[' + TableName + N'] DROP CONSTRAINT [' + DefaultName + N']
END'),
 CreateText AS (N'IF NOT EXISTS ( SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N''[DF_' + TableName + N'_' + ColumnName + N']'') AND type = ''D'' )
BEGIN
 ALTER TABLE [dbo].[' + TableName + N'] ADD CONSTRAINT [DF_' + TableName + N'_' + ColumnName + N'] DEFAULT ' + DefaultDefinition + N' FOR [' + ColumnName + N']
END'),
 OrigCreateText AS (N'IF NOT EXISTS ( SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N''[' + DefaultName + N']'') AND type = ''D'' )
BEGIN
 ALTER TABLE [dbo].[' + TableName + N'] ADD CONSTRAINT [' + DefaultName + N'] DEFAULT ' + DefaultDefinition + N' FOR [' + ColumnName + N']
END')
 );

INSERT INTO @tbl ( TableName, ColumnName, DefaultName, DefaultDefinition )
SELECT t.name, c.name, dc.name, dc.definition
FROM sys.columns c
  JOIN sys.tables t ON t.object_id = c.object_id
  JOIN sys.objects d ON d.object_id = c.default_object_id
  JOIN sys.types tp ON tp.system_type_id = c.system_type_id
  JOIN sys.default_constraints dc ON dc.object_id = d.object_id
WHERE d.type = N'd'
-- This filters query to only defaults on varchar data types
--  AND tp.name = N'varchar'
-- This filters query to only defaults that do not match the naming convention
  AND dc.name <> N'DF_' + t.name + N'_' + c.name
ORDER BY t.name, c.name

PRINT N'-------------------------------------------';
PRINT N'-- Backup of original default contraints';
PRINT N'-------------------------------------------';
PRINT N'/*';
SET @id = ( SELECT TOP 1 Id FROM @tbl ORDER BY Id );
WHILE ( @id IS NOT NULL )
BEGIN
 SELECT @sql = OrigCreateText FROM @tbl WHERE Id = @id;
 PRINT @sql;
 SET @id = ( SELECT TOP 1 Id FROM @tbl WHERE Id > @id ORDER BY Id );
END
PRINT N'*/';
PRINT N'';

PRINT N'-------------------------------------------';
PRINT N'-- Drop default contraints';
PRINT N'-------------------------------------------';
SET @id = ( SELECT TOP 1 Id FROM @tbl ORDER BY Id );
WHILE ( @id IS NOT NULL )
BEGIN
 SELECT @sql = DropText FROM @tbl WHERE Id = @id;
 PRINT @sql;
 IF (@executeScriptsImmediately = 1) EXEC sp_executesql @sql;
 SET @id = ( SELECT TOP 1 Id FROM @tbl WHERE Id > @id ORDER BY Id );
END
PRINT N'';

PRINT N'-------------------------------------------';
PRINT N'-- Create new default contraints';
PRINT N'-------------------------------------------';
SET @id = ( SELECT TOP 1 Id FROM @tbl ORDER BY Id );
WHILE ( @id IS NOT NULL )
BEGIN
 SELECT @sql = CreateText FROM @tbl WHERE Id = @id;
 PRINT @sql;
 IF (@executeScriptsImmediately = 1) EXEC sp_executesql @sql;
 SET @id = ( SELECT TOP 1 Id FROM @tbl WHERE Id > @id ORDER BY Id );
END
PRINT N'';

View and download this script from GitHub.

Granted, you could accomplish the same thing by moving the "templates" to the SELECT statement that inserts rows into the the table variable. I just felt especially nerdy using computed columns. I like being especially nerdy.