-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCompareTablesByMetadata.dsql
More file actions
291 lines (270 loc) · 14.6 KB
/
CompareTablesByMetadata.dsql
File metadata and controls
291 lines (270 loc) · 14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
IF OBJECT_ID ('dbo.CompareTablesMetadata', 'P') IS NOT NULL DROP PROCEDURE dbo.CompareTablesMetadata
GO
CREATE PROC [dbo].[CompareTablesMetadata] @New_Table_Name [VARCHAR](130),@Main_Table_Name [VARCHAR](130),@ComparisonResult [VARCHAR](MAX) OUT AS
/*
IF OBJECT_ID ('dbo.CompareTablesMetadata', 'P') IS NOT NULL DROP PROCEDURE dbo.CompareTablesMetadata
GO
###################################################################################################################
Example:
-------------------------------------------------------------------------------------------------------------------
DECLARE @ComparisonResult VARCHAR(MAX) = 'ByName'
EXEC dbo.CompareTablesMetadata 'dbo.Fact_Table', 'dbo.Fact_Table_New', @ComparisonResult OUTPUT
===================================================================================================================
Proc Description:
-------------------------------------------------------------------------------------------------------------------
This proc comparing 2 tables and returning result of comparison to the string. If tables identical (but not one table) - returning empty string
@New_Table_Name - Name of the one table (not Null)
@Main_Table_Name - Name of the enother table (not Null)
@ComparisonResult - Text description of diferencies found. If no Differencies returns empty String.
- It can also be input parameter. Parameters can be NoPrint, No[] or ByName - it means comparison will be based on names, not Order
###################################################################################################################
*/
BEGIN
/*====================================== TESTING =======================================================================*/
--DECLARE @New_Table_Name VARCHAR(200) = 'dbo.Fact_Table', @Main_Table_Name VARCHAR(130) = 'dbo.Fact_Table_New', @ComparisonResult VARCHAR(MAX) = 'ByName'
/*====================================== TESTING =======================================================================*/
DECLARE @Error VARCHAR(MAX) = '', @Params VARCHAR(100) = ISNULL(@ComparisonResult,'')
SET @ComparisonResult = ''
IF @Main_Table_Name IS NULL SET @Error = @Error + 'Main Table name cannot be NULL!' + CHAR(13)
IF @New_Table_Name IS NULL SET @Error = @Error + 'New Table name cannot be NULL!' + CHAR(13)
IF @Main_Table_Name = @New_Table_Name SET @Error = @Error + 'Don not compare the table with itself!'
IF LEN(@Error) > 0
BEGIN
PRINT @Error
SET @ComparisonResult = @Error
END
ELSE
BEGIN
DECLARE @Main_Schema VARCHAR(100), @Main_Table VARCHAR(200), @New_Schema VARCHAR(100), @New_Table VARCHAR(200)
DECLARE @sql VARCHAR(MAX), @ByNames BIT = 0
DECLARE @Dot INT = CHARINDEX('.',@Main_Table_Name)
SELECT
@Main_Schema = CASE WHEN @Dot = 0 THEN 'dbo' ELSE REPLACE(REPLACE(REPLACE(LEFT(@Main_Table_Name,@Dot),'[',''),']',''),'.','') END,
@Main_Table = CASE WHEN @Dot = 0 THEN REPLACE(REPLACE(@Main_Table_Name,'[',''),']','') ELSE REPLACE(REPLACE(SUBSTRING(@Main_Table_Name,@Dot + 1,200),'[',''),']','') END
IF (@New_Table_Name IS NULL) OR (LEN(@New_Table_Name) = 0)
SET @New_Table_Name = 'New.' + @Main_Table
SET @Dot = CHARINDEX('.',@New_Table_Name)
SELECT
@New_Schema = CASE WHEN @Dot = 0 THEN 'New' ELSE REPLACE(REPLACE(REPLACE(LEFT(@New_Table_Name,@Dot),'[',''),']',''),'.','') END,
@New_Table = CASE WHEN @Dot = 0 THEN REPLACE(REPLACE(@New_Table_Name,'[',''),']','') ELSE REPLACE(REPLACE(SUBSTRING(@New_Table_Name,@Dot + 1,200),'[',''),']','') END
IF CHARINDEX('ByName',@Params) > 0
SET @ByNames = 1
;WITH CTE_New AS
(
SELECT
CAST(I.type_desc AS VARCHAR(22)) COLLATE Latin1_General_100_CI_AS_KS_WS AS IndexType
, CAST(ISNULL(u.name,'-1') AS VARCHAR(100)) AS IndexColName
, CAST(CASE WHEN C.is_descending_key = 1 THEN ' DESC' ELSE ' ASC' END AS VARCHAR(5)) AS IndexColOrder
, CAST(pu.name AS VARCHAR(100)) AS PartitionColumn
, CAST(p.distribution_policy_desc + CASE WHEN ISNULL(Dc.name, '') != '' THEN '(' + Dc.name + ')' ELSE '' END AS VARCHAR(100)) AS TableDistribution
, ROW_NUMBER() OVER (ORDER BY C.key_ordinal) AS RN
FROM sys.Tables as t
JOIN sys.schemas s ON t.schema_id = s.schema_id
JOIN sys.indexes AS I ON I.object_id = t.object_id AND I.index_id <= 1
LEFT JOIN sys.index_columns AS C ON C.object_id = t.object_id AND C.index_id = I.index_id AND C.key_ordinal > 0
LEFT JOIN sys.columns AS u ON u.column_id = C.column_id AND u.object_id = t.object_id
LEFT JOIN sys.partition_schemes AS ps ON ps.data_space_id = i.data_space_id
LEFT JOIN sys.index_columns AS pc ON pc.[object_id] = i.[object_id] AND pc.index_id = i.index_id AND pc.partition_ordinal >= 1 -- because 0 = non-partitioning column
LEFT JOIN sys.columns AS pu ON t.[object_id] = pu.[object_id] AND pc.column_id = pu.column_id
LEFT JOIN sys.pdw_Table_distribution_properties p ON p.[object_id] = t.[object_id]
LEFT JOIN sys.pdw_column_distribution_properties cd ON cd.object_id = t.object_id AND cd.distribution_ordinal = 1
LEFT JOIN sys.columns Dc ON Dc.object_id = t.object_id AND Dc.column_id = cd.column_id
WHERE s.name = @New_Schema AND t.name = @New_Table
)
, CTE_NewParam AS
(
SELECT
CTE1.PartitionColumn, CTE1.TableDistribution,
CTE1.IndexType +
CASE WHEN CTE1.IndexType IN ('CLUSTERED COLUMNSTORE','NONCLUSTERED COLUMNSTORE','HEAP')
THEN ''
ELSE '(' + CTE1.IndexColName + CTE1.IndexColOrder
+ ISNULL(','+ CTE2.IndexColName + CTE2.IndexColOrder, '')
+ ISNULL(','+ CTE3.IndexColName + CTE3.IndexColOrder, '')
+ ISNULL(','+ CTE4.IndexColName + CTE4.IndexColOrder, '')
+ ISNULL(','+ CTE5.IndexColName + CTE5.IndexColOrder, '')
+ ISNULL(','+ CTE6.IndexColName + CTE6.IndexColOrder, '')
+ ISNULL(','+ CTE7.IndexColName + CTE7.IndexColOrder, '')
+ ISNULL(','+ CTE8.IndexColName + CTE8.IndexColOrder, '')
+ ISNULL(','+ CTE9.IndexColName + CTE9.IndexColOrder, '')
+ ISNULL(','+ CTE10.IndexColName +CTE10.IndexColOrder, '') + ')'
END AS TableIndex
FROM CTE_New AS CTE1
LEFT JOIN CTE_New AS CTE2 ON CTE2.RN = 2
LEFT JOIN CTE_New AS CTE3 ON CTE3.RN = 3
LEFT JOIN CTE_New AS CTE4 ON CTE4.RN = 4
LEFT JOIN CTE_New AS CTE5 ON CTE5.RN = 5
LEFT JOIN CTE_New AS CTE6 ON CTE6.RN = 6
LEFT JOIN CTE_New AS CTE7 ON CTE7.RN = 7
LEFT JOIN CTE_New AS CTE8 ON CTE8.RN = 8
LEFT JOIN CTE_New AS CTE9 ON CTE9.RN = 9
LEFT JOIN CTE_New AS CTE10 ON CTE10.RN = 10
WHERE CTE1.RN = 1
)
, CTE_Main AS
(
SELECT
CAST(I.type_desc AS VARCHAR(22)) COLLATE Latin1_General_100_CI_AS_KS_WS AS IndexType
, CAST(ISNULL(u.name,'-1') AS VARCHAR(100)) AS IndexColName
, CAST(CASE WHEN C.is_descending_key = 1 THEN ' DESC' ELSE ' ASC' END AS VARCHAR(5)) AS IndexColOrder
, CAST(pu.name AS VARCHAR(100)) AS PartitionColumn
, CAST(p.distribution_policy_desc + CASE WHEN ISNULL(Dc.name, '') != '' THEN '(' + Dc.name + ')' ELSE '' END AS VARCHAR(100)) AS TableDistribution
, ROW_NUMBER() OVER (ORDER BY C.key_ordinal) AS RN
FROM sys.Tables as t
JOIN sys.schemas s ON t.schema_id = s.schema_id
JOIN sys.indexes AS I ON I.object_id = t.object_id AND I.index_id <= 1
LEFT JOIN sys.index_columns AS C ON C.object_id = t.object_id AND C.index_id = I.index_id AND C.key_ordinal > 0
LEFT JOIN sys.columns AS u ON u.column_id = C.column_id AND u.object_id = t.object_id
LEFT JOIN sys.partition_schemes AS ps ON ps.data_space_id = i.data_space_id
LEFT JOIN sys.index_columns AS pc ON pc.[object_id] = i.[object_id] AND pc.index_id = i.index_id AND pc.partition_ordinal >= 1 -- because 0 = non-partitioning column
LEFT JOIN sys.columns AS pu ON t.[object_id] = pu.[object_id] AND pc.column_id = pu.column_id
LEFT JOIN sys.pdw_Table_distribution_properties p ON p.[object_id] = t.[object_id]
LEFT JOIN sys.pdw_column_distribution_properties cd ON cd.object_id = t.object_id AND cd.distribution_ordinal = 1
LEFT JOIN sys.columns Dc ON Dc.object_id = t.object_id AND Dc.column_id = cd.column_id
WHERE s.name = @Main_Schema AND t.name = @Main_Table
)
, CTE_MainParam AS
(
SELECT
CTE1.PartitionColumn, CTE1.TableDistribution,
CTE1.IndexType +
CASE WHEN CTE1.IndexType IN ('CLUSTERED COLUMNSTORE','NONCLUSTERED COLUMNSTORE','HEAP')
THEN ''
ELSE '(' + CTE1.IndexColName + CTE1.IndexColOrder
+ ISNULL(','+ CTE2.IndexColName + CTE2.IndexColOrder, '')
+ ISNULL(','+ CTE3.IndexColName + CTE3.IndexColOrder, '')
+ ISNULL(','+ CTE4.IndexColName + CTE4.IndexColOrder, '')
+ ISNULL(','+ CTE5.IndexColName + CTE5.IndexColOrder, '')
+ ISNULL(','+ CTE6.IndexColName + CTE6.IndexColOrder, '')
+ ISNULL(','+ CTE7.IndexColName + CTE7.IndexColOrder, '')
+ ISNULL(','+ CTE8.IndexColName + CTE8.IndexColOrder, '')
+ ISNULL(','+ CTE9.IndexColName + CTE9.IndexColOrder, '')
+ ISNULL(','+ CTE10.IndexColName +CTE10.IndexColOrder, '') + ')'
END AS TableIndex
FROM CTE_Main AS CTE1
LEFT JOIN CTE_Main AS CTE2 ON CTE2.RN = 2
LEFT JOIN CTE_Main AS CTE3 ON CTE3.RN = 3
LEFT JOIN CTE_Main AS CTE4 ON CTE4.RN = 4
LEFT JOIN CTE_Main AS CTE5 ON CTE5.RN = 5
LEFT JOIN CTE_Main AS CTE6 ON CTE6.RN = 6
LEFT JOIN CTE_Main AS CTE7 ON CTE7.RN = 7
LEFT JOIN CTE_Main AS CTE8 ON CTE8.RN = 8
LEFT JOIN CTE_Main AS CTE9 ON CTE9.RN = 9
LEFT JOIN CTE_Main AS CTE10 ON CTE10.RN = 10
WHERE CTE1.RN = 1
)
SELECT
@ComparisonResult = CASE WHEN M.PartitionColumn <> N.PartitionColumn THEN 'Partition: ' + M.PartitionColumn + ' <> ' + N.PartitionColumn + '; ' + CHAR(13) ELSE '' END
+ CASE WHEN M.TableDistribution <> N.TableDistribution THEN 'Distribution: ' + M.TableDistribution + ' <> ' + N.TableDistribution + '; ' + CHAR(13) ELSE '' END
+ CASE WHEN M.TableIndex <> N.TableIndex THEN 'Index: ' + M.TableIndex + ' <> ' + N.TableIndex + ';' + CHAR(13) ELSE '' END
FROM CTE_MainParam M
JOIN CTE_NewParam N ON NOT (N.PartitionColumn = M.PartitionColumn AND N.TableDistribution = M.TableDistribution AND N.TableIndex = M.TableIndex)
IF OBJECT_ID('tempdb..#TableColums') IS NOT NULL DROP Table #TableColums;
CREATE Table #TableColums WITH (HEAP, DISTRIBUTION = Replicate) AS
WITH CTE_New AS
(
SELECT c.name AS ColumnName, C.column_id, TYPE_NAME(c.system_type_id) AS ColumnType, c.max_length, c.PRECISION,c.scale, C.is_nullable,collation_name,
ROW_NUMBER() OVER(ORDER BY C.column_id) AS Column_Number
FROM sys.columns c
JOIN sys.Tables t ON c.object_id = t.object_id
JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE s.name = @New_Schema AND t.name = @New_Table
)
, CTE_ColumnNew AS
(
SELECT
CASE WHEN @ByNames = 1 THEN ColumnName ELSE CAST(Column_Number AS VARCHAR(3)) END AS Identifier,
CASE WHEN @ByNames = 1 THEN '' ELSE '[' + ColumnName + '] ' END + ColumnType + -- When we compare by name - name as alredy there and we don't need to add it to Description
CASE
WHEN ColumnType = 'DATETIME2' THEN '(' + CAST(scale AS VARCHAR(1)) +')'
WHEN ColumnType IN ('BINARY','VARBINARY') THEN '(' + ISNULL(NULLIF(CAST(max_length AS VARCHAR(4)),'-1'),'MAX') +')'
WHEN ColumnType IN ('DECIMAL','NUMERIC') THEN '(' + CAST(PRECISION AS VARCHAR(2)) + ', ' + CAST(scale AS VARCHAR(2)) +')'
WHEN ColumnType LIKE '%CHAR' AND LEFT(ColumnType,1) = 'N' THEN '(' + ISNULL(CAST(NULLIF(max_length, -1) / 2 AS VARCHAR(4)),'MAX') +') COLLATE ' + collation_name
WHEN ColumnType LIKE '%CHAR' AND LEFT(ColumnType,1) != 'N' THEN '(' + ISNULL(NULLIF(CAST(max_length AS VARCHAR(4)),'-1'),'MAX') +') COLLATE ' + collation_name
ELSE ''
END +
CASE
WHEN is_nullable = 1 THEN ' NULL'
ELSE ' NOT NULL'
END AS ColumnDesc
FROM CTE_New M
)
, CTE_Main AS
(
SELECT c.name AS ColumnName, C.column_id, TYPE_NAME(c.system_type_id) AS ColumnType, c.max_length, c.PRECISION ,c.scale, C.is_nullable,collation_name,
ROW_NUMBER() OVER(ORDER BY C.column_id) AS Column_Number
FROM sys.columns c
JOIN sys.Tables t ON c.object_id = t.object_id
JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE s.name = @Main_Schema AND t.name = @Main_Table
)
, CTE_ColumnMain AS
(
SELECT
CASE WHEN @ByNames = 1 THEN ColumnName ELSE CAST(Column_Number AS VARCHAR(3)) END AS Identifier,
CASE WHEN @ByNames = 1 THEN '' ELSE '[' + ColumnName + '] ' END + ColumnType +
CASE
WHEN ColumnType = 'DATETIME2' THEN '(' + CAST(scale AS VARCHAR(1)) +')'
WHEN ColumnType IN ('BINARY','VARBINARY') THEN '(' + ISNULL(NULLIF(CAST(max_length AS VARCHAR(4)),'-1'),'MAX') +')'
WHEN ColumnType IN ('DECIMAL','NUMERIC') THEN '(' + CAST(PRECISION AS VARCHAR(2)) + ', ' + CAST(scale AS VARCHAR(2)) +')'
WHEN ColumnType LIKE '%CHAR' AND LEFT(ColumnType,1) = 'N' THEN '(' + ISNULL(CAST(NULLIF(max_length, -1) / 2 AS VARCHAR(4)),'MAX') +') COLLATE ' + collation_name
WHEN ColumnType LIKE '%CHAR' AND LEFT(ColumnType,1) != 'N' THEN '(' + ISNULL(NULLIF(CAST(max_length AS VARCHAR(4)),'-1'),'MAX') +') COLLATE ' + collation_name
ELSE ''
END +
CASE
WHEN is_nullable = 1 THEN ' NULL'
ELSE ' NOT NULL'
END AS ColumnDesc
FROM CTE_Main M
)
, CTE_Difference AS
(
SELECT
ISNULL(M.Identifier,N.Identifier) AS Identifier,
ISNULL(N.ColumnDesc,'') AS NewDesc,
ISNULL(M.ColumnDesc,'') AS MainDesc,
CASE WHEN N.ColumnDesc = M.ColumnDesc THEN 1 ELSE 0 END AS EqualFlag
FROM CTE_ColumnMain AS M
FULL OUTER JOIN CTE_ColumnNew AS N ON M.Identifier = N.Identifier
)
SELECT
Identifier,
MainDesc AS OldType,
NewDesc AS NewType,
CASE
WHEN NewDesc = '' THEN 'Deleted'
WHEN MainDesc = '' THEN 'Added'
ELSE 'Changed'
END AS [Action],
'Column ' + Identifier + ': ' +
CASE
WHEN NewDesc = '' THEN MainDesc + ' DELETED;' + CHAR(13)
WHEN MainDesc = '' THEN NewDesc + ' ADDED;' + CHAR(13)
ELSE MainDesc + ' -=> ' + NewDesc + ';' + CHAR(13)
END AS ColumnDiff
, ROW_NUMBER() OVER(ORDER BY Identifier) AS RN
FROM CTE_Difference
WHERE EqualFlag = 0
SELECT Identifier,OldType,NewType,[Action] FROM #TableColums-- this needed for Developer to copy to Excel
ORDER BY RN
DECLARE @Indicat SMALLINT = 1, @ColumnDiff VARCHAR(200) = '', @NunOfColumns INT
SELECT @NunOfColumns = MAX(RN) FROM #TableColums
WHILE (@Indicat <= @NunOfColumns)
BEGIN
SELECT @ColumnDiff = ColumnDiff
FROM #TableColums M
WHERE M.RN = @Indicat
SET @ComparisonResult = @ComparisonResult + @ColumnDiff
SET @Indicat += 1
END
IF CHARINDEX('No[]',@Params) > 0
SET @ComparisonResult = REPLACE(REPLACE(@ComparisonResult,'[',''),']','')
IF CHARINDEX('NoPrint',@Params) = 0
BEGIN
IF @ComparisonResult = ''
PRINT 'Tables identical'
ELSE
EXEC dbo.LongPrint @ComparisonResult
END
END
END