====== Fun with column metadata ======
{{tag> tsql dynamic}}
===== Metadata =====
DECLARE @tableName NVARCHAR(260) = 'myTable';
DROP TABLE IF EXISTS #columns;
SELECT
OBJECT_SCHEMA_NAME(o.[object_id]) AS TableSchema
, o.[name] AS TableName
, c.[name] AS ColumnName
, c.column_id AS ColumnOrdinal
, t.[name] AS TypeName
, c.max_length AS [MaxLength]
, c.scale
, c.[precision]
, c.is_nullable
, IIF(ic.index_column_id IS NULL, 0, 1) AS IsPkColumn
, ic.index_column_id AS PkOrdinal
, UPPER(t.[name])
+ COALESCE('(' +
CAST(
CASE
WHEN t.[name] LIKE '%char' AND c.max_length = -1 THEN 'MAX'
WHEN t.[name] LIKE 'n%char' THEN CAST(c.max_length/2 AS VARCHAR)
WHEN t.[name] LIKE '%char' THEN CAST(c.max_length AS VARCHAR)
WHEN t.[name] LIKE '%binary' THEN CAST(c.max_length AS VARCHAR)
END AS VARCHAR) + ')', '') AS SqlType
, CASE
WHEN dc.[definition] LIKE '((%))' THEN SUBSTRING(dc.[definition], 3, LEN(dc.[definition]) - 4)
WHEN dc.[definition] LIKE '(%)' THEN SUBSTRING(dc.[definition], 2, LEN(dc.[definition]) - 2)
ELSE dc.[definition]
END AS DefaultValue
INTO #columns
FROM sys.objects o
INNER JOIN sys.columns c ON c.[object_id] = o.[object_id]
INNER JOIN sys.types t
ON t.system_type_id = c.system_type_id
AND t.system_type_id = t.user_type_id
LEFT JOIN sys.indexes i
ON i.[object_id] = o.[object_id]
AND i.is_primary_key = 1
LEFT JOIN sys.index_columns ic
ON ic.[object_id] = i.[object_id]
AND ic.index_id = i.index_id
AND ic.column_id = c.column_id
LEFT JOIN sys.default_constraints dc
ON dc.parent_object_id = o.[object_id]
AND dc.parent_column_id = c.column_id
WHERE o.[object_id] = OBJECT_ID(@tableName)
ORDER BY ColumnOrdinal;
SELECT * FROM #columns;
===== Temporary tables =====
with cols AS (
SELECT
[name] AS ColumnName
FROM tempdb.sys.columns
WHERE [object_id] = OBJECT_ID('tempdb..#tmp')
)
select *
into #columns
from cols
This works in Azure SQL DB too!
===== Table diff =====
UPDATE #columns
SET PkOrdinal = COALESCE(PkOrdinal, 5000)
DECLARE @joinCols NVARCHAR(MAX) = ''
DECLARE @selectCols NVARCHAR(MAX) = ''
DECLARE @orderCols NVARCHAR(MAX) = ''
DECLARE @sql NVARCHAR(MAX) = ''
SELECT
@joinCols += '
AND (old.' + QUOTENAME(ColumnName) + ' = new.' + QUOTENAME(ColumnName) + ' OR old.' + QUOTENAME(ColumnName) + ' IS NULL AND new.' + QUOTENAME(ColumnName) + ' IS NULL)'
, @selectCols += '
, COALESCE(old.' + QUOTENAME(ColumnName) + ', new.' + QUOTENAME(ColumnName) + ') AS ' + QUOTENAME(ColumnName)
, @orderCols += '
, COALESCE(old.' + QUOTENAME(ColumnName) + ', new.' + QUOTENAME(ColumnName) + ')'
FROM #columns
ORDER BY PkOrdinal
SELECT TOP 1
@sql = 'WITH old AS (
SELECT
''OLD'' AS Version
, *
FROM oldTable
), new AS (
SELECT
''NEW'' AS Version
, *
FROM ' + QUOTENAME(TableSchema) + '.' + QUOTENAME(TableName) + '
)
SELECT
COALESCE(old.[Version], new.[Version]) AS [Version]' + @selectCols + '
FROM old
FULL OUTER JOIN new
ON 1 = 1' + @joinCols + '
WHERE old.[Version] IS NULL
OR new.[Version] IS NULL
ORDER BY
' + SUBSTRING(@orderCols, 5, LEN(@orderCols))
FROM #columns
PRINT @sql
If the resulting SQL statement is large, you may need to [[tsql:print_big]].