|
| 1 | +/********************************************************************* |
| 2 | +Scott Peters |
| 3 | +Full Outer Join Create Dynamic SQL |
| 4 | +https://advancedsqlpuzzles.com |
| 5 | +Last Updated: 10/06/2022 |
| 6 | +
|
| 7 | +This script is written in Microsoft SQL Server's T-SQL |
| 8 | +
|
| 9 | +See full instructions in PDF format at the following GitHub repository: |
| 10 | +https://github.com/smpetersgithub/AdvancedSQLPuzzles/tree/main/Database%20Tips%20and%20Tricks/Table%20Validation |
| 11 | +
|
| 12 | +**********************************************************************/ |
| 13 | +SET NOCOUNT ON |
| 14 | + |
| 15 | +---------------------------------------- |
| 16 | +---------------------------------------- |
| 17 | +--Tables are created in this order, which I have defined as levels |
| 18 | +--The levels are used in the creation of the dynamic SQL for sorting |
| 19 | +DROP TABLE IF EXISTS #Select;-------------Level 100 |
| 20 | +DROP TABLE IF EXISTS #Exists;-------------Level 200 |
| 21 | +DROP TABLE IF EXISTS #RowNumber;----------Level 300 |
| 22 | +DROP TABLE IF EXISTS #Count;--------------Level 400 |
| 23 | +DROP TABLE IF EXISTS #Distinct_Count;-----Level 500 |
| 24 | +DROP TABLE IF EXISTS #Compare;------------Level 600 |
| 25 | +DROP TABLE IF EXISTS #Columns;------------Level 600 (Set the QueryOrder to the same value as #Compare to project the _Compare field next to the _Table1 and _Table2 fields) |
| 26 | +DROP TABLE IF EXISTS #Into;---------------Level 700 |
| 27 | +DROP TABLE IF EXISTS #From;---------------Level 800 |
| 28 | +DROP TABLE IF EXISTS #FullOuterJoin;------Level 900 |
| 29 | +DROP TABLE IF EXISTS #SQLStatementTemp;-------Used to create dynamic SQL statment |
| 30 | +DROP TABLE IF EXISTS #SQLStatementFinal;--Used to create dynamic SQL statment |
| 31 | +GO |
| 32 | + |
| 33 | +---------------------------------------- |
| 34 | +---------------------------------------- |
| 35 | +--Set this value!! |
| 36 | +--It is used to query the ##TableInformation table |
| 37 | +DECLARE @LookupID AS INTEGER = 1;-----------------Set this value!!!!!! |
| 38 | + |
| 39 | +------------------------------------------- |
| 40 | +------------------------------------------- |
| 41 | +--Set variables from the ##TableInformation table |
| 42 | +DECLARE @MySQLStatement AS VARCHAR(MAX); |
| 43 | +DECLARE @TempTable AS VARCHAR(MAX) = (SELECT CONCAT('##',Table1Name,'_TemporaryTable') FROM ##TableInformation WHERE LookupID = @LookupID); |
| 44 | +DECLARE @DropTable AS VARCHAR(MAX) = CONCAT('DROP TABLE IF EXISTS ',@TempTable); |
| 45 | +DECLARE @Table1 AS VARCHAR(MAX) = (SELECT Table1Name FROM ##TableInformation WHERE LookupID = @LookupID); |
| 46 | +DECLARE @Table2 AS VARCHAR(MAX) = (SELECT Table2Name FROM ##TableInformation WHERE LookupID = @LookupID); |
| 47 | +DECLARE @Schema1 AS VARCHAR(MAX) = (SELECT Schema1Name FROM ##TableInformation WHERE LookupID = @LookupID); |
| 48 | +DECLARE @Schema2 AS VARCHAR(MAX) = (SELECT Schema2Name FROM ##TableInformation WHERE LookupID = @LookupID); |
| 49 | +DECLARE @Exists1 AS VARCHAR(MAX) = (SELECT Exists1 FROM ##TableInformation WHERE LookupID = @LookupID); |
| 50 | +DECLARE @Exists2 AS VARCHAR(MAX) = (SELECT Exists2 FROM ##TableInformation WHERE LookupID = @LookupID); |
| 51 | +DECLARE @JoinSyntax AS VARCHAR(MAX) = (SELECT CONCAT(Exists1, ' = ', Exists2) FROM ##TableInformation WHERE LookupID = @LookupID); |
| 52 | +DECLARE @RowNumberSyntax AS VARCHAR(MAX) = (SELECT Exists1 FROM ##TableInformation WHERE LookupID = @LookupID); |
| 53 | + |
| 54 | +--Drop the temporary table |
| 55 | +EXECUTE(@DropTable); |
| 56 | + |
| 57 | +----------------------------- |
| 58 | +----------------------------- |
| 59 | +--Level_100 |
| 60 | +--SELECT statement |
| 61 | +SELECT 100 AS QueryOrder |
| 62 | + ,'SELECT ''Start Compare-->'' AS CompareStart' AS SQLStatement |
| 63 | +INTO #Select |
| 64 | +UNION ALL |
| 65 | +SELECT 101, CONCAT(',''',@Schema1, ',', @Table1, ''' AS TableName1') |
| 66 | +UNION ALL |
| 67 | +SELECT 102, CONCAT(',''',@Schema1, ',', @Table2, ''' AS TableName2') |
| 68 | +UNION ALL |
| 69 | +SELECT 103, CONCAT(',''',@@SERVERNAME, ''' AS ServerName') |
| 70 | +UNION ALL |
| 71 | +SELECT 104, CONCAT(',''',@Exists1, ''' AS JoinSyntax_Table1') |
| 72 | +UNION ALL |
| 73 | +SELECT 105, CONCAT(',''',@Exists2, ''' AS JoinSyntax_Table2'); |
| 74 | + |
| 75 | +----------------------------- |
| 76 | +----------------------------- |
| 77 | +--Level 200 |
| 78 | +--Exists |
| 79 | +SELECT 200 AS QueryOrder |
| 80 | + ,CONCAT(',CASE WHEN ', @Exists1, ' = '''' THEN 1 ELSE 0 END AS NotExists_Table1') AS SQLStatement |
| 81 | +INTO #Exists |
| 82 | +UNION ALL |
| 83 | +SELECT 210 AS QueryOrder |
| 84 | + ,CONCAT(',CASE WHEN ', @Exists2, ' = '''' THEN 1 ELSE 0 END AS NotExists_Table2') AS SQLStatement |
| 85 | + |
| 86 | +----------------------------- |
| 87 | +----------------------------- |
| 88 | +--Level_300 |
| 89 | +--RowNumber_Table1 (Find Duplicates) |
| 90 | +SELECT 300 AS QueryOrder |
| 91 | + ,CONCAT(',ROW_NUMBER() OVER (PARTITION BY ', @RowNumberSyntax, ' ORDER BY ',@RowNumberSyntax,') AS RowNumber_Table1') AS SQLStatement |
| 92 | +INTO #RowNumber |
| 93 | + |
| 94 | +----------------------------- |
| 95 | +----------------------------- |
| 96 | +--Level 400 |
| 97 | +--Count_Table1, Count_Table2 |
| 98 | +SELECT 400 AS QueryOrder |
| 99 | + ,CONCAT(',', '(SELECT COUNT(*) FROM ',CONCAT(@Schema1,'.',@Table1),') AS Count_Table1') AS SQLStatement |
| 100 | +INTO #Count |
| 101 | +UNION ALL |
| 102 | +SELECT 410 AS QueryOrder |
| 103 | + ,CONCAT(',', '(SELECT COUNT(*) FROM ',CONCAT(@Schema2,'.',@Table2),') AS Count_Table2') AS SQLStatement |
| 104 | + |
| 105 | +----------------------------- |
| 106 | +----------------------------- |
| 107 | +--Level 500 |
| 108 | +--DistinctCount fields |
| 109 | +SELECT 500 AS QueryOrder |
| 110 | + ,CONCAT(',', '(SELECT COUNT(DISTINCT ', @Exists1, ') FROM ',CONCAT(@Schema1,'.',@Table1),' AS t1) AS DistinctCount_Table1') AS SQLStatement |
| 111 | +INTO #DISTINCT_COUNT |
| 112 | +UNION ALL |
| 113 | +SELECT 510 AS QueryOrder |
| 114 | + ,CONCAT(',', '(SELECT COUNT(DISTINCT ', @Exists2, ') FROM ',CONCAT(@Schema2,'.',@Table2),' AS t2) AS DistinctCount_Table2') AS SQLStatement |
| 115 | +UNION ALL |
| 116 | +SELECT 520 |
| 117 | + ,CONCAT(',', '(SELECT COUNT(*) FROM ',CONCAT(@Schema1,'.',@Table1),' AS t1 INNER JOIN ',CONCAT(@Schema2,'.',@Table2),' AS t2 ON ',@Joinsyntax,') AS DistinctIntersect') |
| 118 | + |
| 119 | +----------------------------- |
| 120 | +----------------------------- |
| 121 | +--Level 600 |
| 122 | +--Compare fields |
| 123 | +SELECT DISTINCT |
| 124 | + 600 AS QueryOrder |
| 125 | + ,c.Name AS ColumnName |
| 126 | + ,CONCAT('OR ',c.Name, '_Compare = 1') AS ColumnName_Compare |
| 127 | + ,CONCAT(',CASE WHEN t1.[',c.name, '] IS NULL AND t2.[',c.name, '] IS NULL THEN 0 WHEN t1.[',c.name, '] = ', 't2.[', c.name, '] THEN 0 ELSE 1 END AS [', c.name,'_Compare]') |
| 128 | + AS SQLStatement |
| 129 | +INTO #Compare |
| 130 | +FROM sys.schemas s LEFT OUTER JOIN |
| 131 | + sys.tables t ON s.schema_id = t.schema_id INNER JOIN |
| 132 | + sys.columns c ON t.object_id = c.object_id INNER JOIN |
| 133 | + sys.types ty ON ty.user_type_id = c.user_type_id |
| 134 | +WHERE (t.name = @Table1 AND s.name = @Schema1) OR |
| 135 | + (t.name = @Table2 AND s.name = @Schema2); |
| 136 | + |
| 137 | +----------------------------- |
| 138 | +----------------------------- |
| 139 | +--Level 600 |
| 140 | +--Creates the Column fields |
| 141 | +SELECT 600 AS QueryOrder --Set this value to the same as #Compare to project the _Compare field next to the _Table1 and _Table2 fields |
| 142 | + ,c.Name AS ColumnName |
| 143 | + ,CASE WHEN t.name = @Table1 THEN CONCAT(',t1.[',c.name,'] AS [',c.name, '_Table1]') |
| 144 | + WHEN t.name = @Table2 THEN CONCAT(',t2.[',c.name,'] AS [',c.name, '_Table2]') END AS SQLStatement |
| 145 | +INTO #Columns |
| 146 | +FROM sys.schemas s LEFT OUTER JOIN |
| 147 | + sys.tables t ON s.schema_id = t.schema_id INNER JOIN |
| 148 | + sys.columns c ON t.object_id = c.object_id INNER JOIN |
| 149 | + sys.types ty ON ty.user_type_id = c.user_type_id |
| 150 | +WHERE (t.name = @Table1 AND s.name = @Schema1) |
| 151 | + OR |
| 152 | + (t.name = @Table2 AND s.name = @Schema2); |
| 153 | + |
| 154 | +----------------------------- |
| 155 | +----------------------------- |
| 156 | +--Level 700 |
| 157 | +--INTO Statement |
| 158 | +SELECT 700 AS QueryOrder |
| 159 | + ,CONCAT('INTO ',@TempTable) AS SQLStatement |
| 160 | +INTO #Into; |
| 161 | + |
| 162 | +----------------------------- |
| 163 | +----------------------------- |
| 164 | +--Level 800 |
| 165 | +--FROM Statement |
| 166 | +SELECT 800 AS QueryOrder |
| 167 | + ,'FROM' AS SQLStatement |
| 168 | +INTO #From; |
| 169 | + |
| 170 | +----------------------------- |
| 171 | +----------------------------- |
| 172 | +--Level 900 |
| 173 | +--FULL OUTER JOIN |
| 174 | +SELECT 900 AS QueryOrder |
| 175 | + ,CONCAT(@Schema1,'.',@Table1, ' t1 FULL OUTER JOIN ') AS SQLStatement |
| 176 | +INTO #FullOuterJoin |
| 177 | +UNION |
| 178 | +SELECT 950 AS QueryOrder |
| 179 | + ,CONCAT(@Schema2,'.',@Table2, ' t2 ON ',@JoinSyntax); |
| 180 | + |
| 181 | +----------------------------- |
| 182 | +----------------------------- |
| 183 | +--Create table #SQLStatement |
| 184 | +--This is then used to create the #SQLStatementFinal table |
| 185 | +;WITH CTE_SQLStatement_A AS |
| 186 | +( |
| 187 | +SELECT QueryOrder |
| 188 | + ,SQLStatement |
| 189 | +FROM #Select |
| 190 | +UNION ALL |
| 191 | +SELECT QueryOrder |
| 192 | + ,SQLStatement |
| 193 | +FROM #Exists |
| 194 | +UNION ALL |
| 195 | +SELECT QueryOrder |
| 196 | + ,SQLStatement |
| 197 | +FROM #Distinct_Count |
| 198 | +UNION ALL |
| 199 | +SELECT QueryOrder |
| 200 | + ,SQLStatement |
| 201 | +FROM #Count |
| 202 | +UNION ALL |
| 203 | +SELECT QueryOrder |
| 204 | + ,SQLStatement |
| 205 | +FROM #From |
| 206 | +UNION ALL |
| 207 | +SELECT QueryOrder |
| 208 | + ,SQLStatement |
| 209 | +FROM #FullOuterJoin |
| 210 | +), |
| 211 | +CTE_SQLStatement_B AS |
| 212 | +( |
| 213 | +SELECT QueryOrder |
| 214 | + ,ColumnName AS SortID |
| 215 | + ,SQLStatement |
| 216 | +FROM #Columns |
| 217 | +UNION ALL |
| 218 | +SELECT QueryOrder |
| 219 | + ,ColumnName AS SortID |
| 220 | + ,SQLStatement |
| 221 | +FROM #Compare |
| 222 | +UNION ALL |
| 223 | +SELECT QueryOrder |
| 224 | + ,CONVERT(VARCHAR(255), NEWID()) SortID |
| 225 | + ,SQLStatement |
| 226 | +FROM CTE_SQLStatement_A |
| 227 | +) |
| 228 | +SELECT ROW_NUMBER() OVER (ORDER BY QueryOrder, SortID, SQLStatement) AS QueryOrder |
| 229 | + ,SQLStatement |
| 230 | +INTO #SQLStatementTemp |
| 231 | +FROM CTE_SQLStatement_B |
| 232 | +ORDER BY QueryOrder, SortID, SQLStatement; |
| 233 | + |
| 234 | +----------------------------- |
| 235 | +----------------------------- |
| 236 | +--Create table #SQLStatementFinal |
| 237 | +SELECT 'WITH CTE_SQLStatement AS ( ' AS SQLStatement |
| 238 | +INTO #SQLStatementFinal |
| 239 | +UNION ALL |
| 240 | +SELECT SQLStatement FROM #SQLStatementTemp |
| 241 | +UNION ALL |
| 242 | +SELECT ') SELECT ' |
| 243 | +UNION ALL |
| 244 | +SELECT 'CASE WHEN ' |
| 245 | +UNION ALL |
| 246 | +SELECT SUBSTRING(STRING_AGG(ColumnName_Compare,' '),4,LEN(STRING_AGG(ColumnName_Compare,' ')))-- REMOVES THE INITIAL 'OR' IN THE CASE STATEMENT |
| 247 | +FROM #Compare |
| 248 | +UNION ALL |
| 249 | +SELECT ' THEN 1 ELSE 0 END AS [Compare_Summary]' |
| 250 | +UNION ALL |
| 251 | +SELECT ',* ' |
| 252 | +UNION ALL |
| 253 | +SELECT SQLStatement |
| 254 | +FROM #Into |
| 255 | +UNION ALL |
| 256 | +SELECT 'FROM CTE_SQLStatement;' |
| 257 | + |
| 258 | +----------------------------- |
| 259 | +----------------------------- |
| 260 | +--Places the SQL statement into one line |
| 261 | +SELECT @MySQLStatement = STRING_AGG(SQLStatement,' ') |
| 262 | +FROM #SQLStatementFinal; |
| 263 | + |
| 264 | +/* |
| 265 | +--Display the SQL Statement |
| 266 | +SELECT * FROM #SQLStatementFinal; |
| 267 | +*/ |
| 268 | + |
| 269 | + |
| 270 | +EXECUTE(@MySQLStatement); |
| 271 | +EXECUTE('SELECT DISTINCT * FROM ' + @TempTable + ' ORDER BY 1 DESC, 2,3,4,5'); --The CONCAT function does not work for dynamic sql. |
| 272 | + |
| 273 | +------------------------------- |
0 commit comments