execsql2 1.130.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2917 @@
1
+ -- ss_upsert.sql
2
+ --
3
+ -- PURPOSE
4
+ -- A set of execsql scripts to check data in a staging table, or
5
+ -- a set of staging tables, and then update and insert rows of a base table
6
+ -- or base tables from the staging table(s) of the same name.
7
+ --
8
+ -- This script contains code specific to Microsoft SQL Server. It was developed
9
+ -- and tested using SQL Server 2017 Developer, and also tested against SQL Server 2016.
10
+ --
11
+ -- HOW TO USE THESE SCRIPTS
12
+ -- In the following steps, "call" means to use an execsql "execute script"
13
+ -- metacommand. Script names are displayed in uppercase to distinguish
14
+ -- them below, but execsql is not case-sensitive.
15
+ --
16
+ -- The simplest usage is:
17
+ -- 1. Call STAGED_TO_LOAD to create and initially populate a table
18
+ -- with the names of staging tables to be loaded, and initial
19
+ -- control variables used by other scripts.
20
+ -- 2. Call LOAD_STAGING to perform QA checks for erroneous nulls,
21
+ -- duplicated primary keys, and missing foreign keys, and to
22
+ -- load the data using update and insert statements if there were
23
+ -- no QA errors.
24
+ --
25
+ -- The control table that is produced in step 1 above can be edited to
26
+ -- add a list of columns to exclude from the update, or to change the
27
+ -- defaults for display of changed data. See the header notes for the
28
+ -- STAGED_TO_LOAD script.
29
+ --
30
+ -- The processes of performing QA checks and performing the upsert operations
31
+ -- can be further broken down into individual steps. See Note #1 below
32
+ -- for the other scripts that can be used to carry out these steps.
33
+ --
34
+ -- NOTES
35
+ -- 1. The scripts contained in this file that are intended to be called
36
+ -- directly by the user are:
37
+ -- STAGED_TO_LOAD : Initialize a control table to load multiple tables.
38
+ -- LOAD_STAGING : Perform all QA checks and load data from all staging tables.
39
+ -- QA_ALL : Perform null and foreign key checks on all staging tables.
40
+ -- UPSERT_ALL : Load data from all staging tables.
41
+ -- NULLQA_ONE : Perform null column checks on one staging table.
42
+ -- PKQA_ONE : Perform primary key check on one staging table.
43
+ -- FKQA_ONE : Perform foreign key checks on one staging table.
44
+ -- UPSERT_ONE : Load data from one staging table.
45
+ -- UPDTPK_ONE : Perform PK updates for one table.
46
+ -- UPDTPKQA_ONE : Perform QA checks related to PK updates, for one table.
47
+ -- This file contains other scripts that are intended to be used
48
+ -- only by one of the scripts listed above, and not called
49
+ -- directly by the user.
50
+ -- 2. These scripts query the information schema to obtain
51
+ -- the information needed to perform the checks and changes.
52
+ -- 3. These scripts were developed for Microsoft SQL Server; they were developed
53
+ -- and tested using SQL Server 2017 Developer Edition, and also tested using
54
+ -- Microsoft SQL Server 2016 Professional; they will likely require
55
+ -- modification to run on other DBMSs, or with earlier versions of SQL Server.
56
+ -- 4. These scripts take arguments that control their functions. They
57
+ -- also use global variables pertinent to logging, if they are
58
+ -- defined. The logging-control variables are global because
59
+ -- they may also be used by other code that uses these scripts,
60
+ -- and some of that code may be older and only recognize global
61
+ -- variables rather than script arguments; logging is intended
62
+ -- to be compatible with them.
63
+ -- 5. The control table that is used to drive the loading of multiple
64
+ -- staging tables will be updated by the QA scripts with information
65
+ -- about any QA failures. This information consists of a list of
66
+ -- the names of the columns or constraints that failed the check,
67
+ -- with the number of failing rows in parentheses following the name.
68
+ -- 6. The control table that is used to drive the loading of multiple
69
+ -- staging tables will be updated by the upsert operation with
70
+ -- a count of the number of rows of the base table that are updated,
71
+ -- and a count of the number of rows that were inserted into the
72
+ -- base table.
73
+ -- 7. All of these scripts assume that schema, table, and column
74
+ -- names need not be quoted.
75
+ -- 8. These scripts create temporary tables. All of these
76
+ -- have prefixes of "ups_". Scripts that include this file
77
+ -- should not use this prefix to avoid possible conflicts.
78
+ --
79
+ -- COPYRIGHT AND LICENSE
80
+ -- Copyright (c) 2019, Elizabeth Shea
81
+ -- This program is free software: you can redistribute it and/or modify it
82
+ -- under the terms of the GNU General Public License as published by the
83
+ -- Free Software Foundation, either version 3 of the License, or (at your
84
+ -- option) any later version. This program is distributed in the hope that
85
+ -- it will be useful, but WITHOUT ANY WARRANTY; without even the implied
86
+ -- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
87
+ -- GNU General Public License for more details. The GNU General Public
88
+ -- License is available at http://www.gnu.org/licenses/.
89
+
90
+ -- AUTHORS
91
+ -- Elizabeth Shea (ES)
92
+ -- Dreas Nielsen (RDN)
93
+ --
94
+ -- VERSION
95
+ -- 2.0.0
96
+ --
97
+ -- HISTORY
98
+ -- Date Remarks
99
+ -- ----------- -----------------------------------------------------
100
+ -- 2019-02-17 Created. Began adapting from pg_upsert.sql (version
101
+ -- for PostgresSQL). ES.
102
+ -- 2019-03-03 Completed first draft. ES.
103
+ -- 2019-03-07 Added primary key check and fixed console progress bar
104
+ -- for QA checks. ES.
105
+ -- 2019-03-08 Updated header. ES.
106
+ -- 2019-03-09 Minor updates and corrections to address RDN comments.
107
+ -- ES.
108
+ -- 2019-03-13 Added definition and execution of validation scripts
109
+ -- to validate base and staging schemas and tables. ES.
110
+ -- 2019-03-14 Streamlined execution of validation scripts. ES.
111
+ -- 2019-05-01 Replaced one instance of 'trim()' with 'rtrim(ltrim())'. RDN.
112
+ -- 2019-05-08 Added notes on primary key update script to header
113
+ -- and added script UPDATEPK_ONE.
114
+ -- Made edit to selection of #ups_cols in script UPSERT_ONE
115
+ -- to prevent an error if the staging table contains any
116
+ -- columns that are not in the base table (e.g., "new" cols
117
+ -- for PK updates). ES.
118
+ -- 2019-05-10 Changed name of PK update script; added header notes for
119
+ -- PK update QA script. Changed table_name length in
120
+ -- control table from 8000 to 1700, max key length for a
121
+ -- nonclustered index. ES.
122
+ -- 2019-05-14 Added script UPDTPKQA_ONE and some (not all) QA checks. ES.
123
+ -- 2019-05-21 Completed first draft of UPDTPKQA_ONE. ES.
124
+ -- 2019-05-22 Rearranged, organized, and standardized variable names
125
+ -- in UPDTPKQA_ONE. ES.
126
+ -- 2019-05-29 Corrections to comments. ES.
127
+ -- 2019-06-03 Correction to logging in UPDTPKQA_ONE_INNERLOOP. ES.
128
+ -- 2020-04-28 Explicitly rolled back the changes if 'do_commit' = "No". RDN.
129
+ -- 2020-05-09 Moved the assignment of 'upsert_progress_denom' out of the
130
+ -- 'update_console_qa' script. RDN.
131
+ -- ==================================================================
132
+
133
+ -- ################################################################
134
+ -- Script TEMPTABLE_ISVALID
135
+ -- ===============================================================
136
+ --
137
+ -- Utility script to validate SQL Server temp table name.
138
+ -- Confirms that the name passed by the user is prefixed with a '#',
139
+ -- and raises an error if not
140
+ --
141
+ -- Input parameters:
142
+ -- temptable_name : The name of the temporary table to be created.
143
+ --
144
+ -- Columns in the table created:
145
+ -- temptbl_name : The name of the temp table to be created by
146
+ -- the calling script.
147
+ --
148
+ --
149
+ -- Example:
150
+ -- -- !x! execute script temp_table_isvalid with (temptable_name=#stagingtables)
151
+ -- ===============================================================
152
+ -- !x! BEGIN SCRIPT TEMPTABLE_ISVALID with parameters (temptable_name)
153
+
154
+ -- Take the user-provided temp_table argument and validate syntax
155
+ if object_id('tempdb..#tmptbl_name') is not null drop table #tmptbl_name;
156
+ select
157
+ case when left('!!#temptable_name!!',1) = '#'
158
+ then 1
159
+ else 0
160
+ end as temptbl_name
161
+ into #tmptbl_name
162
+ ;
163
+ -- !x! subdata ~valid_tmptable #tmptbl_name
164
+ -- !x! if(is_zero(!!~valid_tmptable!!))
165
+ -- !x! sub ~message Error in SQL Server temporary table name assignment, script !!$CURRENT_SCRIPT!!, line !!$SCRIPT_LINE!!.
166
+ -- !x! sub_append ~message The name "!!#temptable_name!!" is not a valid SQL Server temporary table name.
167
+ -- !x! sub_append ~message Temporary table names must include a '#' prefix (e.g., '#mytable')
168
+ -- !x! halt message "!!~message!!"
169
+ -- !x! endif
170
+
171
+ -- !x! END SCRIPT
172
+ -- ################## End of TEMPTABLE_ISVALID #####################
173
+ -- ################################################################
174
+
175
+
176
+ -- ################################################################
177
+ -- Script STRING_AGG
178
+ -- ===============================================================
179
+ --
180
+ -- Utility script to perform string aggregation
181
+ -- Beginning with 2017 edition, SQL Server *does* include the string_agg()
182
+ -- function, but earlier versions do not.
183
+ --
184
+ -- Input parameters:
185
+ -- table_name : The name of the table containing the column to be aggregated.
186
+ -- This may be a temp table containing pre-processed strings.
187
+ -- string_col : The name of the column to be aggregated.
188
+ -- order_col : The name of the column to order by
189
+ -- delimiter : Character to be used as a delimiter
190
+ -- string_var : Name of variable to which aggregated string will be assigned
191
+ --
192
+ -- Output parameters:
193
+ -- string_var : The name of the variable to receive the aggregated string.
194
+ --
195
+ --
196
+ -- Example:
197
+ -- -- !x! execute script string_agg with (table_name=#nonnulcols, string_col=null_errors, order_col=null_errors, delimiter=", ", string_var=+nullcols)
198
+ -- ===============================================================
199
+ -- !x! BEGIN SCRIPT STRING_AGG with parameters (table_name, string_col, order_col, delimiter, string_var)
200
+
201
+ if object_id('tempdb..#agg_string') is not null drop table #agg_string;
202
+ with enum as
203
+ (
204
+ select
205
+ cast(!!#string_col!! as varchar(max)) as agg_string,
206
+ row_number() over (order by !!#order_col!!) as row_num
207
+ from
208
+ !!#table_name!!
209
+ ),
210
+ agg as
211
+ (
212
+ select
213
+ one.agg_string,
214
+ one.row_num
215
+ from
216
+ enum as one
217
+ where
218
+ one.row_num=1
219
+ UNION ALL
220
+ select
221
+ agg.agg_string + '!!#delimiter!!' + enum.agg_string as agg_string,
222
+ enum.row_num
223
+ from
224
+ agg, enum
225
+ where
226
+ enum.row_num=agg.row_num+1
227
+ )
228
+ select
229
+ agg_string
230
+ into #agg_string
231
+ from agg
232
+ where row_num=(select max(row_num) from agg);
233
+ -- !x! if(hasrows(#agg_string))
234
+ -- !x! subdata !!#string_var!! #agg_string
235
+ -- !x! endif
236
+
237
+ -- !x! END SCRIPT
238
+ -- ################## End of STRING_AGG #####################
239
+ -- ################################################################
240
+
241
+
242
+ -- ################################################################
243
+ -- Script VALIDATE_SCHEMAS
244
+ -- ===============================================================
245
+ --
246
+ -- Utility script to validate base and staging schema
247
+ --
248
+ -- Required input arguments:
249
+ -- base_schema : The name of the base table schema.
250
+ -- staging : The name of the staging schema.
251
+ --
252
+ -- Required output arguments:
253
+ -- error_list : The name of the variable to receive a comma-
254
+ -- delimited list of the names of invalid
255
+ -- schema names.
256
+ -- ===============================================================
257
+ -- !x! BEGIN SCRIPT VALIDATE_SCHEMAS with parameters (base_schema, staging, error_list)
258
+
259
+ if object_id('tempdb..#ups_ctrl_invl_schema') is not null drop table #ups_ctrl_invl_schema;
260
+ select
261
+ schemas.schema_name,
262
+ schemas.schema_type,
263
+ schemas.schema_name + ' (' + schema_type + ')' as schema_string
264
+ into #ups_ctrl_invl_schema
265
+ from
266
+ (
267
+ select
268
+ '!!#base_schema!!' as schema_name,
269
+ 'base' as schema_type
270
+ union
271
+ select
272
+
273
+ '!!#staging!!' as schema_name,
274
+ 'staging' as schema_type
275
+ ) as schemas
276
+ left join information_schema.schemata as iss on schemas.schema_name=iss.schema_name
277
+ where
278
+ iss.schema_name is null
279
+ ;
280
+
281
+ -- !x! if(hasrows(#ups_ctrl_invl_schema))
282
+ -- !x! execute script string_agg with (table_name=#ups_ctrl_invl_schema, string_col=schema_string, order_col=schema_type, delimiter='; ', string_var=!!#error_list!!)
283
+ -- !x! endif
284
+
285
+ -- !x! END SCRIPT
286
+ -- #################### End of VALIDATE_SCHEMAS #################
287
+ -- ################################################################
288
+
289
+
290
+ -- ################################################################
291
+ -- Script VALIDATE_ONE
292
+ -- ===============================================================
293
+ --
294
+ -- Utility script to validate one table in both base and staging schema
295
+ -- Halts script processing if any either of the schemas are non-existent,
296
+ -- or if either of the tables are not present within those schemas.
297
+ --
298
+ -- Input parameters:
299
+ -- base_schema : The name of the base table schema.
300
+ -- staging : The name of the staging schema.
301
+ -- table_name : The name of the table.
302
+ -- script : The name of the script in which the
303
+ -- schemas and table were referenced
304
+ -- (for error reporting).
305
+ -- script_line : The script line in which they were referenced
306
+ -- (for error reporting).
307
+ -- ===============================================================
308
+ -- !x! BEGIN SCRIPT VALIDATE_ONE with parameters (base_schema, staging, table, script, script_line)
309
+
310
+ -- Initialize the strings used to compile error information
311
+ -- !x! sub_empty ~err_info
312
+ -- !x! sub_empty ~error_list
313
+
314
+
315
+ -- Validate schemas
316
+ -- !x! execute script validate_schemas with args (base_schema=!!#base_schema!!, staging=!!#staging!!, error_list=+err_info)
317
+
318
+
319
+ -- If no schemas are invalid, check tables
320
+ -- !x! if(is_null("!!~err_info!!"))
321
+ if object_id('tempdb..#ups_invl_table') is not null drop table #ups_invl_table;
322
+ select
323
+ tt.schema_name,
324
+ tt.schema_type,
325
+ tt.schema_name + '.' + tt.table_name + ' (' + tt.schema_type + ')' as schema_table
326
+ into #ups_invl_table
327
+ from
328
+ (
329
+ select
330
+ '!!#base_schema!!' as schema_name,
331
+ 'base' as schema_type,
332
+ '!!#table!!' as table_name
333
+ union
334
+ select
335
+
336
+ '!!#staging!!' as schema_name,
337
+ 'staging' as schema_type,
338
+ '!!#table!!' as table_name
339
+ ) as tt
340
+ left join information_schema.tables as iss on tt.schema_name=iss.table_schema and tt.table_name=iss.table_name
341
+ where
342
+ iss.table_name is null
343
+ ;
344
+
345
+ -- !x! if(hasrows(#ups_invl_table))
346
+ -- !x! execute script string_agg with (table_name=#ups_invl_table, string_col=schema_table, order_col=schema_table, delimiter='; ', string_var=+err_info)
347
+ -- !x! sub ~error_list Non-existent table: !!~err_info!!
348
+ -- !x! endif
349
+
350
+ -- !x! else
351
+ -- !x! sub ~error_list Non-existent schema(s): !!~err_info!!
352
+ -- !x! endif
353
+
354
+
355
+ -- Halt script if any schemas or tables were found to be invalid
356
+ -- !x! if(not is_null("!!~error_list!!"))
357
+
358
+ -- !x! sub ~error_message ERROR - INVALID OBJECT IN SCRIPT ARGUMENT
359
+
360
+ -- !x! if(sub_defined(log_changes))
361
+ -- !x! andif(is_true(!!log_changes!!))
362
+ -- !x! write "==================================================================" to !!logfile!!
363
+ -- !x! write "!!$current_time!! -- !!~error_message!!" to !!logfile!!
364
+ -- !x! write "Script: !!#script!!; Line: !!#script_line!!" to !!logfile!!
365
+ -- !x! write "!!~error_list!!" to !!logfile!!
366
+ -- !x! endif
367
+
368
+ -- !x! sub_append ~error_message Script: !!#script!!; Line: !!#script_line!!
369
+ -- !x! sub_append ~error_message !!~error_list!!
370
+ -- !x! halt message "!!~error_message!!"
371
+
372
+ -- !x! endif
373
+
374
+
375
+ -- !x! END SCRIPT
376
+ -- #################### End of VALIDATE_ONE #####################
377
+ -- ################################################################
378
+
379
+
380
+
381
+ -- ################################################################
382
+ -- Script VALIDATE_CONTROL
383
+ -- ===============================================================
384
+ --
385
+ -- Utility script to validate contents of control table against about
386
+ -- base and staging schema
387
+ --
388
+ -- Required input arguments:
389
+ -- base_schema : The name of the base table schema.
390
+ -- staging : The name of the staging schema.
391
+ -- control_table : The name of a temporary table as created by the
392
+ -- script STAGED_TO_LOAD.
393
+ -- script : The name of the script in which the
394
+ -- schemas and control table were referenced
395
+ -- (for error reporting).
396
+ -- script_line : The script line in which they were referenced
397
+ -- (for error reporting).
398
+ -- ===============================================================
399
+ -- !x! BEGIN SCRIPT VALIDATE_CONTROL with parameters (base_schema, staging, control_table, script, script_line)
400
+
401
+ -- Initialize the strings used to compile error information
402
+ -- !x! sub_empty ~err_info
403
+ -- !x! sub_empty ~error_list
404
+
405
+ -- !x! execute script validate_schemas with args (base_schema=!!#base_schema!!, staging=!!#staging!!, error_list=+err_info)
406
+
407
+
408
+ -- If no schemas are invalid, check tables
409
+ -- !x! if(is_null("!!~err_info!!"))
410
+ if object_id('tempdb..#ups_validate_control') is not null drop table #ups_validate_control;
411
+ select
412
+ '!!#base_schema!!' as base_schema,
413
+ '!!#staging!!' as staging_schema,
414
+ table_name,
415
+ cast(0 as bit) as base_exists,
416
+ cast(0 as bit) as staging_exists
417
+ into #ups_validate_control
418
+ from !!#control_table!!
419
+ ;
420
+
421
+ -- Update the control table
422
+ update vc
423
+ set base_exists = cast(case when bt.table_name is null then 0 else 1 end as bit),
424
+ staging_exists = cast(case when st.table_name is null then 0 else 1 end as bit)
425
+ from #ups_validate_control as vc
426
+ left join information_schema.tables as bt on vc.base_schema=bt.table_schema and vc.table_name=bt.table_name
427
+ and bt.table_type='BASE TABLE'
428
+ left join information_schema.tables as st on vc.staging_schema=st.table_schema and vc.table_name=st.table_name
429
+ and st.table_type='BASE TABLE'
430
+ ;
431
+
432
+ if object_id('tempdb..#ups_ctrl_invl_table') is not null drop table #ups_ctrl_invl_table;
433
+ select
434
+ schema_table
435
+ into #ups_ctrl_invl_table
436
+ from
437
+ (
438
+ select base_schema + '.' + table_name as schema_table
439
+ from #ups_validate_control
440
+ where not base_exists=1
441
+ union
442
+ select staging_schema + '.' + table_name as schema_table
443
+ from #ups_validate_control
444
+ where not staging_exists=1
445
+ ) as it
446
+ ;
447
+
448
+ -- Any table is invalid
449
+ -- !x! if(hasrows(#ups_ctrl_invl_table))
450
+ -- !x! execute script string_agg with (table_name=#ups_ctrl_invl_table, string_col=schema_table, order_col=schema_table, delimiter='; ', string_var=+err_info)
451
+ -- !x! sub ~error_list Non-existent table(s): !!~err_info!!
452
+ -- !x! endif
453
+
454
+ -- !x! else
455
+ -- !x! sub ~error_list Non-existent schema(s): !!~err_info!!
456
+ -- !x! endif
457
+
458
+
459
+ -- Halt script if any invalid objects found in control table
460
+ -- !x! if(not is_null("!!~error_list!!"))
461
+
462
+ -- !x! sub ~error_message ERROR - INVALID OBJECTS IN CONTROL TABLE
463
+
464
+ -- !x! if(sub_defined(log_changes))
465
+ -- !x! andif(is_true(!!log_changes!!))
466
+ -- !x! write "==================================================================" to !!logfile!!
467
+ -- !x! write "!!$current_time!! -- !!~error_message!!" to !!logfile!!
468
+ -- !x! write "Script: !!#script!!; Line: !!#script_line!!" to !!logfile!!
469
+ -- !x! write "!!~error_list!!" to !!logfile!!
470
+ -- !x! endif
471
+
472
+ -- !x! sub_append ~error_message Script: !!#script!!; Line: !!#script_line!!
473
+ -- !x! sub_append ~error_message !!~error_list!!
474
+ -- !x! halt message "!!~error_message!!"
475
+
476
+ -- !x! endif
477
+
478
+
479
+ -- !x! END SCRIPT
480
+ -- #################### End of VALIDATE_CONTROL #################
481
+ -- ################################################################
482
+
483
+
484
+ -- ################################################################
485
+ -- Script STAGED_TO_LOAD
486
+ -- ===============================================================
487
+ --
488
+ -- Creates a table having the structure that is used to drive other
489
+ -- scripts that perform QA checks and the upsert operation on multiple
490
+ -- staging tables.
491
+ --
492
+ -- Input parameters:
493
+ -- control_table : The name of the temporary table to be created.
494
+ -- In SQL Server, this *must* include a '#'
495
+ -- prefix. A validation step will raise
496
+ -- an error if an invalid temp table name is passed.
497
+ -- table_list : A string of comma-separated table names,
498
+ -- identifying the staging tables to be
499
+ -- checked or loaded.
500
+ --
501
+ -- Columns in the table created:
502
+ -- table_name : The name of a base table that has a
503
+ -- corresponding table in a staging schema
504
+ -- containing data to be used to modify
505
+ -- the base table.
506
+ -- exclude_cols : Contains a comma-separated list of columns
507
+ -- in the base table that are not to be
508
+ -- updated from the staging table. This is
509
+ -- uninitialized.
510
+ -- exclude_null_checks :
511
+ -- Contains a comma-separated list of single-quoted
512
+ -- column names identifying column in the
513
+ -- staging table for which null checks
514
+ -- should not be performed. This is
515
+ -- uninitialized.
516
+ -- display_changes : A Boolean indicating whether the 'upsert'
517
+ -- operation should display the changes to
518
+ -- be made to the base table. A separate
519
+ -- GUI display is used for updates and inserts.
520
+ -- Initialized to True.
521
+ -- display_final : A Boolean indicating whether the 'upsert'
522
+ -- operation should display the entire base
523
+ -- table after updates and inserts have been
524
+ -- made. Initialized to False.
525
+ -- null_errors : Will contain a comma-separated list of
526
+ -- columns that are non-nullable in the base
527
+ -- table but that have nulls in the staging
528
+ -- table. Initialized to null; may be filled
529
+ -- by the QA routines.
530
+ -- pk_errors : Will contain a count of the number of distinct
531
+ -- primary key values having duplicate rows,
532
+ -- followed by the total row count for the
533
+ -- duplicated keys. Initialized to null; may
534
+ -- be filled by the QA routines.
535
+ -- fk_errors : Will contain a comma-separated list of
536
+ -- foreign-key constraint names that are
537
+ -- not met by data in the staging table.
538
+ -- Initialized to null; may be filled by the
539
+ -- QA routines.
540
+ -- rows_updated : Will contain a count of the rows updated
541
+ -- in the table by the upsert operation.
542
+ -- rows_inserted : Will contain a count of the rows inserted
543
+ -- in the table by the upsert operation.
544
+ --
545
+ -- Example:
546
+ -- -- !x! execute script staged_to_load with (control_table=#stagingtables, table_list="tbla, tblb, tblc")
547
+ -- ===============================================================
548
+
549
+ -- !x! BEGIN SCRIPT STAGED_TO_LOAD with parameters (control_table, table_list)
550
+
551
+ -- Run script to validate that control table name is valid temp table name
552
+ -- !x! execute script temptable_isvalid with args (temptable_name=!!#control_table!!)
553
+
554
+
555
+ if object_id('tempdb..!!#control_table!!') is not null drop table !!#control_table!!;
556
+ create table !!#control_table!! (
557
+ table_name varchar(1700) not null unique,
558
+ exclude_cols varchar(8000),
559
+ exclude_null_checks varchar(8000),
560
+ display_changes varchar(3) not null default 'Yes',
561
+ display_final varchar(3) not null default 'No',
562
+ null_errors varchar(8000),
563
+ pk_errors varchar(8000),
564
+ fk_errors varchar(8000),
565
+ rows_updated integer,
566
+ rows_inserted integer,
567
+ check (display_changes in ('Yes', 'No')),
568
+ check (display_final in ('Yes', 'No'))
569
+ );
570
+
571
+
572
+ -- Recursive CTE to parse table list argument
573
+ -- NOTE: SQL Server 2017 includes the trim() function, but SQL Server 2016 does not,
574
+ -- so a combination of ltrim and rtrim is used here instead.
575
+ with itemtable as (
576
+ select
577
+ case when charindex(',', table_string) = 0
578
+ then rtrim(ltrim(table_string))
579
+ else rtrim(ltrim(substring(table_string, 1,charindex(',', table_string)-1)))
580
+ end as table_name,
581
+ case when charindex(',', table_string) = 0
582
+ then NULL
583
+ else rtrim(ltrim(substring(table_string, charindex(',', table_string)+1, len(table_string))))
584
+ end as remaining_list
585
+ from
586
+ (select '!!#table_list!!' as table_string) as ts
587
+ UNION ALL
588
+ select
589
+ case when charindex(',', remaining_list) = 0
590
+ then rtrim(ltrim(remaining_list))
591
+ else rtrim(ltrim(substring(remaining_list, 1,charindex(',', remaining_list)-1)))
592
+ end as table_name,
593
+ case when charindex(',', remaining_list) = 0
594
+ then NULL
595
+ else rtrim(ltrim(substring(remaining_list, charindex(',', remaining_list)+1, len(remaining_list))))
596
+ end as remaining_list
597
+ from
598
+ itemtable
599
+ where
600
+ remaining_list is not null
601
+ --Guards against entries with trailing commas:
602
+ -- e.g, 'table1, table2,'
603
+ and rtrim(ltrim(remaining_list))<>''
604
+ )
605
+ insert into !!#control_table!!
606
+ (table_name)
607
+ select table_name as item
608
+ from itemtable;
609
+
610
+
611
+ -- !x! END SCRIPT
612
+ -- ################## End of STAGED_TO_LOAD #####################
613
+ -- ################################################################
614
+
615
+
616
+
617
+ -- ################################################################
618
+ -- Script LOAD_STAGING
619
+ -- ===============================================================
620
+ --
621
+ -- Performs QA checks for nulls in non-null columns, for duplicated
622
+ -- primary key values, and for invalid foreign keys in a set of staging
623
+ -- tables to be loaded into base tables. If there are failures in the
624
+ -- QA checks, loading is not attempted. If the loading step is carried
625
+ -- out, it is done within a transaction.
626
+ --
627
+ -- The "null_errors", "pk_errors", and "fk_errors" columns of the
628
+ -- control table will be updated to identify any errors that occur,
629
+ -- so that this information is available to the caller.
630
+ --
631
+ -- The "rows_updated" and "rows_inserted" columns of the control table
632
+ -- will be updated with counts of the number of rows affected by the
633
+ -- upsert operation for each table.
634
+ --
635
+ -- When the upsert operation updates the base table, all columns of the
636
+ -- base table that are also in the staging table are updated. The
637
+ -- update operation does not test to see if column contents are different,
638
+ -- and so does not update only those values that are different.
639
+ --
640
+ -- Input parameters:
641
+ -- base_schema : The name of the base table schema.
642
+ -- staging : The name of the staging schema.
643
+ -- control_table : The name of a temporary table as created by the
644
+ -- script STAGED_TO_LOAD.
645
+ -- do_commit : Whether or not the script should commit
646
+ -- the changes; should be 'Yes' or 'No'.
647
+ -- Global variables:
648
+ -- logfile : The name of a log file to which error
649
+ -- messages will be written. Optional.
650
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
651
+ -- the SQL that is generated for each foreign
652
+ -- key check, and for each update and insert
653
+ -- statement, is written to the logfile. Optional.
654
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
655
+ -- foreign key errors are written to the logfile.
656
+ -- Optional.
657
+ -- log_changes : A value of 'Yes' or 'No' indicating whether
658
+ -- the updated and inserted data should be
659
+ -- written to the logfile. Optional.
660
+ --
661
+ -- Tables and views created or modified:
662
+ -- #ups_qa_fails : temporary table
663
+ -- ===============================================================
664
+
665
+ -- !x! BEGIN SCRIPT LOAD_STAGING with parameters (base_schema, staging, control_table, do_commit)
666
+
667
+
668
+ -- Clear the columns of return values from the control table, in case this control
669
+ -- table has been used previously.
670
+ update !!#control_table!!
671
+ set
672
+ null_errors = null,
673
+ pk_errors = null,
674
+ fk_errors = null,
675
+ rows_updated = null,
676
+ rows_inserted = null
677
+ ;
678
+
679
+ -- Run QA checks.
680
+ -- !x! execute script QA_ALL with arguments (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
681
+ if object_id('tempdb..#ups_qa_fails') is not null drop table #ups_qa_fails;
682
+ select *
683
+ into #ups_qa_fails
684
+ from !!#control_table!!
685
+ where null_errors is not null or pk_errors is not null or fk_errors is not null;
686
+ -- !x! if(not hasrows(#ups_qa_fails))
687
+ -- !x! sub ~preautocommit !!$autocommit_state!!
688
+ -- !x! autocommit off
689
+ -- Run the UPSERT operation.
690
+ -- !x! execute script UPSERT_ALL with arguments (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
691
+ -- Commit the changes and then restore the previous autocommit state.
692
+ -- !x! if(is_true(!!#do_commit!!))
693
+ -- !x! autocommit on with commit
694
+ -- !x! write "CHANGES COMMITTED."
695
+ -- !x! if(sub_defined(log_changes))
696
+ -- !x! andif(is_true(!!log_changes!!))
697
+ -- !x! write "" to !!logfile!!
698
+ -- !x! write "==================================================================" to !!logfile!!
699
+ -- !x! write "!!$current_time!! -- CHANGES COMMITTED." to !!logfile!!
700
+ -- !x! endif
701
+ -- !x! else
702
+ -- !x! autocommit on with rollback
703
+ -- !x! write "CHANGES NOT COMMITTED ('do_commit' argument = !!#do_commit!!)"
704
+ -- !x! if(sub_defined(log_changes))
705
+ -- !x! andif(is_true(!!log_changes!!))
706
+ -- !x! write "" to !!logfile!!
707
+ -- !x! write "==================================================================" to !!logfile!!
708
+ -- !x! write "!!$current_time!! -- CHANGES NOT COMMITTED ('do_commit' argument = !!#do_commit!!)" to !!logfile!!
709
+ -- !x! endif
710
+ -- !x! endif
711
+ -- !x! autocommit !!~preautocommit!!
712
+ -- !x! endif
713
+
714
+
715
+ -- !x! END SCRIPT
716
+ -- ################### End of LOAD_STAGING ######################
717
+ -- ################################################################
718
+
719
+
720
+
721
+ -- ################################################################
722
+ -- Script NULLQA_ONE
723
+ -- ===============================================================
724
+ --
725
+ -- Checks that non-nullable columns are fully populated in a
726
+ -- staging table that is an image of the base table.
727
+ -- Reports any non-conforming columns to the console and optionally
728
+ -- to a log file.
729
+ --
730
+ -- Required input arguments:
731
+ -- base_schema : The name of the base table schema.
732
+ -- staging : The name of the staging schema.
733
+ -- table : The table name--same for base and staging.
734
+ -- Optional input arguments:
735
+ -- exclude_null_checks : A comma-separated list of singly-quoted
736
+ -- column names to be excluded from null checks.
737
+ --
738
+ -- Required output arguments:
739
+ -- error_list : The name of the variable to receive a comma-
740
+ -- delimited list of the names of non-null
741
+ -- columns that contain nulls; each column name
742
+ -- will be followed by the number of rows with
743
+ -- nulls, in parentheses.
744
+ --
745
+ -- Global variables:
746
+ -- logfile : The name of a log file to which error
747
+ -- messages will be written. Optional.
748
+ --
749
+ -- Tables and views created or modified:
750
+ -- #ups_nonnull_cols : temporary table
751
+ -- #ups_next_column : temporary table
752
+ -- #ups_null_error_list : temporary table
753
+ -- #ups_qa_nonnull_col : temporary table
754
+ -- ===============================================================
755
+
756
+ -- !x! BEGIN SCRIPT NULLQA_ONE with parameters (base_schema, staging, table, error_list)
757
+
758
+ -- Write an initial header to the logfile.
759
+ -- !x! if(sub_defined(logfile))
760
+ -- !x! write "" to !!logfile!!
761
+ -- !x! write "==================================================================" to !!logfile!!
762
+ -- !x! write "!!$current_time!! -- Non-null QA checks on table !!#staging!!.!!#table!!" to !!logfile!!
763
+ -- !x! endif
764
+
765
+
766
+ -- !x! write "Conducting non-null QA checks on table !!#staging!!.!!#table!!"
767
+
768
+ -- Validate inputs: base/staging schemas and table
769
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
770
+
771
+ -- Initialize the return value to empty (no null errors)
772
+ -- !x! sub_empty !!#error_list!!
773
+
774
+ -- Create a table listing the columns of the base table that must
775
+ -- be non-null and that do not have a default expression.
776
+ -- Include a column for the number of rows with nulls in the staging table.
777
+ -- Include a 'processed' column for loop control.
778
+ -- !x! if(sub_defined(#exclude_null_checks))
779
+ -- !x! sub ~omitnull and column_name not in (!!#exclude_null_checks!!)
780
+ -- !x! else
781
+ -- !x! sub_empty ~omitnull
782
+ -- !x! endif
783
+ if object_id('tempdb..#ups_nonnull_cols') is not null drop table #ups_nonnull_cols;
784
+ select
785
+ column_name,
786
+ cast(0 as integer) as null_rows,
787
+ cast(0 as bit) as processed
788
+ into
789
+ #ups_nonnull_cols
790
+ from
791
+ information_schema.columns
792
+ where
793
+ table_schema = '!!#base_schema!!'
794
+ and table_name = '!!#table!!'
795
+ and is_nullable = 'NO'
796
+ and column_default is null
797
+ !!~omitnull!!
798
+ ;
799
+
800
+
801
+ -- Create a script to select one column to process.
802
+ -- !x! begin script ups_get_next_column
803
+ if object_id('tempdb..#ups_next_column') is not null drop table #ups_next_column;
804
+ select top 1 column_name
805
+ into #ups_next_column
806
+ from #ups_nonnull_cols
807
+ where not processed=1;
808
+ -- !x! end script
809
+
810
+ -- Process all non-nullable columns.
811
+ -- !x! execute script nullqa_one_innerloop with (staging=!!#staging!!, table=!!#table!!)
812
+
813
+ -- Create the return value
814
+ if object_id('tempdb..#ups_null_error_list') is not null drop table #ups_null_error_list;
815
+ select
816
+ column_name + ' (' + cast(null_rows as varchar(max)) + ')' as prepped_col
817
+ into #ups_null_error_list
818
+ from
819
+ #ups_nonnull_cols
820
+ where
821
+ coalesce(null_rows, 0) > 0
822
+ ;
823
+ -- !x! execute script string_agg with (table_name="#ups_null_error_list", string_col=prepped_col, order_col=prepped_col, delimiter=", ", string_var=!!#error_list!!)
824
+
825
+
826
+ -- !x! END SCRIPT
827
+ -- End of NULLQA_ONE
828
+ -- ****************************************************************
829
+ -- ****************************************************************
830
+ -- ****************************************************************
831
+ -- Script NULLQA_ONE_INNERLOOP
832
+ -- ---------------------------------------------------------------
833
+ -- !x! BEGIN SCRIPT NULLQA_ONE_INNERLOOP with parameters (staging, table)
834
+
835
+ -- !x! execute script ups_get_next_column
836
+ -- !x! if(hasrows(#ups_next_column))
837
+ -- !x! subdata ~column_name #ups_next_column
838
+
839
+ -- !x! if(sub_defined(logfile))
840
+ -- !x! write "Checking column !!~column_name!!." to !!logfile!!
841
+ -- !x! endif
842
+
843
+ if object_id('tempdb..#ups_qa_nonnull_col') is not null drop table #ups_qa_nonnull_col;
844
+ select top 1 nrows
845
+ into #ups_qa_nonnull_col
846
+ from (
847
+ select count(*) as nrows
848
+ from !!#staging!!.!!#table!!
849
+ where !!~column_name!! is null
850
+ ) as nullcount
851
+ where nrows > 0;
852
+ -- !x! if(hasrows(#ups_qa_nonnull_col))
853
+ -- !x! subdata ~nullrows #ups_qa_nonnull_col
854
+ -- !x! write " Column !!~column_name!! has !!~nullrows!! nulls."
855
+ -- !x! if(sub_defined(logfile))
856
+ -- !x! write " Column !!~column_name!! has !!~nullrows!! nulls." to !!logfile!!
857
+ -- !x! endif
858
+ update #ups_nonnull_cols
859
+ set null_rows = (select top 1 nrows from #ups_qa_nonnull_col)
860
+ where column_name = '!!~column_name!!';
861
+ -- !x! endif
862
+
863
+
864
+ -- Mark this constraint as processed.
865
+ update #ups_nonnull_cols
866
+ set processed = 1
867
+ where column_name = '!!~column_name!!';
868
+
869
+ -- Loop.
870
+ -- !x! execute script nullqa_one_innerloop with (staging=!!#staging!!, table=!!#table!!)
871
+
872
+ -- !x! endif
873
+
874
+ -- !x! END SCRIPT
875
+ -- ################### End of NULL_QA_ONE #######################
876
+ -- ################################################################
877
+
878
+
879
+
880
+ -- ################################################################
881
+ -- Script PKQA_ONE
882
+ --
883
+ -- Check data a staging table for violations of the primary key
884
+ -- of the corresponding base table.
885
+ -- Reports any PK violations found to the console and optionally
886
+ -- to a log file.
887
+ --
888
+ -- Input parameters:
889
+ -- base_schema : The name of the base table schema.
890
+ -- staging : The name of the staging schema.
891
+ -- table : The table name--same for base and staging.
892
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
893
+ -- unrecognized values should be displayed
894
+ -- in a GUI.
895
+ -- Output parameters:
896
+ -- error_list : The name of the variable to receive a count
897
+ -- of the total number of distinct PK values
898
+ -- having violations, followed by a count of
899
+ -- the total rows associated with each.
900
+ --
901
+ -- Global variables:
902
+ -- logfile : The name of a log file to which update
903
+ -- messages will be written. Optional.
904
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
905
+ -- the SQL that is generated for each foreign
906
+ -- key check is written to the logfile. Optional.
907
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
908
+ -- foreign key errors are written to the logfile.
909
+ -- Optional.
910
+ --
911
+ -- Tables and views created or modified:
912
+ -- #ups_primary_key_columns : temporary table
913
+ -- #ups_pk_check : temporary table
914
+ -- #ups_ercnt : temporary table
915
+ -- ===============================================================
916
+
917
+ -- !x! BEGIN SCRIPT PKQA_ONE with parameters (base_schema, staging, table, display_errors, error_list)
918
+
919
+ -- Write an initial header to the logfile.
920
+ -- !x! if(sub_defined(logfile))
921
+ -- !x! write "" to !!logfile!!
922
+ -- !x! write "==================================================================" to !!logfile!!
923
+ -- !x! write "!!$current_time!! -- Primary key QA checks on table !!#staging!!.!!#table!!" to !!logfile!!
924
+ -- !x! endif
925
+
926
+ -- !x! write "Conducting primary key QA checks on table !!#staging!!.!!#table!!"
927
+
928
+ -- Validate inputs: base/staging schemas and table
929
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
930
+
931
+ -- Initialize the return value to False (no primary key errors)
932
+ -- !x! sub_empty !!#error_list!!
933
+
934
+ -- Create a table of primary key columns on this table
935
+ if object_id('tempdb..#ups_primary_key_columns') is not null drop table #ups_primary_key_columns;
936
+ select k.constraint_name, k.column_name, k.ordinal_position
937
+ into #ups_primary_key_columns
938
+ from information_schema.table_constraints as tc
939
+ inner join information_schema.key_column_usage as k
940
+ on tc.constraint_type = 'PRIMARY KEY'
941
+ and tc.constraint_name = k.constraint_name
942
+ and tc.constraint_catalog = k.constraint_catalog
943
+ and tc.constraint_schema = k.constraint_schema
944
+ and tc.table_schema = k.table_schema
945
+ and tc.table_name = k.table_name
946
+ and tc.constraint_name = k.constraint_name
947
+ where
948
+ k.table_name = '!!#table!!'
949
+ and k.table_schema = '!!#base_schema!!'
950
+ order by k.ordinal_position
951
+ ;
952
+
953
+ -- !x! if(hasrows(#ups_primary_key_columns))
954
+ -- !x! subdata ~constraint_name #ups_primary_key_columns
955
+
956
+ -- !x! if(sub_defined(logfile))
957
+ -- !x! write "Checking constraint !!~constraint_name!!." to !!logfile!!
958
+ -- !x! endif
959
+
960
+ -- Get a comma-delimited list of primary key columns to build SQL selection for duplicate keys
961
+ -- !x! sub_empty ~pkcollist
962
+ -- !x! execute script string_agg with (table_name=#ups_primary_key_columns, string_col=column_name, order_col=ordinal_position, delimiter=", ", string_var=+pkcollist)
963
+
964
+ -- Construct a query to test for duplicate values for pk columns.
965
+ -- !x! sub ~pk_check if object_id('tempdb..#ups_pk_check') is not null drop table #ups_pk_check;
966
+ -- !x! sub_append ~pk_check select !!~pkcollist!!, count(*) as row_count
967
+ -- !x! sub_append ~pk_check into #ups_pk_check
968
+ -- !x! sub_append ~pk_check from !!#staging!!.!!#table!! as s
969
+ -- !x! sub_append ~pk_check group by !!~pkcollist!!
970
+ -- !x! sub_append ~pk_check having count(*) > 1
971
+
972
+ -- Write the SQL to the log file if requested.
973
+ -- !x! if(sub_defined(logfile))
974
+ -- !x! andif(sub_defined(log_sql))
975
+ -- !x! andif(is_true(!!log_sql!!))
976
+ -- !x! write "SQL for primary key check:" to !!logfile!!
977
+ -- !x! write [!!~pk_check!!] to !!logfile!!
978
+ -- !x! endif
979
+
980
+ -- Run the check.
981
+ !!~pk_check!!;
982
+ -- !x! if(hasrows(#ups_pk_check))
983
+ -- !x! write " Duplicate key error on columns: !!~pkcollist!!."
984
+ if object_id('tempdb..#ups_ercnt') is not null drop table #ups_ercnt;
985
+ select count(*) as errcnt, sum(row_count) as total_rows
986
+ into #ups_ercnt
987
+ from #ups_pk_check;
988
+ -- !x! select_sub #ups_ercnt
989
+ -- !x! sub !!#error_list!! !!@errcnt!! duplicated key(s) (!!@total_rows!! rows)
990
+ -- !x! if(sub_defined(logfile))
991
+ -- !x! write "Duplicate primary key values in !!#staging!!.!!#table!!" to !!logfile!!
992
+ -- !x! if(sub_defined(log_errors))
993
+ -- !x! andif(is_true(!!log_errors!!))
994
+ -- !x! export #ups_pk_check append to !!logfile!! as txt
995
+ -- !x! endif
996
+ -- !x! endif
997
+ -- !x! if(is_true(!!#display_errors!!))
998
+ -- !x! prompt message "Primary key violations in !!#table!!" display #ups_pk_check
999
+ -- !x! endif
1000
+ -- !x! endif
1001
+ -- !x! endif
1002
+
1003
+
1004
+ -- !x! END SCRIPT
1005
+ -- #################### End of PKQA_ONE ########################
1006
+ -- ################################################################
1007
+
1008
+
1009
+
1010
+ -- ################################################################
1011
+ -- Script FKQA_ONE
1012
+ --
1013
+ -- Checks foreign keys from a staging table against a base table
1014
+ -- and, if it exists, another staging table that is an image of the
1015
+ -- base table.
1016
+ -- Reports any bad references found to the console and optionally
1017
+ -- to a log file.
1018
+ --
1019
+ -- Input parameters:
1020
+ -- base_schema : The name of the base table schema.
1021
+ -- staging : The name of the staging schema.
1022
+ -- table : The table name--same for base and staging.
1023
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
1024
+ -- unrecognized values should be displayed
1025
+ -- in a GUI.
1026
+ -- Output parameters:
1027
+ -- error_list : The name of the variable to receive a comma-
1028
+ -- delimited list of the names of foreign key
1029
+ -- constraints that are not met.
1030
+ --
1031
+ -- Global variables:
1032
+ -- logfile : The name of a log file to which update
1033
+ -- messages will be written. Optional.
1034
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
1035
+ -- the SQL that is generated for each foreign
1036
+ -- key check is written to the logfile. Optional.
1037
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
1038
+ -- foreign key errors are written to the logfile.
1039
+ -- Optional.
1040
+ --
1041
+ -- Tables and views created or modified:
1042
+ -- ups_foreign_key_columns : temporary table
1043
+ -- ups_sel_fks : temporary table
1044
+ -- ups_fk_constraints : temporary table
1045
+ -- ups_next_constraint : temporary table
1046
+ -- ups_fk_error_list : temporary table
1047
+ -- ups_one_fk : temporary table
1048
+ -- ups_fk_joins : temporary table
1049
+ -- ups_fk_check : temporary table
1050
+ -- ups_ercnt : temporary table
1051
+ -- ===============================================================
1052
+
1053
+ -- !x! BEGIN SCRIPT FKQA_ONE with parameters (base_schema, staging, table, display_errors, error_list)
1054
+
1055
+ -- Write an initial header to the logfile.
1056
+ -- !x! if(sub_defined(logfile))
1057
+ -- !x! write "" to !!logfile!!
1058
+ -- !x! write "==================================================================" to !!logfile!!
1059
+ -- !x! write "!!$current_time!! -- Foreign key QA checks on table !!#staging!!.!!#table!!" to !!logfile!!
1060
+ -- !x! endif
1061
+
1062
+ -- !x! write "Conducting foreign key QA checks on table !!#staging!!.!!#table!!"
1063
+
1064
+ -- Validate inputs: base/staging schemas and table
1065
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
1066
+
1067
+ -- Initialize the return value to False (no foreign key errors)
1068
+ -- !x! sub_empty !!#error_list!!
1069
+
1070
+
1071
+ -- Create a table of *all* foreign key dependencies in this database.
1072
+ -- Because this may be an expensive operation (in terms of time), the
1073
+ -- table is not re-created if it already exists. "Already exists"
1074
+ -- means that a table with the expected name exists. No check is
1075
+ -- done to ensure that this table has the correct structure. The
1076
+ -- goal is to create the table of all foreign keys only once to
1077
+ -- minimize the time required if QA checks are to be run on multiple
1078
+ -- staging tables.
1079
+ if object_id('tempdb..#ups_foreign_key_columns') is null
1080
+ select
1081
+ rc.constraint_name,
1082
+ cu.table_schema,
1083
+ cu.table_name,
1084
+ cu.column_name,
1085
+ cu.ordinal_position,
1086
+ cu_uq.table_schema as uq_schema,
1087
+ cu_uq.table_name as uq_table,
1088
+ cu_uq.column_name as uq_column
1089
+ into #ups_foreign_key_columns
1090
+ from
1091
+ (select distinct constraint_catalog, constraint_schema, constraint_name,
1092
+ unique_constraint_catalog, unique_constraint_schema, unique_constraint_name
1093
+ from information_schema.referential_constraints) as rc
1094
+ inner join (select * from information_schema.table_constraints
1095
+ where constraint_type = 'FOREIGN KEY') as tc
1096
+ on tc.constraint_catalog = rc.constraint_catalog
1097
+ and tc.constraint_schema = rc.constraint_schema
1098
+ and tc.constraint_name = rc.constraint_name
1099
+ inner join (select * from information_schema.table_constraints
1100
+ where constraint_type not in ('FOREIGN KEY', 'CHECK') ) as tc_uq
1101
+ on tc_uq.constraint_catalog = rc.unique_constraint_catalog
1102
+ and tc_uq.constraint_schema = rc.unique_constraint_schema
1103
+ and tc_uq.constraint_name = rc.unique_constraint_name
1104
+ inner join information_schema.key_column_usage as cu
1105
+ on cu.constraint_catalog = tc.constraint_catalog
1106
+ and cu.constraint_schema = tc.constraint_schema
1107
+ and cu.constraint_name = tc.constraint_name
1108
+ and cu.table_catalog = tc.table_catalog
1109
+ and cu.table_schema = tc.table_schema
1110
+ and cu.table_name = tc.table_name
1111
+ inner join information_schema.key_column_usage as cu_uq
1112
+ on cu_uq.constraint_catalog = tc_uq.constraint_catalog
1113
+ and cu_uq.constraint_schema = tc_uq.constraint_schema
1114
+ and cu_uq.constraint_name = tc_uq.constraint_name
1115
+ and cu_uq.table_catalog = tc_uq.table_catalog
1116
+ and cu_uq.table_schema = tc_uq.table_schema
1117
+ and cu_uq.ordinal_position = cu.ordinal_position
1118
+ ;
1119
+
1120
+
1121
+ -- Create a temporary table of just the foreign key relationships for the base
1122
+ -- table corresponding to the staging table to check.
1123
+ if object_id('tempdb..#ups_sel_fks') is not null drop table #ups_sel_fks;
1124
+ select
1125
+ constraint_name, column_name,
1126
+ ordinal_position,
1127
+ uq_schema, uq_table, uq_column
1128
+ into #ups_sel_fks
1129
+ from
1130
+ #ups_foreign_key_columns
1131
+ where
1132
+ table_schema = '!!#base_schema!!'
1133
+ and table_name = '!!#table!!';
1134
+
1135
+ -- Create a temporary table of all unique constraint names for
1136
+ -- this table, with an integer column to be populated with the
1137
+ -- number of rows failing the foreign key check, and a 'processed'
1138
+ -- flag to control looping.
1139
+ if object_id('tempdb..#ups_fk_constraints') is not null drop table #ups_fk_constraints;
1140
+ select distinct
1141
+ constraint_name,
1142
+ cast(0 as integer) as fkerror_rows,
1143
+ cast(0 as bit) as processed
1144
+ into #ups_fk_constraints
1145
+ from #ups_sel_fks;
1146
+
1147
+ -- Create a script to select one constraint to process
1148
+ -- !x! begin script ups_get_next_constraint
1149
+ if object_id('tempdb..#ups_next_constraint') is not null drop table #ups_next_constraint;
1150
+ select top 1 constraint_name
1151
+ into #ups_next_constraint
1152
+ from #ups_fk_constraints
1153
+ where not processed=1;
1154
+ -- !x! end script
1155
+
1156
+
1157
+ -- Process all constraints: check every foreign key.
1158
+ -- !x! execute script fk_qa_one_innerloop with (staging=!!#staging!!, table=!!#table!!, display_errors=!!#display_errors!!)
1159
+
1160
+ -- Create the return value.
1161
+ if object_id('tempdb..#ups_fk_error_list') is not null drop table #ups_fk_error_list;
1162
+ select
1163
+ constraint_name + ' (' + cast(fkerror_rows as varchar(max)) + ')' as fkc_errors,
1164
+ constraint_name
1165
+ into #ups_fk_error_list
1166
+ from #ups_fk_constraints
1167
+ where coalesce(fkerror_rows, 0) > 0;
1168
+
1169
+ -- !x! execute script string_agg with (table_name=#ups_fk_error_list, string_col=fkc_errors, order_col=constraint_name, delimiter=", ", string_var=!!#error_list!!)
1170
+
1171
+ -- !x! END SCRIPT
1172
+ -- End of FKQA_ONE
1173
+ -- ****************************************************************
1174
+ -- ****************************************************************
1175
+ -- Script FK_QA_ONE_INNERLOOP
1176
+ -- ----------------------------------------------------------------
1177
+ -- !x! BEGIN SCRIPT FK_QA_ONE_INNERLOOP with parameters (staging, table, display_errors)
1178
+
1179
+ -- !x! execute script ups_get_next_constraint
1180
+ -- !x! if(hasrows(#ups_next_constraint))
1181
+ -- !x! subdata ~constraint_name #ups_next_constraint
1182
+
1183
+ -- !x! if(sub_defined(logfile))
1184
+ -- !x! write "Checking constraint !!~constraint_name!!." to !!logfile!!
1185
+ -- !x! endif
1186
+
1187
+
1188
+ if object_id('tempdb..#ups_one_fk') is not null drop table #ups_one_fk;
1189
+ select column_name, uq_schema, uq_table, uq_column, ordinal_position
1190
+ into #ups_one_fk
1191
+ from #ups_sel_fks
1192
+ where
1193
+ constraint_name = '!!~constraint_name!!';
1194
+
1195
+ -- Get the unique table schema and name into data variables.
1196
+ -- !x! select_sub #ups_one_fk
1197
+
1198
+ -- Create join expressions from staging table (s) to unique table (u)
1199
+ -- and to staging table equivalent to unique table (su) (though we
1200
+ -- don't know yet if the latter exists). Also create a 'where'
1201
+ -- condition to ensure that all columns being matched are non-null.
1202
+ -- Also create a comma-separated list of the columns being checked.
1203
+ if object_id('tempdb..#ups_fk_joins') is not null drop table #ups_fk_joins;
1204
+ select
1205
+ cast('s.' + column_name + ' = u.' + uq_column as varchar(max)) as u_join,
1206
+ cast('s.' + column_name + ' = su.' + uq_column as varchar(max)) as su_join,
1207
+ cast('s.' + column_name + ' is not null' as varchar(max)) as s_not_null,
1208
+ cast('s.' + column_name as varchar(max)) as s_checked,
1209
+ ordinal_position
1210
+ into #ups_fk_joins
1211
+ from
1212
+ #ups_one_fk;
1213
+
1214
+ -- Create local variables for the different parts of the join expressions
1215
+ -- !x! sub_empty ~u_join
1216
+ -- !x! sub_empty ~su_join
1217
+ -- !x! sub_empty ~s_not_null
1218
+ -- !x! sub_empty ~s_checked
1219
+
1220
+ -- Then populate them using the string aggregation script
1221
+ -- !x! execute script string_agg with(table_name=#ups_fk_joins, string_col=u_join, order_col=ordinal_position, delimiter =" and ", string_var=+u_join)
1222
+ -- !x! execute script string_agg with(table_name=#ups_fk_joins, string_col=su_join, order_col=ordinal_position, delimiter =" and ", string_var=+su_join)
1223
+ -- !x! execute script string_agg with(table_name=#ups_fk_joins, string_col=s_not_null, order_col=ordinal_position, delimiter =" and ", string_var=+s_not_null)
1224
+ -- !x! execute script string_agg with(table_name=#ups_fk_joins, string_col=s_checked, order_col=ordinal_position, delimiter =", ", string_var=+s_checked)
1225
+
1226
+
1227
+ -- Determine whether a staging-table equivalent of the unique table exists.
1228
+ -- !x! sub su_exists No
1229
+ -- !x! if(table_exists(!!#staging!!.!!@uq_table!!))
1230
+ -- !x! sub su_exists Yes
1231
+ -- !x! endif
1232
+
1233
+ -- Construct a query to test for missing unique values for fk columns.
1234
+ -- !x! sub ~fk_check if object_id('tempdb..#ups_fk_check') is not null drop table #ups_fk_check;
1235
+ -- !x! sub_append ~fk_check select !!~s_checked!!, count(*) as row_count
1236
+ -- !x! sub_append ~fk_check into #ups_fk_check
1237
+ -- !x! sub_append ~fk_check from !!#staging!!.!!#table!! as s
1238
+ -- !x! sub_append ~fk_check left join !!@uq_schema!!.!!@uq_table!! as u on !!~u_join!!
1239
+ -- !x! if(is_true(!!su_exists!!))
1240
+ -- !x! sub_append ~fk_check left join !!#staging!!.!!@uq_table!! as su on !!~su_join!!
1241
+ -- !x! endif
1242
+ -- !x! sub_append ~fk_check where u.!!@uq_column!! is null
1243
+ -- !x! if(is_true(!!su_exists!!))
1244
+ -- !x! sub_append ~fk_check and su.!!@uq_column!! is null
1245
+ -- !x! endif
1246
+ -- !x! sub_append ~fk_check and !!~s_not_null!!
1247
+ -- !x! sub_append ~fk_check group by !!~s_checked!!
1248
+
1249
+ -- Write the SQL to the log file if requested.
1250
+ -- !x! if(sub_defined(logfile))
1251
+ -- !x! andif(sub_defined(log_sql))
1252
+ -- !x! andif(is_true(!!log_sql!!))
1253
+ -- !x! write "SQL for foreign key check:" to !!logfile!!
1254
+ -- !x! write [!!~fk_check!!] to !!logfile!!
1255
+ -- !x! endif
1256
+
1257
+ -- Run the check.
1258
+ !!~fk_check!!;
1259
+ -- !x! if(hasrows(#ups_fk_check))
1260
+ -- !x! write " Foreign key error referencing !!@uq_table!!."
1261
+ if object_id('tempdb..#ups_ercnt') is not null drop table #ups_ercnt;
1262
+ select count(*) as ercnt
1263
+ into #ups_ercnt
1264
+ from #ups_fk_check;
1265
+ -- !x! subdata ~errcnt #ups_ercnt
1266
+ update #ups_fk_constraints
1267
+ set fkerror_rows = !!~errcnt!!
1268
+ where constraint_name = '!!~constraint_name!!';
1269
+ -- !x! if(sub_defined(logfile))
1270
+ -- !x! write " Foreign key errors in !!#table!! referencing !!@uq_table!!" to !!logfile!!
1271
+ -- !x! if(sub_defined(log_errors))
1272
+ -- !x! andif(is_true(!!log_errors!!))
1273
+ -- !x! export #ups_fk_check append to !!logfile!! as txt
1274
+ -- !x! endif
1275
+ -- !x! endif
1276
+ -- !x! if(is_true(!!#display_errors!!))
1277
+ -- !x! prompt message "Foreign key errors in !!#table!! referencing !!@uq_table!!" display #ups_fk_check
1278
+ -- !x! endif
1279
+ -- !x! endif
1280
+
1281
+
1282
+ -- Mark this constraint as processed.
1283
+ update #ups_fk_constraints
1284
+ set processed = 1
1285
+ where constraint_name = '!!~constraint_name!!';
1286
+
1287
+ -- Loop.
1288
+ -- !x! execute script fk_qa_one_innerloop with (staging=!!#staging!!, table=!!#table!!, display_errors=!!#display_errors!!)
1289
+
1290
+ -- !x! endif
1291
+
1292
+ -- !x! END SCRIPT
1293
+ -- #################### End of FK_QA_ONE ########################
1294
+ -- ################################################################
1295
+
1296
+
1297
+
1298
+ -- ################################################################
1299
+ -- Script UPSERT_ONE
1300
+ --
1301
+ -- Adds data from a staging table to a base table, using UPDATE
1302
+ -- and INSERT statements. Displays data to be modified to the
1303
+ -- user before any modifications are done. Reports the changes
1304
+ -- made to the console and optionally to a log file.
1305
+ --
1306
+ -- Input parameters:
1307
+ -- base_schema : The name of the base table schema.
1308
+ -- staging : The name of the staging schema.
1309
+ -- table : The table name--same for base and staging.
1310
+ -- exclude_cols : A comma-delimited list of single-quoted
1311
+ -- column names within enclosing double quotes,
1312
+ -- identifying the columns
1313
+ -- of the base table that are not to be
1314
+ -- modified. These may be autonumber
1315
+ -- columns or columns filled by triggers.
1316
+ -- display_changes : A boolean variable indicating whether
1317
+ -- or not the changes to be made to the
1318
+ -- base table should be displayed in a GUI.
1319
+ -- display_final : A boolean variable indicating whether or
1320
+ -- not the base table should be displayed
1321
+ -- after updates and inserts are completed.
1322
+ -- updcntvar : The name of a substitution variable that
1323
+ -- will be set to the number of rows updated.
1324
+ -- inscntvar : The name of a substitution variable that
1325
+ -- will be set to the number of rows inserted.
1326
+ --
1327
+ -- Global variables:
1328
+ -- logfile : The name of a log file to which update
1329
+ -- messages will be written. Optional.
1330
+ -- log_sql : A value of 'Yes' or 'No' indicating whether
1331
+ -- the update and insert statements should
1332
+ -- also be written to the logfile. Optional.
1333
+ -- log_changes : A value of 'Yes' or 'No' indicating whether
1334
+ -- the updated and inserted data should be
1335
+ -- written to the logfile. Optional.
1336
+ --
1337
+ -- Tables and views created or modified:
1338
+ -- ups_cols : temporary table
1339
+ -- ups_pks : temporary table
1340
+ -- ups_allcollist : temporary table
1341
+ -- ups_allbasecollist : temporary table
1342
+ -- ups_allstgcollist : temporary table
1343
+ -- ups_pkcollist : temporary table
1344
+ -- ups_joinexpr : temporary table
1345
+ -- ups_basematches : temporary table
1346
+ -- ups_stgmatches : temporary table
1347
+ -- ups_nk : temporary table
1348
+ -- ups_assexpr : temporary table
1349
+ -- ups_newrows : temporary table
1350
+ -- ===============================================================
1351
+
1352
+ -- !x! BEGIN SCRIPT UPSERT_ONE with parameters (base_schema, staging, table, exclude_cols, display_changes, display_final, updcntvar, inscntvar)
1353
+
1354
+ -- Remove substitution variables that will contain the generated
1355
+ -- update and insert statements so that the existence of valid
1356
+ -- statements can be later tested based on the existence of these variables.
1357
+ -- !x! rm_sub ~updatestmt
1358
+ -- !x! rm_sub ~insertstmt
1359
+
1360
+ -- !x! sub ~do_updates Yes
1361
+ -- !x! sub ~do_inserts Yes
1362
+
1363
+ -- !x! if(sub_defined(logfile))
1364
+ -- !x! write "" to !!logfile!!
1365
+ -- !x! write "==================================================================" to !!logfile!!
1366
+ -- !x! write "!!$current_time!! -- Performing upsert on table !!#base_schema!!.!!#table!!" to !!logfile!!
1367
+ -- !x! endif
1368
+
1369
+
1370
+ -- !x! write "Performing upsert on table !!#base_schema!!.!!#table!!"
1371
+
1372
+
1373
+ -- Validate inputs: base/staging schemas and table
1374
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
1375
+
1376
+
1377
+ -- Populate a (temporary) table with the names of the columns
1378
+ -- in the base table that are to be updated from the staging table.
1379
+ -- Include only those columns from staging table that are also in base table.
1380
+ -- !x! if(is_null("!!#exclude_cols!!"))
1381
+ -- !x! sub_empty ~col_excl
1382
+ -- !x! else
1383
+ -- !x! sub ~col_excl and column_name not in (!!#exclude_cols!!)
1384
+ -- !x! endif
1385
+ if object_id('tempdb..#ups_cols') is not null drop table #ups_cols;
1386
+ select s.column_name, s.ordinal_position
1387
+ into #ups_cols
1388
+ from
1389
+ information_schema.columns as s
1390
+ inner join information_schema.columns as b on s.column_name=b.column_name
1391
+ where
1392
+ s.table_schema = '!!#staging!!'
1393
+ and s.table_name = '!!#table!!'
1394
+ and b.table_schema = '!!#base_schema!!'
1395
+ and b.table_name = '!!#table!!'
1396
+ !!~col_excl!!
1397
+ ;
1398
+
1399
+
1400
+ -- Populate a (temporary) table with the names of the primary key
1401
+ -- columns of the base table.
1402
+ if object_id('tempdb..#ups_pks') is not null drop table #ups_pks;
1403
+ select k.column_name, k.ordinal_position
1404
+ into #ups_pks
1405
+ from information_schema.table_constraints as tc
1406
+ inner join information_schema.key_column_usage as k
1407
+ on tc.constraint_type = 'PRIMARY KEY'
1408
+ and tc.constraint_name = k.constraint_name
1409
+ and tc.constraint_catalog = k.constraint_catalog
1410
+ and tc.constraint_schema = k.constraint_schema
1411
+ and tc.table_schema = k.table_schema
1412
+ and tc.table_name = k.table_name
1413
+ and tc.constraint_name = k.constraint_name
1414
+ where
1415
+ k.table_name = '!!#table!!'
1416
+ and k.table_schema = '!!#base_schema!!'
1417
+ ;
1418
+
1419
+ -- Get all base table columns that are to be updated into a comma-delimited list.
1420
+ if object_id('tempdb..#ups_allcollist') is not null drop table #ups_allcollist;
1421
+ select
1422
+ cast(column_name as varchar(max)) as column_list,
1423
+ ordinal_position
1424
+ into #ups_allcollist
1425
+ from #ups_cols
1426
+ ;
1427
+ -- !x! sub_empty ~allcollist
1428
+ -- !x! execute script string_agg with(table_name=#ups_allcollist, string_col=column_list, order_col=ordinal_position, delimiter=", ", string_var=+allcollist)
1429
+
1430
+
1431
+ -- Get all base table columns that are to be updated into a comma-delimited list
1432
+ -- with a "b." prefix.
1433
+ if object_id('tempdb..#ups_allbasecollist') is not null drop table #ups_allbasecollist;
1434
+ select
1435
+ cast('b.' + column_name as varchar(max)) as column_list,
1436
+ ordinal_position
1437
+ into #ups_allbasecollist
1438
+ from #ups_cols;
1439
+ -- !x! sub_empty ~allbasecollist
1440
+ -- !x! execute script string_agg with(table_name=#ups_allbasecollist, string_col=column_list, order_col=ordinal_position, delimiter=", ", string_var=+allbasecollist)
1441
+
1442
+
1443
+ -- Get all staging table column names for columns that are to be updated
1444
+ -- into a comma-delimited list with an "s." prefix.
1445
+ if object_id('tempdb..#ups_allstgcollist') is not null drop table #ups_allstgcollist;
1446
+ select
1447
+ cast('s.' + column_name as varchar(max)) as column_list,
1448
+ ordinal_position
1449
+ into #ups_allstgcollist
1450
+ from #ups_cols;
1451
+ -- !x! sub_empty ~allstgcollist
1452
+ -- !x! execute script string_agg with(table_name=#ups_allstgcollist, string_col=column_list, order_col=ordinal_position, delimiter=", ", string_var=+allstgcollist)
1453
+
1454
+
1455
+ -- Get the primary key columns in a comma-delimited list.
1456
+ if object_id('tempdb..#ups_pkcollist') is not null drop table #ups_pkcollist;
1457
+ select
1458
+ cast(column_name as varchar(max)) as column_list,
1459
+ ordinal_position
1460
+ into #ups_pkcollist
1461
+ from #ups_pks;
1462
+ -- !x! sub_empty ~pkcollist
1463
+ -- !x! execute script string_agg with(table_name=#ups_pkcollist, string_col=column_list, order_col=ordinal_position, delimiter=", ", string_var=+pkcollist)
1464
+
1465
+
1466
+ -- Create a join expression for key columns of the base (b) and
1467
+ -- staging (s) tables.
1468
+ if object_id('tempdb..#ups_joinexpr') is not null drop table #ups_joinexpr;
1469
+ select
1470
+ cast('b.' + column_name + ' = s.' + column_name as varchar(max)) as column_list,
1471
+ ordinal_position
1472
+ into #ups_joinexpr
1473
+ from
1474
+ #ups_pks;
1475
+ -- !x! sub_empty ~joinexpr
1476
+ -- !x! execute script string_agg with(table_name=#ups_joinexpr, string_col=column_list, order_col=ordinal_position, delimiter=" and ", string_var=+joinexpr)
1477
+
1478
+
1479
+ -- Create a FROM clause for an inner join between base and staging
1480
+ -- tables on the primary key column(s).
1481
+ -- !x! sub ~fromclause FROM !!#base_schema!!.!!#table!! as b INNER JOIN !!#staging!!.!!#table!! as s ON !!~joinexpr!!
1482
+
1483
+
1484
+ -- Create SELECT queries to pull all columns with matching keys from both
1485
+ -- base and staging tables.
1486
+ if object_id('tempdb..#ups_basematches') is not null drop table #ups_basematches;
1487
+ select !!~allbasecollist!!
1488
+ into #ups_basematches
1489
+ !!~fromclause!!;
1490
+
1491
+ if object_id('tempdb..#ups_stgmatches') is not null drop table #ups_stgmatches;
1492
+ select !!~allstgcollist!!
1493
+ into #ups_stgmatches
1494
+ !!~fromclause!!;
1495
+
1496
+ --Get non-key columns to be updated
1497
+ if object_id('tempdb..#ups_nk') is not null drop table #ups_nk;
1498
+ select column_name
1499
+ into #ups_nk
1500
+ from
1501
+ (
1502
+ select column_name from #ups_cols
1503
+ except
1504
+ select column_name from #ups_pks
1505
+ ) as nk
1506
+ ;
1507
+
1508
+
1509
+ -- Prompt user to examine matching data and commit, don't commit, or quit.
1510
+ -- !x! if(hasrows(#ups_stgmatches))
1511
+ -- !x! andif(hasrows(#ups_nk))
1512
+ -- Prompt user to examine matching data and commit, don't commit, or quit.
1513
+ -- !x! if(is_true(!!#display_changes!!))
1514
+ -- !x! prompt ask "Do you want to make these changes? For table !!#table!!, new data are shown in the top table below; existing data are in the lower table." sub ~do_updates compare #ups_stgmatches and #ups_basematches key (!!~pkcollist!!)
1515
+ -- !x! endif
1516
+ -- !x! if(is_true(!!~do_updates!!))
1517
+ -- Create an assignment expression to update non-key columns of the
1518
+ -- base table (as b) from columns of the staging table (as s).
1519
+ if object_id('tempdb..#ups_assexpr') is not null drop table #ups_assexpr;
1520
+ select
1521
+ cast('b.' + column_name + ' = s.' + column_name as varchar(max)) as column_list
1522
+ into #ups_assexpr
1523
+ from #ups_nk;
1524
+ -- !x! sub_empty ~assexpr
1525
+ -- !x! execute script string_agg with(table_name=#ups_assexpr, string_col=column_list, order_col=column_list, delimiter=", ", string_var=+assexpr)
1526
+ -- Create an UPDATE statement to update the base table with
1527
+ -- non-key columns from the staging table. No semicolon terminating generated SQL.
1528
+ -- !x! sub ~updatestmt UPDATE b SET !!~assexpr!! FROM !!#base_schema!!.!!#table!! as b INNER JOIN !!#staging!!.!!#table!! as s on !!~joinexpr!!
1529
+ -- !x! endif
1530
+ -- !x! endif
1531
+
1532
+ -- Create a select statement to find all rows of the staging table
1533
+ -- that are not in the base table.
1534
+ if object_id('tempdb..#ups_newrows') is not null drop table #ups_newrows;
1535
+ with newpks as (
1536
+ select !!~pkcollist!! from !!#staging!!.!!#table!!
1537
+ except
1538
+ select !!~pkcollist!! from !!#base_schema!!.!!#table!!
1539
+ )
1540
+ select
1541
+ --s.*
1542
+ !!~allstgcollist!!
1543
+ into #ups_newrows
1544
+ from
1545
+ !!#staging!!.!!#table!! as s
1546
+ inner join newpks as b on !!~joinexpr!!;
1547
+
1548
+ -- Prompt user to examine new data and continue or quit.
1549
+ -- !x! if(hasrows(#ups_newrows))
1550
+ -- !x! if(is_true(!!#display_changes!!))
1551
+ -- !x! prompt ask "Do you want to add these new data to the !!#base_schema!!.!!#table!! table?" sub ~do_inserts display #ups_newrows
1552
+ -- !x! endif
1553
+
1554
+ -- !x! if(is_true(!!~do_inserts!!))
1555
+ -- Create an insert statement. No semicolon terminating generated SQL.
1556
+ -- !x! sub ~insertstmt INSERT INTO !!#base_schema!!.!!#table!! (!!~allcollist!!) SELECT !!~allcollist!! FROM #ups_newrows
1557
+ -- !x! endif
1558
+ -- !x! endif
1559
+
1560
+ -- Run the update and insert statements.
1561
+
1562
+
1563
+ -- !x! if(sub_defined(~updatestmt))
1564
+ -- !x! andif(is_true(!!~do_updates!!))
1565
+ -- !x! write "Updating !!#base_schema!!.!!#table!!"
1566
+ -- !x! if(sub_defined(logfile))
1567
+ -- !x! write "" to !!logfile!!
1568
+ -- !x! if(sub_defined(log_sql))
1569
+ -- !x! andif(is_true(!!log_sql!!))
1570
+ -- !x! write "UPDATE statement for !!#base_schema!!.!!#table!!:" to !!logfile!!
1571
+ -- !x! write [!!~updatestmt!!] to !!logfile!!
1572
+ -- !x! endif
1573
+ -- !x! if(sub_defined(log_changes))
1574
+ -- !x! andif(is_true(!!log_changes!!))
1575
+ -- !x! write "Updates:" to !!logfile!!
1576
+ -- !x! export #ups_stgmatches append to !!logfile!! as txt
1577
+ -- !x! endif
1578
+ -- !x! write "" to !!logfile!!
1579
+ -- !x! endif
1580
+ !!~updatestmt!!;
1581
+ -- !x! sub !!#updcntvar!! !!$last_rowcount!!
1582
+ -- !x! if(sub_defined(logfile))
1583
+ -- !x! write "!!$last_rowcount!! rows of !!#base_schema!!.!!#table!! updated." to !!logfile!!
1584
+ -- !x! endif
1585
+ -- !x! write " !!$last_rowcount!! rows updated."
1586
+ -- !x! endif
1587
+
1588
+ -- !x! if(sub_defined(~insertstmt))
1589
+ -- !x! andif(is_true(!!~do_inserts!!))
1590
+ -- !x! write "Adding data to !!#base_schema!!.!!#table!!"
1591
+ -- !x! if(sub_defined(logfile))
1592
+ -- !x! write "" to !!logfile!!
1593
+ -- !x! if(sub_defined(log_sql))
1594
+ -- !x! andif(is_true(!!log_sql!!))
1595
+ -- !x! write "INSERT statement for !!#base_schema!!.!!#table!!:" to !!logfile!!
1596
+ -- !x! write [!!~insertstmt!!] to !!logfile!!
1597
+ -- !x! endif
1598
+ -- !x! if(sub_defined(log_changes))
1599
+ -- !x! andif(is_true(!!log_changes!!))
1600
+ -- !x! write "New data:" to !!logfile!!
1601
+ -- !x! export #ups_newrows append to !!logfile!! as txt
1602
+ -- !x! endif
1603
+ -- !x! write "" to !!logfile!!
1604
+ -- !x! endif
1605
+ !!~insertstmt!!;
1606
+ -- !x! sub !!#inscntvar!! !!$last_rowcount!!
1607
+ -- !x! if(sub_defined(logfile))
1608
+ -- !x! write "!!$last_rowcount!! rows added to !!#base_schema!!.!!#table!!." to !!logfile!!
1609
+ -- !x! endif
1610
+ -- !x! write " !!$last_rowcount!! rows added."
1611
+ -- !x! endif
1612
+
1613
+ -- !x! if(is_true(!!#display_final!!))
1614
+ -- !x! prompt message "Table !!#base_schema!!.!!#table!! after updates and inserts." display !!#base_schema!!.!!#table!!
1615
+ -- !x! endif
1616
+
1617
+ -- !x! END SCRIPT
1618
+ -- ################### End of UPSERT_ONE ########################
1619
+ -- ################################################################
1620
+
1621
+
1622
+
1623
+
1624
+ -- ################################################################
1625
+ -- Script UPSERT_ALL
1626
+ --
1627
+ -- Updates multiple base tables with new or revised data from
1628
+ -- staging tables, using the UPSERT_ONE script.
1629
+ --
1630
+ -- Input parameters:
1631
+ -- base_schema : The name of the base table schema.
1632
+ -- staging : The name of the staging schema.
1633
+ -- control_table : The name of a table containing at least the
1634
+ -- following four columns:
1635
+ -- table_name : The name of a table
1636
+ -- to be updated.
1637
+ -- exclude_cols : A comma-delimited
1638
+ -- list of single-
1639
+ -- quoted column
1640
+ -- names, as required
1641
+ -- by UPDATE_ANY.
1642
+ -- display_changes : A value of "Yes" or
1643
+ -- "No" indicating
1644
+ -- whether the changes
1645
+ -- for the table should
1646
+ -- be displayed.
1647
+ -- display_final : A value of "Yes" or
1648
+ -- "No" indicating
1649
+ -- whether the final
1650
+ -- state of the table
1651
+ -- should be displayed.
1652
+ -- A table with these columns will be created
1653
+ -- by the script STAGED_TO_LOAD.
1654
+ --
1655
+ -- Global variables:
1656
+ -- logfile : The name of a log file to which update
1657
+ -- messages will be written. Optional.
1658
+ -- log_sql : A boolean variable indicating whether
1659
+ -- the update and insert statements should
1660
+ -- also be written to the logfile.
1661
+ -- upsert_progress_denom : Created or modified.
1662
+ --
1663
+ -- Tables and views used:
1664
+ -- ups_dependencies : temporary table
1665
+ -- ups_ordered_tables : temporary table
1666
+ -- ups_upsert_rows : temporary table
1667
+ --
1668
+ -- Counter variables:
1669
+ -- 221585944 : Progress indicator.
1670
+ --
1671
+ -- ===============================================================
1672
+
1673
+ -- !x! BEGIN SCRIPT UPSERT_ALL with parameters (base_schema, staging, control_table)
1674
+
1675
+
1676
+ -- Validate contents of control table
1677
+ -- !x! execute script validate_control with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!, script=!!$CURRENT_SCRIPT_NAME!!, script_line=!!$SCRIPT_LINE!!)
1678
+
1679
+ -- Initialize the status and progress bars if the console is running.
1680
+ -- !x! if(console_on)
1681
+ -- !x! reset counter 221585944
1682
+ -- !x! console status "Merging data"
1683
+ -- !x! console progress 0
1684
+ if object_id('tempdb..#ups_upsert_rows') is not null drop table #ups_upsert_rows;
1685
+ select count(*) + 1 as upsert_rows
1686
+ into #ups_upsert_rows
1687
+ from !!#control_table!!;
1688
+ -- !x! subdata upsert_progress_denom #ups_upsert_rows
1689
+ -- !x! endif
1690
+
1691
+ -- Get a table of all dependencies for the base schema.
1692
+ if object_id('tempdb..#ups_dependencies') is not null drop table #ups_dependencies;
1693
+ select
1694
+ tc.table_name as child,
1695
+ tp.table_name as parent
1696
+ into #ups_dependencies
1697
+ from
1698
+ information_schema.table_constraints as tc
1699
+ inner join information_schema.referential_constraints as cu on tc.constraint_name=cu.constraint_name
1700
+ inner join information_schema.table_constraints as tp on tp.constraint_name=cu.unique_constraint_name
1701
+ where
1702
+ tc.constraint_type = 'FOREIGN KEY'
1703
+ and tc.table_schema = '!!#base_schema!!'
1704
+ --Exclude cases where parent and child are same table (to protect against infinite recursion in table ordering)
1705
+ and tc.table_name<>tp.table_name;
1706
+
1707
+
1708
+ -- Create a list of tables in the base schema ordered by dependency.
1709
+ if object_id('tempdb..#ups_ordered_tables') is not null drop table #ups_ordered_tables;
1710
+ with dep_depth as (
1711
+ select
1712
+ dep.child as first_child,
1713
+ dep.child,
1714
+ dep.parent,
1715
+ 1 as lvl
1716
+ from
1717
+ #ups_dependencies as dep
1718
+ union all
1719
+ select
1720
+ dd.first_child,
1721
+ dep.child,
1722
+ dep.parent,
1723
+ dd.lvl + 1 as lvl
1724
+ from
1725
+ dep_depth as dd
1726
+ inner join #ups_dependencies as dep on dep.parent = dd.child
1727
+ and dep.child <> dd.parent
1728
+ and not (dep.parent = dd.first_child and dd.lvl > 2)
1729
+ )
1730
+ select
1731
+ table_name,
1732
+ table_order
1733
+ into
1734
+ #ups_ordered_tables
1735
+ from
1736
+ (
1737
+ --All parents
1738
+ select
1739
+ dd.parent as table_name,
1740
+ max(lvl) as table_order
1741
+ from
1742
+ dep_depth as dd
1743
+ group by
1744
+ dd.parent
1745
+ union
1746
+ --Children that are not parents
1747
+ select
1748
+ dd.child as table_name,
1749
+ max(lvl) + 1 as level
1750
+ from
1751
+ dep_depth as dd
1752
+ left join #ups_dependencies as dp on dp.parent = dd.child
1753
+ where
1754
+ dp.parent is null
1755
+ group by
1756
+ dd.child
1757
+ union
1758
+ --Neither parents nor children
1759
+ select distinct
1760
+ t.table_name,
1761
+ 0 as level
1762
+ from
1763
+ information_schema.tables as t
1764
+ left join #ups_dependencies as p on t.table_name=p.parent
1765
+ left join #ups_dependencies as c on t.table_name=c.child
1766
+ where
1767
+ t.table_schema = '!!#base_schema!!'
1768
+ and t.table_type = 'BASE TABLE'
1769
+ and p.parent is null
1770
+ and c.child is null
1771
+ ) as all_levels;
1772
+
1773
+
1774
+ -- Create a list of the selected tables with ordering information.
1775
+ if object_id('tempdb..#ups_proctables') is not null drop table #ups_proctables;
1776
+ select
1777
+ ot.table_order,
1778
+ tl.table_name,
1779
+ tl.exclude_cols,
1780
+ tl.display_changes,
1781
+ tl.display_final,
1782
+ tl.rows_updated,
1783
+ tl.rows_inserted,
1784
+ cast(0 as bit) as processed
1785
+ into
1786
+ #ups_proctables
1787
+ from
1788
+ !!#control_table!! as tl
1789
+ inner join #ups_ordered_tables as ot on ot.table_name = tl.table_name
1790
+ ;
1791
+
1792
+
1793
+ -- Create a selection (this would be a view if temp views were allowed) returning a single unprocessed table, in order.
1794
+ -- !x! begin script ups_get_toprocess
1795
+ if object_id('tempdb..#ups_toprocess') is not null drop table #ups_toprocess;
1796
+ select top 1
1797
+ table_name, exclude_cols,
1798
+ display_changes, display_final,
1799
+ rows_updated, rows_inserted
1800
+ into #ups_toprocess
1801
+ from #ups_proctables
1802
+ where not processed=1
1803
+ order by table_order;
1804
+ -- !x! end script
1805
+
1806
+
1807
+ -- Process all tables in order.
1808
+ -- !x! execute script upsert_all_innerloop with (base_schema=!!#base_schema!!, staging=!!#staging!!)
1809
+
1810
+ -- Move the update/insert counts back into the control table.
1811
+ update ct
1812
+ set
1813
+ rows_updated = pt.rows_updated,
1814
+ rows_inserted = pt.rows_inserted
1815
+ from !!#control_table!! as ct
1816
+ inner join #ups_proctables as pt
1817
+ on pt.table_name = ct.table_name;
1818
+
1819
+
1820
+ -- Update the status bar if the console is running.
1821
+ -- !x! if(console_on)
1822
+ -- !x! console status "Data merge complete"
1823
+ -- !x! console progress 0
1824
+ -- !x! endif
1825
+
1826
+
1827
+ -- !x! END SCRIPT
1828
+ -- UPSERT_ALL
1829
+ -- ****************************************************************
1830
+ -- ****************************************************************
1831
+ -- Script UPSERT_ALL_INNERLOOP
1832
+ -- ---------------------------------------------------------------
1833
+
1834
+ -- !x! BEGIN SCRIPT UPSERT_ALL_INNERLOOP with parameters (base_schema, staging)
1835
+
1836
+ -- Update the status bar if the console is running.
1837
+ -- !x! if(console_on)
1838
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
1839
+ -- !x! endif
1840
+
1841
+ -- !x! execute script ups_get_toprocess
1842
+ -- !x! if(hasrows(#ups_toprocess))
1843
+ -- Create local variables to store the row counts from updates and inserts.
1844
+ -- !x! sub ~rows_updated 0
1845
+ -- !x! sub ~rows_inserted 0
1846
+
1847
+ -- !x! select_sub #ups_toprocess
1848
+ -- !x! execute script upsert_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, exclude_cols=[!!@exclude_cols!!], display_changes=!!@display_changes!!, display_final=!!@display_final!!, updcntvar=+rows_updated, inscntvar=+rows_inserted)
1849
+
1850
+ update #ups_proctables
1851
+ set rows_updated = !!~rows_updated!!,
1852
+ rows_inserted = !!~rows_inserted!!
1853
+ where table_name = '!!@table_name!!';
1854
+
1855
+ update #ups_proctables
1856
+ set processed = cast(1 as bit)
1857
+ where table_name = '!!@table_name!!';
1858
+ -- !x! execute script upsert_all_innerloop with (base_schema=!!#base_schema!!, staging=!!#staging!!)
1859
+ -- !x! endif
1860
+
1861
+ -- !x! END SCRIPT
1862
+ -- ############### End of UPSERT_ALL_INNERLOOP ##################
1863
+ -- ################################################################
1864
+
1865
+
1866
+ -- ################################################################
1867
+ -- Script QA_ALL
1868
+ --
1869
+ -- Conducts null and foreign key checks on multiple staging tables
1870
+ -- containing new or revised data for staging tables, using the
1871
+ -- NULLQA_ONE, PKQA_ONE, and FKQA_ONE scripts.
1872
+ --
1873
+ -- Input parameters:
1874
+ -- base_schema : The name of the base table schema.
1875
+ -- staging : The name of the staging schema.
1876
+ -- control_table : The name of a table containing at least the
1877
+ -- following three columns:
1878
+ -- table_name : The name of a table
1879
+ -- to be updated.
1880
+ -- null_errors : For a comma-separated
1881
+ -- list of columns that
1882
+ -- are non-nullable in
1883
+ -- the base table but
1884
+ -- null in the staging
1885
+ -- table.
1886
+ -- pk_errors : For a count of the number
1887
+ -- of distinct primary key
1888
+ -- values that are duplicated,
1889
+ -- followed by the total row
1890
+ -- count for the duplicated keys.
1891
+ -- fk_errors : For a comma-separated
1892
+ -- list of foreign-key
1893
+ -- constraint names that
1894
+ -- are not met by the
1895
+ -- staging table.
1896
+ -- A table with these columns will be created
1897
+ -- by the script STAGED_TO_LOAD.
1898
+ --
1899
+ -- Global variables:
1900
+ -- logfile : The name of a log file to which error
1901
+ -- messages will be written. Optional.
1902
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
1903
+ -- the SQL that is generated for each foreign
1904
+ -- key check is written to the logfile. Optional.
1905
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
1906
+ -- foreign key errors are written to the logfile.
1907
+ -- Optional.
1908
+ --
1909
+ -- Tables and views used:
1910
+ -- ups_proctables : temporary table
1911
+ -- ups_toprocess : temporary table
1912
+ -- ups_upsert_rows : temporary table
1913
+ --
1914
+ -- Counters used:
1915
+ -- 221585944 : Progress bar position
1916
+ --
1917
+ -- Global variables modified:
1918
+ -- upsert_progress_denom : Created or redefined.
1919
+ --
1920
+ -- ===============================================================
1921
+
1922
+ -- !x! BEGIN SCRIPT QA_ALL with parameters (base_schema, staging, control_table)
1923
+
1924
+ -- Get denominator for progress bar if console is on.
1925
+ -- !x! if(console_on)
1926
+ if object_id('tempdb..#ups_upsert_rows') is not null drop table #ups_upsert_rows;
1927
+ select count(*) + 1 as upsert_rows
1928
+ into #ups_upsert_rows
1929
+ from !!#control_table!!;
1930
+ -- !x! subdata upsert_progress_denom #ups_upsert_rows
1931
+ -- !x! endif
1932
+
1933
+ -- Initialize the status and progress bars if the console is running.
1934
+ -- !x! begin script update_console_qa with parameters (check_type)
1935
+ -- !x! if(console_on)
1936
+ -- !x! reset counter 221585944
1937
+ -- !x! console status "Performing !!#check_type!! QA checks"
1938
+ -- !x! console progress 0
1939
+ -- !x! endif
1940
+ -- !x! end script
1941
+
1942
+ -- Create a list of the selected tables with a loop control flag.
1943
+ if object_id('tempdb..#ups_proctables ') is not null drop table #ups_proctables ;
1944
+ select
1945
+ tl.table_name,
1946
+ tl.exclude_null_checks,
1947
+ tl.display_changes,
1948
+ cast(0 as bit) as processed
1949
+ into #ups_proctables
1950
+ from
1951
+ !!#control_table!! as tl
1952
+ ;
1953
+
1954
+ -- Create a script returning a single unprocessed table, in order
1955
+ -- !x! begin script ups_qa_get_toprocess
1956
+ if object_id('tempdb..#ups_toprocess') is not null drop table #ups_toprocess;
1957
+ select top 1
1958
+ table_name, exclude_null_checks, display_changes
1959
+ into #ups_toprocess
1960
+ from #ups_proctables
1961
+ where not processed=1
1962
+ ;
1963
+ -- !x! end script
1964
+
1965
+ -- Perform null QA checks on all tables.
1966
+ -- !x! execute script update_console_qa with args (check_type=NULL)
1967
+ -- !x! execute script qa_all_nullloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1968
+
1969
+
1970
+ -- Perform primary QA checks on all tables.
1971
+ update #ups_proctables set processed = 0;
1972
+ -- !x! execute script update_console_qa with args (check_type=primary key)
1973
+ -- !x! execute script qa_all_pkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1974
+
1975
+ -- Perform foreign key QA checks on all tables.
1976
+ update #ups_proctables set processed = 0;
1977
+ -- !x! execute script update_console_qa with args (check_type=foreign key)
1978
+ -- !x! execute script qa_all_fkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1979
+
1980
+
1981
+ -- Update the status bar if the console is running.
1982
+ -- !x! if(console_on)
1983
+ -- !x! console status "Data QA checks complete"
1984
+ -- !x! console progress 0
1985
+ -- !x! endif
1986
+
1987
+
1988
+ -- !x! END SCRIPT
1989
+ -- QA_ALL
1990
+ -- ****************************************************************
1991
+ -- ****************************************************************
1992
+ -- Script QA_ALL_NULLLOOP
1993
+ -- ---------------------------------------------------------------
1994
+
1995
+ -- !x! BEGIN SCRIPT QA_ALL_NULLLOOP with parameters (base_schema, staging, control_table)
1996
+
1997
+ -- !x! sub_empty ~ups_null_error_list
1998
+
1999
+ -- Update the status bar if the console is running.
2000
+ -- !x! if(console_on)
2001
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
2002
+ -- !x! endif
2003
+
2004
+ -- !x! execute script ups_qa_get_toprocess
2005
+ -- !x! if(hasrows(#ups_toprocess))
2006
+ -- !x! select_sub #ups_toprocess
2007
+ -- !x! if(is_null("!!@exclude_null_checks!!"))
2008
+ -- !x! execute script nullqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, error_list=+ups_null_error_list)
2009
+ -- !x! else
2010
+ -- !x! execute script nullqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, error_list=+ups_null_error_list, exclude_null_checks=[!!@exclude_null_checks!!])
2011
+ -- !x! endif
2012
+ -- !x! if(not is_null("!!~ups_null_error_list!!"))
2013
+ update !!#control_table!!
2014
+ set null_errors = '!!~ups_null_error_list!!'
2015
+ where table_name = '!!@table_name!!';
2016
+ -- !x! endif
2017
+
2018
+ update #ups_proctables
2019
+ set processed = 1
2020
+ where table_name = '!!@table_name!!';
2021
+ -- !x! execute script qa_all_nullloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
2022
+ -- !x! endif
2023
+
2024
+
2025
+ -- !x! END SCRIPT
2026
+ -- QA_ALL_NULLLOOP
2027
+ -- ****************************************************************
2028
+ -- ****************************************************************
2029
+ -- Script QA_ALL_PKLOOP
2030
+ -- ---------------------------------------------------------------
2031
+
2032
+ -- !x! BEGIN SCRIPT QA_ALL_PKLOOP with parameters (base_schema, staging, control_table)
2033
+
2034
+ -- !x! sub_empty ~ups_pk_error_list
2035
+ -- Update the status bar if the console is running.
2036
+ -- !x! if(console_on)
2037
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
2038
+ -- !x! endif
2039
+
2040
+ -- !x! execute script ups_qa_get_toprocess
2041
+ -- !x! if(hasrows(#ups_toprocess))
2042
+ -- !x! select_sub #ups_toprocess
2043
+ -- !x! execute script pkqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, display_errors=!!@display_changes!!, error_list=+ups_pk_error_list)
2044
+ -- !x! if(not is_null("!!~ups_pk_error_list!!"))
2045
+ update !!#control_table!!
2046
+ set pk_errors = '!!~ups_pk_error_list!!'
2047
+ where table_name = '!!@table_name!!';
2048
+ -- !x! endif
2049
+
2050
+ update #ups_proctables
2051
+ set processed = 1
2052
+ where table_name = '!!@table_name!!';
2053
+ -- !x! execute script qa_all_pkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
2054
+ -- !x! endif
2055
+
2056
+
2057
+ -- !x! END SCRIPT
2058
+ -- QA_ALL_PKLOOP
2059
+ -- ****************************************************************
2060
+ -- ****************************************************************
2061
+ -- Script QA_ALL_FKLOOP
2062
+ -- ---------------------------------------------------------------
2063
+ -- !x! BEGIN SCRIPT QA_ALL_FKLOOP with parameters (base_schema, staging, control_table)
2064
+
2065
+ -- !x! sub_empty ~ups_error_list
2066
+
2067
+ -- Update the status bar if the console is running.
2068
+ -- !x! if(console_on)
2069
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
2070
+ -- !x! endif
2071
+
2072
+ -- !x! execute script ups_qa_get_toprocess
2073
+ -- !x! if(hasrows(#ups_toprocess))
2074
+ -- !x! select_sub #ups_toprocess
2075
+ -- !x! execute script fkqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, display_errors=!!@display_changes!!, error_list=+ups_error_list)
2076
+ -- !x! if(not is_null("!!~ups_error_list!!"))
2077
+ update !!#control_table!!
2078
+ set fk_errors = '!!~ups_error_list!!'
2079
+ where table_name = '!!@table_name!!';
2080
+ -- !x! endif
2081
+
2082
+ update #ups_proctables
2083
+ set processed = 1
2084
+ where table_name = '!!@table_name!!';
2085
+ -- !x! execute script qa_all_fkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
2086
+ -- !x! endif
2087
+
2088
+ -- !x! END SCRIPT
2089
+ -- ##################### End of QA_ALL ###########################
2090
+ -- #################################################################
2091
+
2092
+
2093
+ -- ################################################################
2094
+ -- Script UPDTPK_ONE
2095
+ --
2096
+ -- Updates primary keys in base table, based on new and older
2097
+ -- values of PK columns in a staging table, using UPDATE
2098
+ -- statements. Displays data to be modified to the
2099
+ -- user before any modifications are done. Reports the changes
2100
+ -- made to the console and optionally to a log file.
2101
+ --
2102
+ -- Input parameters:
2103
+ -- base_schema : The name of the base table schema.
2104
+ -- staging : The name of the staging schema.
2105
+ -- table : The table name--same for base and staging.
2106
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
2107
+ -- any errors should be displayed in a GUI.
2108
+ -- display_changes : A value of 'Yes' or 'No' to indicate whether
2109
+ -- or not the changes to be made to the
2110
+ -- base table should be displayed in a GUI.
2111
+ --
2112
+ -- Global variables:
2113
+ -- logfile : The name of a log file to which update
2114
+ -- messages will be written. Optional.
2115
+ -- log_sql : A value of 'Yes' or 'No' indicating whether
2116
+ -- the update and insert statements should
2117
+ -- also be written to the logfile. Optional.
2118
+ -- log_changes : A value of 'Yes' or 'No' indicating whether
2119
+ -- the updated and inserted data should be
2120
+ -- written to the logfile. Optional.
2121
+ --
2122
+ -- Tables and views created or modified:
2123
+ -- ups_pkqa_errors : temporary table
2124
+ -- ups_pkcollinfo : temporary table
2125
+ -- ups_pkupdates : temporary table
2126
+ -- ===============================================================
2127
+
2128
+ -- !x! BEGIN SCRIPT UPDTPK_ONE with parameters (base_schema, staging, table, display_errors, display_changes)
2129
+
2130
+ -- !x! if(console_on)
2131
+ -- !x! console status "Primary key updates"
2132
+ -- !x! endif
2133
+
2134
+ -- Validate inputs: base/staging schemas and table
2135
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
2136
+
2137
+ -- Write an initial header to the logfile.
2138
+ -- !x! if(sub_defined(logfile))
2139
+ -- !x! write "" to !!logfile!!
2140
+ -- !x! write "==================================================================" to !!logfile!!
2141
+ -- !x! write "!!$current_time!! -- Performing primary key updates on table !!#base_schema!!.!!#table!! from !!#staging!!.!!#table!!" to !!logfile!!
2142
+ -- !x! endif
2143
+
2144
+ -- !x! write "Performing primary key updates on table !!#base_schema!!.!!#table!! from !!#staging!!.!!#table!!"
2145
+
2146
+ -- Create a temp table to store the results of the PK update QA checks
2147
+ if object_id('tempdb..#ups_pkqa_errors') is not null drop table #ups_pkqa_errors;
2148
+ create table #ups_pkqa_errors (
2149
+ error_code varchar(40),
2150
+ error_description varchar(500)
2151
+ );
2152
+
2153
+ -- Populate a (temporary) table with the names of the primary key columns of the base table.
2154
+ -- Get the old and new primary key columns from staging table into various formats
2155
+ -- to use later to construct SQL statement to select records in various ways for both updates and QA checks.
2156
+ -- Include column lists, join expression, and where clause
2157
+ if object_id('tempdb..#ups_pkcol_info') is not null drop table #ups_pkcol_info;
2158
+ select
2159
+ k.table_schema,
2160
+ k.table_name,
2161
+ k.column_name,
2162
+ cast('b.' + column_name as varchar(max)) as base_aliased,
2163
+ cast('s.' + column_name as varchar(max)) as staging_aliased,
2164
+ cast('s.' + column_name + ' as staging_' + column_name as varchar(max)) as staging_aliased_prefix,
2165
+ cast('b.' + column_name + ' = s.' + column_name as varchar(max)) as join_expr,
2166
+ cast('new_' + column_name as varchar(max)) as newpk_col,
2167
+ cast('s.new_' + column_name as varchar(max)) as newpk_col_aliased,
2168
+ cast('new_' + column_name + ' is null' as varchar(max)) as newpk_col_empty,
2169
+ cast('new_' + column_name + ' is not null' as varchar(max)) as newpk_col_not_empty,
2170
+ cast('b.' + column_name + ' = s.new_' + column_name as varchar(max)) as assmt_expr,
2171
+ cast('s.new_' + column_name + ' = b.new_' + column_name as varchar(max)) as join_expr_new,
2172
+ k.ordinal_position
2173
+ into #ups_pkcol_info
2174
+ from information_schema.table_constraints as tc
2175
+ inner join information_schema.key_column_usage as k
2176
+ on tc.constraint_type = 'PRIMARY KEY'
2177
+ and tc.constraint_name = k.constraint_name
2178
+ and tc.constraint_catalog = k.constraint_catalog
2179
+ and tc.constraint_schema = k.constraint_schema
2180
+ and tc.table_schema = k.table_schema
2181
+ and tc.table_name = k.table_name
2182
+ and tc.constraint_name = k.constraint_name
2183
+ where
2184
+ k.table_name = '!!#table!!'
2185
+ and k.table_schema = '!!#base_schema!!'
2186
+ ;
2187
+
2188
+ -- Run QA checks
2189
+ -- !x! execute script UPDTPKQA_ONE with arguments(base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, pkinfo_table=#ups_pkcol_info, qaerror_table=#ups_pkqa_errors, display_errors=!!#display_errors!!)
2190
+
2191
+
2192
+ -- Run the PK update ONLY if QA check script returned no errors
2193
+ -- !x! if(not hasrows(#ups_pkqa_errors))
2194
+ -- !x! rm_sub ~updatestmt
2195
+
2196
+ -- !x! sub ~do_updates Yes
2197
+
2198
+ -- !x! if(sub_defined(logfile))
2199
+ -- !x! write "" to !!logfile!!
2200
+ -- !x! write "==================================================================" to !!logfile!!
2201
+ -- !x! write "!!$current_time!! -- Performing primary key update on table !!#base_schema!!.!!#table!!" to !!logfile!!
2202
+ -- !x! endif
2203
+
2204
+ -- !x! if(console_on)
2205
+ -- !x! console status "Performing PK updates"
2206
+ -- !x! console progress 0
2207
+ -- !x! endif
2208
+
2209
+ -- !x! write "Performing primary key update on table !!#base_schema!!.!!#table!!"
2210
+
2211
+ -- Get list of old primary key columns prefixed with alias
2212
+ -- !x! sub_empty ~oldpk_cols
2213
+ -- !x! execute script string_agg with(table_name=#ups_pkcol_info, string_col=base_aliased, order_col=ordinal_position, delimiter=", ", string_var=+oldpk_cols)
2214
+
2215
+ -- Get list of columns containing new primary key values
2216
+ -- !x! sub_empty ~newpk_cols
2217
+ -- !x! execute script string_agg with(table_name=#ups_pkcol_info, string_col=newpk_col, order_col=ordinal_position, delimiter=", ", string_var=+newpk_cols)
2218
+
2219
+ -- Create a join expression for an inner join between base and staging
2220
+ -- !x! sub_empty ~joinexpr
2221
+ -- !x! execute script string_agg with(table_name=#ups_pkcol_info, string_col=join_expr, order_col=ordinal_position, delimiter=" and ", string_var=+joinexpr)
2222
+
2223
+ -- Create a FROM clause for an inner join between base and staging
2224
+ -- tables on the primary key column(s).
2225
+ -- !x! sub ~fromclause FROM !!#base_schema!!.!!#table!! as b INNER JOIN !!#staging!!.!!#table!! as s ON !!~joinexpr!!
2226
+
2227
+
2228
+ -- Create a WHERE clause for the rows to include in the selection (only those having new PK columns populated in the staging table)
2229
+ -- !x! sub_empty ~wherecondition
2230
+ -- !x! execute script string_agg with(table_name=#ups_pkcol_info, string_col=newpk_col_not_empty, order_col=ordinal_position, delimiter=" and ", string_var=+wherecondition)
2231
+ -- !x! sub ~whereclause WHERE !!~wherecondition!!
2232
+
2233
+
2234
+ -- Select all matches for PK update into temp table
2235
+ if object_id('tempdb..#ups_pkupdates') is not null drop table #ups_pkupdates;
2236
+ select
2237
+ !!~oldpk_cols!!,
2238
+ !!~newpk_cols!!
2239
+ into #ups_pkupdates
2240
+ !!~fromclause!!
2241
+ !!~whereclause!!
2242
+ ;
2243
+
2244
+
2245
+ -- Prompt user to examine matching data and commit, don't commit, or quit.
2246
+ -- !x! if(hasrows(#ups_pkupdates))
2247
+ -- !x! if(is_true(!!#display_changes!!))
2248
+ -- !x! prompt ask "Do you want to make these changes to primary key values for table !!#table!!?" sub ~do_updates display #ups_pkupdates
2249
+ -- !x! endif
2250
+ -- !x! if(is_true(!!~do_updates!!))
2251
+ -- Create an assignment expression to update key columns of the
2252
+ -- base table (as b) from "new_" columns of the staging table (as s).
2253
+ -- !x! sub_empty ~assmt_expr
2254
+ -- !x! execute script string_agg with(table_name=#ups_pkcol_info, string_col=assmt_expr, order_col=ordinal_position, delimiter=", ", string_var=+assmt_expr)
2255
+
2256
+ -- Create an UPDATE statement to update PK columns of the base table with
2257
+ -- "new" PK columns from the staging table. No semicolon terminating generated SQL.
2258
+ -- !x! sub ~updatestmt UPDATE b SET !!~assmt_expr!! FROM !!#base_schema!!.!!#table!! as b INNER JOIN !!#staging!!.!!#table!! as s on !!~joinexpr!! !!~whereclause!!
2259
+
2260
+
2261
+ -- !x! write "Updating !!#base_schema!!.!!#table!!"
2262
+ -- !x! if(sub_defined(logfile))
2263
+ -- !x! write "" to !!logfile!!
2264
+ -- !x! if(sub_defined(log_sql))
2265
+ -- !x! andif(is_true(!!log_sql!!))
2266
+ -- !x! write "UPDATE statement for !!#base_schema!!.!!#table!!:" to !!logfile!!
2267
+ -- !x! write [!!~updatestmt!!] to !!logfile!!
2268
+ -- !x! endif
2269
+ -- !x! if(sub_defined(log_changes))
2270
+ -- !x! andif(is_true(!!log_changes!!))
2271
+ -- !x! write "Updates:" to !!logfile!!
2272
+ -- !x! export #ups_pkupdates append to !!logfile!! as txt
2273
+ -- !x! endif
2274
+ -- !x! write "" to !!logfile!!
2275
+ -- !x! endif
2276
+ !!~updatestmt!!;
2277
+ -- -- !x! sub !!#updcntvar!! !!$last_rowcount!!
2278
+ -- !x! if(sub_defined(logfile))
2279
+ -- !x! write "!!$last_rowcount!! rows of !!#base_schema!!.!!#table!! updated." to !!logfile!!
2280
+ -- !x! endif
2281
+ -- !x! write " !!$last_rowcount!! rows updated."
2282
+ -- !x! endif
2283
+ -- !x! else
2284
+ --!x! write "No primary key updates specified for existing records in !!#base_schema!!.!!#table!!"
2285
+ -- !x! endif
2286
+ -- !x! endif
2287
+
2288
+
2289
+ -- !x! END SCRIPT
2290
+ -- ################### End of UPDTPK_ONE ########################
2291
+ -- ################################################################
2292
+
2293
+
2294
+
2295
+ -- ################################################################
2296
+ -- Script UPDTPKQA_ONE
2297
+ --
2298
+ -- Performs QA checks on requested primary key updates to a table,
2299
+ -- based on old and new values of the table's primary key columns
2300
+ -- in a staging table.
2301
+ --
2302
+ -- Input parameters:
2303
+ -- base_schema : The name of the base table schema.
2304
+ -- staging : The name of the staging schema.
2305
+ -- table : The table name--same for base and staging.
2306
+ -- pkinfo_table : The name of a temporary table to be passed by
2307
+ -- the caller that contains information about the table PK,
2308
+ -- including strings to be used in constructing
2309
+ -- SQL for checks
2310
+ -- qaerror_table : The name of a temporary table to
2311
+ -- store any errors found by QA checks.
2312
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
2313
+ -- any errors should be displayed in a GUI.
2314
+ -- Output parameters:
2315
+ -- error_list : The name of the variable to receive FILL IN.
2316
+ --
2317
+ -- Global variables:
2318
+ -- logfile : The name of a log file to which update
2319
+ -- messages will be written. Optional.
2320
+ -- log_sql : A value of 'Yes' or 'No' indicating whether
2321
+ -- the update and insert statements should
2322
+ -- also be written to the logfile. Optional.
2323
+ -- Currently only writes SQL for foreign key checks
2324
+ -- (final check) to log.
2325
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
2326
+ -- errors are written to the logfile. Optional.
2327
+ --
2328
+ -- Tables and views created or modified:
2329
+ -- ups_missing_pk_cols : temporary table
2330
+ -- ups_any_pk_cols : temporary table
2331
+ -- ===============================================================
2332
+
2333
+ -- !x! BEGIN SCRIPT UPDTPKQA_ONE with parameters (base_schema, staging, table, pkinfo_table, qaerror_table, display_errors)
2334
+
2335
+
2336
+ -- Write an initial header to the logfile.
2337
+ -- !x! if(sub_defined(logfile))
2338
+ -- !x! write "" to !!logfile!!
2339
+ -- !x! write "==================================================================" to !!logfile!!
2340
+ -- !x! write "!!$current_time!! -- QA checks for primary key updates on table !!#table!!" to !!logfile!!
2341
+ -- !x! endif
2342
+
2343
+ -- !x! write "Conducting QA checks on table !!#staging!!.!!#table!! for primary key updates to table !!#base_schema!!.!!#table!!"
2344
+
2345
+ -- Initialize the status and progress bars if the console is running.
2346
+ -- !x! if(console_on)
2347
+ -- !x! console status "QA checks for PK updates on !!#base_schema!!.!!#table!!"
2348
+ -- !x! endif
2349
+
2350
+
2351
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2352
+ -- Check 1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2353
+ -- No primary key constraint on base table
2354
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2355
+ -- !x! if(not hasrows(!!#pkinfo_table!!))
2356
+
2357
+ -- !x! sub ~error_description No primary key constraint on base table !!#base_schema!!.!!#table!!
2358
+ -- !x! write " !!~error_description!!"
2359
+ -- !x! if(sub_defined(logfile))
2360
+ -- !x! write "" to !!logfile!!
2361
+ -- !x! write "!!~error_description!!" to !!logfile!!
2362
+ -- !x! endif
2363
+ insert into !!#qaerror_table!! (error_code, error_description)
2364
+ values ('No PK on base table', '!!~error_description!!')
2365
+ ;
2366
+
2367
+ -- No other QA checks are conducted if this check fails:
2368
+ -- Remaining QA checks are conducted ONLY if base table has PK
2369
+ -- !x! else
2370
+
2371
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2372
+ -- Check 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2373
+ -- A "new" PK column exists in staging table for every PK column of base table
2374
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2375
+
2376
+ -- Find any MISSING PK columns in staging table
2377
+ if object_id('tempdb..#ups_missing_pk_cols') is not null drop table #ups_missing_pk_cols;
2378
+ select
2379
+ newpk_col, ordinal_position
2380
+ into #ups_missing_pk_cols
2381
+ from
2382
+ --Base table PK columns, with expected name in staging table ("new_" prepended to column name)
2383
+ !!#pkinfo_table!! as pk
2384
+ --Staging table columns
2385
+ left join
2386
+ (
2387
+ select table_name, column_name
2388
+ from information_schema.columns
2389
+ where
2390
+ table_schema = '!!#staging!!'
2391
+ ) as stag on pk.table_name=stag.table_name and pk.newpk_col=stag.column_name
2392
+ where
2393
+ stag.column_name is null
2394
+ ;
2395
+
2396
+ -- !x! if(hasrows(#ups_missing_pk_cols))
2397
+
2398
+ -- !x! sub_empty ~error_info
2399
+ -- !x! execute script string_agg with(table_name=#ups_missing_pk_cols, string_col=newpk_col, order_col=ordinal_position, delimiter=", ", string_var=+error_info)
2400
+
2401
+ -- !x! sub ~error_description New primary key column(s) missing from staging table: !!~error_info!!
2402
+
2403
+ -- !x! write " !!~error_description!!"
2404
+ -- !x! if(sub_defined(logfile))
2405
+ -- !x! write "" to !!logfile!!
2406
+ -- !x! write "!!~error_description!!" to !!logfile!!
2407
+ -- !x! endif
2408
+ insert into !!#qaerror_table!! (error_code, error_description)
2409
+ values ('Missing new PK column(s)', '!!~error_description!!')
2410
+ ;
2411
+
2412
+ -- No other QA checks are conducted if this check fails:
2413
+ -- Remaining QA checks are all conducted ONLY if all expected "new PK" columns exist in staging table
2414
+ -- !x! else
2415
+
2416
+ -- Library of aggregated strings used to construct SQL for the remaining checks
2417
+
2418
+ -- "Old" PK columns, column names only
2419
+ -- !x! sub_empty ~old_pkcol
2420
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=column_name, order_col=ordinal_position, delimiter=", ", string_var=+old_pkcol)
2421
+
2422
+ -- "Old" PK columns from aliased staging table
2423
+ -- !x! sub_empty ~old_pkcol_aliased
2424
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=staging_aliased, order_col=ordinal_position, delimiter=", ", string_var=+old_pkcol_aliased)
2425
+
2426
+ -- "Old" PK columns from aliased staging table, with a prefix indicating they're from staging table
2427
+ -- !x! sub_empty ~old_pkcol_aliased_prefix
2428
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=staging_aliased_prefix, order_col=ordinal_position, delimiter=", ", string_var=+old_pkcol_aliased_prefix)
2429
+
2430
+ -- "New" PK columns from staging table
2431
+ -- !x! sub_empty ~new_pkcol
2432
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=newpk_col, order_col=ordinal_position, delimiter=", ", string_var=+new_pkcol)
2433
+
2434
+ -- "NEW" PK columns from aliased staging table
2435
+ -- !x! sub_empty ~new_pkcol_aliased
2436
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=newpk_col_aliased, order_col=ordinal_position, delimiter=", ", string_var=+new_pkcol_aliased)
2437
+
2438
+ -- Just base table
2439
+ -- !x! sub ~base_table !!#base_schema!!.!!#table!!
2440
+
2441
+ -- Just staging table
2442
+ -- !x! sub ~staging_table !!#staging!!.!!#table!!
2443
+
2444
+ -- Join condition: Base to staging on original (not "new_") PK columns
2445
+ -- !x! sub_empty ~joincond_origorig
2446
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=join_expr, order_col=ordinal_position, delimiter=" and ", string_var=+joincond_origorig)
2447
+
2448
+ -- Join condition: Base ORIG PK to staging NEW PK
2449
+ -- !x! sub_empty ~joincond_oldnew
2450
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=assmt_expr, order_col=ordinal_position, delimiter=" and ", string_var=+joincond_oldnew)
2451
+
2452
+ -- Join condition: Two instances of NEW PK columns from staging table
2453
+ -- !x! sub_empty ~joincond_newnew
2454
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=join_expr_new, order_col=ordinal_position, delimiter=" and ", string_var=+joincond_newnew)
2455
+
2456
+ -- Clause: ANY new PK cols populated
2457
+ -- !x! sub_empty ~any_newpk_col_not_empty
2458
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=newpk_col_not_empty, order_col=ordinal_position, delimiter=" or ", string_var=+any_newpk_col_not_empty)
2459
+
2460
+ -- Clause: ALL new PK cols populated
2461
+ -- !x! sub_empty ~all_newpk_col_not_empty
2462
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=newpk_col_not_empty, order_col=ordinal_position, delimiter=" and ", string_var=+all_newpk_col_not_empty)
2463
+
2464
+ -- Clause: ANY new PK cols empty
2465
+ -- !x! sub_empty ~any_newpk_col_empty
2466
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=newpk_col_empty, order_col=ordinal_position, delimiter=" or ", string_var=+any_newpk_col_empty)
2467
+
2468
+ -- Clause: ALL new PK cols empty
2469
+ -- !x! sub_empty ~all_newpk_col_empty
2470
+ -- !x! execute script string_agg with(table_name=!!#pkinfo_table!!, string_col=newpk_col_empty, order_col=ordinal_position, delimiter=" and ", string_var=+all_newpk_col_empty)
2471
+
2472
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2473
+ -- Check 3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2474
+ -- There are any rows with PK updates specified.
2475
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2476
+
2477
+ -- Find any populated new PK columns in staging table
2478
+ if object_id('tempdb..#ups_any_pk_cols') is not null drop table #ups_any_pk_cols;
2479
+ select * into #ups_any_pk_cols
2480
+ from !!~staging_table!!
2481
+ where !!~any_newpk_col_not_empty!!;
2482
+ -- !x! if(not hasrows(#ups_any_pk_cols))
2483
+ -- !x! sub ~error_description No primary key updates specified in !!#staging!!.!!#table!!
2484
+ -- !x! write " !!~error_description!!"
2485
+ -- !x! if(sub_defined(logfile))
2486
+ -- !x! write "" to !!logfile!!
2487
+ -- !x! write "!!~error_description!!" to !!logfile!!
2488
+ -- !x! endif
2489
+ insert into !!#qaerror_table!! (error_code, error_description)
2490
+ values ('No PK updates specified in staging table', '!!~error_description!!')
2491
+ ;
2492
+ -- No other QA checks are conducted if this check fails
2493
+ -- !x! else
2494
+
2495
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2496
+ -- Check 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2497
+ -- Where any "new" PK column is populated in the staging table, they are all populated.
2498
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2499
+
2500
+ -- Construct SQL statement looking for any NULLs in "new" PK columns in rows where any PK columns are populated
2501
+ -- Find any EMPTY PK columns in staging table
2502
+ if object_id('tempdb..#ups_empty_pk_cols') is not null drop table #ups_empty_pk_cols;
2503
+ select
2504
+ !!~old_pkcol!!,
2505
+ !!~new_pkcol!!
2506
+ into #ups_empty_pk_cols
2507
+ from
2508
+ !!~staging_table!!
2509
+ where
2510
+ not (!!~all_newpk_col_empty!!)
2511
+ and (!!~any_newpk_col_empty!!)
2512
+ ;
2513
+
2514
+ -- !x! if(hasrows(#ups_empty_pk_cols))
2515
+ if object_id('tempdb..#ups_empty_pk_cols_rwcnt') is not null drop table #ups_empty_pk_cols_rwcnt;
2516
+ select count(*) as rwcnt
2517
+ into #ups_empty_pk_cols_rwcnt
2518
+ from #ups_empty_pk_cols
2519
+ ;
2520
+ -- !x! subdata ~rowcount #ups_empty_pk_cols_rwcnt
2521
+ -- !x! sub ~error_description Missing values in new PK columns in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2522
+ -- !x! write " !!~error_description!!"
2523
+ insert into !!#qaerror_table!! (error_code, error_description)
2524
+ values ('Incomplete mapping', '!!~error_description!!')
2525
+ ;
2526
+ -- !x! if(sub_defined(logfile))
2527
+ -- !x! write "" to !!logfile!!
2528
+ -- !x! write "!!~error_description!!" to !!logfile!!
2529
+ -- !x! if(sub_defined(log_errors))
2530
+ -- !x! andif(is_true(!!log_errors!!))
2531
+ -- !x! export #ups_empty_pk_cols append to !!logfile!! as txt
2532
+ -- !x! endif
2533
+ -- !x! endif
2534
+ -- !x! if(is_true(!!#display_errors!!))
2535
+ -- !x! prompt message "Missing values in new PK columns in !!#staging!!.!!#table!!" display #ups_empty_pk_cols
2536
+ -- !x! endif
2537
+ -- !x! endif
2538
+
2539
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2540
+ -- Check 5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2541
+ -- Where any "new" PK column is populated in the staging table, the value of the original PK for that row is valid
2542
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2543
+
2544
+ -- New PK col in staging table are not empty
2545
+ if object_id('tempdb..#ups_old_pks_wc') is not null drop table #ups_old_pks_wc;
2546
+ select top 1 base_aliased
2547
+ into #ups_old_pks_wc
2548
+ from !!#pkinfo_table!!
2549
+ order by ordinal_position;
2550
+ -- !x! subdata ~old_pk_firstcol #ups_old_pks_wc
2551
+
2552
+
2553
+ if object_id('tempdb..#ups_invalid_old_pks') is not null drop table #ups_invalid_old_pks;
2554
+ select
2555
+ !!~old_pkcol_aliased!!,
2556
+ !!~new_pkcol!!
2557
+ into #ups_invalid_old_pks
2558
+ from !!~staging_table!! as s
2559
+ left join !!~base_table!! as b on !!~joincond_origorig!!
2560
+ where !!~all_newpk_col_not_empty!! and !!~old_pk_firstcol!! is null
2561
+ ;
2562
+
2563
+ -- !x! if(hasrows(#ups_invalid_old_pks))
2564
+ if object_id('tempdb..#ups_invld_pk_rwcnt') is not null drop table #ups_invld_pk_rwcnt;
2565
+ select count(*) as rwcnt
2566
+ into #ups_invld_pk_rwcnt
2567
+ from #ups_invalid_old_pks
2568
+ ;
2569
+ -- !x! subdata ~rowcount #ups_invld_pk_rwcnt
2570
+ -- !x! sub ~error_description Invalid original PK in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2571
+ -- !x! write " !!~error_description!!"
2572
+ insert into !!#qaerror_table!! (error_code, error_description)
2573
+ values ('Invalid old PK value', '!!~error_description!!')
2574
+ ;
2575
+ -- !x! if(sub_defined(logfile))
2576
+ -- !x! write "" to !!logfile!!
2577
+ -- !x! write "!!~error_description!!" to !!logfile!!
2578
+ -- !x! if(sub_defined(log_errors))
2579
+ -- !x! andif(is_true(!!log_errors!!))
2580
+ -- !x! export #ups_invalid_old_pks append to !!logfile!! as txt
2581
+ -- !x! endif
2582
+ -- !x! endif
2583
+ -- !x! if(is_true(!!#display_errors!!))
2584
+ -- !x! prompt message "Invalid original PK in !!#staging!!.!!#table!!" display #ups_invalid_old_pks
2585
+ -- !x! endif
2586
+ -- !x! endif
2587
+
2588
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2589
+ -- Check 6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2590
+ -- None of the "new" PK values already exist in the base table
2591
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2592
+ if object_id('tempdb..#ups_existing_new_pks') is not null drop table #ups_existing_new_pks;
2593
+ select
2594
+ !!~old_pkcol_aliased_prefix!!,
2595
+ !!~new_pkcol!!,
2596
+ b.*
2597
+ into #ups_existing_new_pks
2598
+ from !!~staging_table!! as s
2599
+ inner join !!~base_table!! as b on !!~joincond_oldnew!!
2600
+ ;
2601
+
2602
+ -- !x! if(hasrows(#ups_existing_new_pks))
2603
+ if object_id('tempdb..#ups_exst_nwpk_rwcnt') is not null drop table #ups_exst_nwpk_rwcnt;
2604
+ select count(*) as rwcnt
2605
+ into #ups_exst_nwpk_rwcnt
2606
+ from #ups_existing_new_pks
2607
+ ;
2608
+ -- !x! subdata ~rowcount #ups_exst_nwpk_rwcnt
2609
+ -- !x! sub ~error_description New PK already exists in !!#base_schema!!.!!#table!!: !!~rowcount!! row(s)
2610
+ -- !x! write " !!~error_description!!"
2611
+ insert into !!#qaerror_table!! (error_code, error_description)
2612
+ values ('Existing new PK value', '!!~error_description!!')
2613
+ ;
2614
+ -- !x! if(sub_defined(logfile))
2615
+ -- !x! write "" to !!logfile!!
2616
+ -- !x! write "!!~error_description!!" to !!logfile!!
2617
+ -- !x! if(sub_defined(log_errors))
2618
+ -- !x! andif(is_true(!!log_errors!!))
2619
+ -- !x! export #ups_existing_new_pks append to !!logfile!! as txt
2620
+ -- !x! endif
2621
+ -- !x! endif
2622
+ -- !x! if(is_true(!!#display_errors!!))
2623
+ -- !x! prompt message "New PK already exists in !!#base_schema!!.!!#table!!" display #ups_existing_new_pks
2624
+ -- !x! endif
2625
+ -- !x! endif
2626
+
2627
+
2628
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2629
+ -- Check 7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2630
+ -- No two (or more) original PK values map to same new PK value
2631
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2632
+ if object_id('tempdb..#ups_pk_mapping_conflict') is not null drop table #ups_pk_mapping_conflict;
2633
+ select
2634
+ !!~old_pkcol_aliased!!,
2635
+ !!~new_pkcol_aliased!!
2636
+ into #ups_pk_mapping_conflict
2637
+ from !!~staging_table!! as s
2638
+ inner join
2639
+ (
2640
+ select
2641
+ !!~new_pkcol!!
2642
+ from
2643
+ (select distinct !!~old_pkcol!!, !!~new_pkcol!! from !!~staging_table!! where !!~all_newpk_col_not_empty!!) as a
2644
+ group by
2645
+ !!~new_pkcol!!
2646
+ having count(*) >1
2647
+ ) as b on !!~joincond_newnew!!
2648
+ ;
2649
+
2650
+ -- !x! if(hasrows(#ups_pk_mapping_conflict))
2651
+ if object_id('tempdb..#ups_map_conf_rwcnt') is not null drop table #ups_map_conf_rwcnt;
2652
+ select count(*) as rwcnt
2653
+ into #ups_map_conf_rwcnt
2654
+ from #ups_pk_mapping_conflict
2655
+ ;
2656
+ -- !x! subdata ~rowcount #ups_map_conf_rwcnt
2657
+ -- !x! sub ~error_description Multiple original PKs mapped to same new PK in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2658
+ -- !x! write " !!~error_description!!"
2659
+ insert into !!#qaerror_table!! (error_code, error_description)
2660
+ values ('Mapping conflict', '!!~error_description!!')
2661
+ ;
2662
+ -- !x! if(sub_defined(logfile))
2663
+ -- !x! write "" to !!logfile!!
2664
+ -- !x! write "!!~error_description!!" to !!logfile!!
2665
+ -- !x! if(sub_defined(log_errors))
2666
+ -- !x! andif(is_true(!!log_errors!!))
2667
+ -- !x! export #ups_pk_mapping_conflict append to !!logfile!! as txt
2668
+ -- !x! endif
2669
+ -- !x! endif
2670
+ -- !x! if(is_true(!!#display_errors!!))
2671
+ -- !x! prompt message "Multiple original PKs mapped to same new PK in !!#staging!!.!!#table!!" display #ups_pk_mapping_conflict
2672
+ -- !x! endif
2673
+ -- !x! endif
2674
+
2675
+
2676
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2677
+ -- Check 8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2678
+ -- No single original PK value maps to multiple new PK values
2679
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2680
+
2681
+ if object_id('tempdb..#ups_pk_duplicate_keys') is not null drop table #ups_pk_duplicate_keys;
2682
+ select
2683
+ !!~old_pkcol_aliased!!,
2684
+ !!~new_pkcol_aliased!!
2685
+ into #ups_pk_duplicate_keys
2686
+ from !!~staging_table!! as s
2687
+ inner join
2688
+ (
2689
+ select
2690
+ !!~old_pkcol!!
2691
+ from
2692
+ (select distinct !!~old_pkcol!!, !!~new_pkcol!! from !!~staging_table!! where !!~all_newpk_col_not_empty!!) as a
2693
+ group by
2694
+ !!~old_pkcol!!
2695
+ having count(*)>1
2696
+ ) as b on !!~joincond_origorig!!
2697
+ ;
2698
+
2699
+ -- !x! if(hasrows(#ups_pk_duplicate_keys))
2700
+ if object_id('tempdb..#ups_dup_key_rwcnt') is not null drop table #ups_dup_key_rwcnt;
2701
+ select count(*) as rwcnt
2702
+ into #ups_dup_key_rwcnt
2703
+ from #ups_pk_duplicate_keys
2704
+ ;
2705
+ -- !x! subdata ~rowcount #ups_dup_key_rwcnt
2706
+ -- !x! sub ~error_description Original PK mapped to multiple new PKs in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2707
+ -- !x! write " !!~error_description!!"
2708
+ insert into !!#qaerror_table!! (error_code, error_description)
2709
+ values ('Duplicate keys', '!!~error_description!!')
2710
+ ;
2711
+ -- !x! if(sub_defined(logfile))
2712
+ -- !x! write "" to !!logfile!!
2713
+ -- !x! write "!!~error_description!!" to !!logfile!!
2714
+ -- !x! if(sub_defined(log_errors))
2715
+ -- !x! andif(is_true(!!log_errors!!))
2716
+ -- !x! export #ups_pk_duplicate_keys append to !!logfile!! as txt
2717
+ -- !x! endif
2718
+ -- !x! endif
2719
+ -- !x! if(is_true(!!#display_errors!!))
2720
+ -- !x! prompt message "Original PK mapped to multiple new PKs in !!#staging!!.!!#table!!" display #ups_pk_duplicate_keys
2721
+ -- !x! endif
2722
+ -- !x! endif
2723
+
2724
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2725
+ -- Check 9 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2726
+ -- If any of the PK columns reference a parent table, all the "new" values of that column are valid
2727
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2728
+
2729
+ -- Get ALL column references for the base table
2730
+ if object_id('tempdb..#ups_fkcol_refs') is not null drop table #ups_fkcol_refs;
2731
+ select
2732
+ object_name(fk.constraint_object_id) as fk_constraint,
2733
+ '!!#staging!!' as staging_schema,
2734
+ schema_name(t.schema_id) as table_schema,
2735
+ t.name as table_name,
2736
+ cc.name as column_name,
2737
+ cc.column_id,
2738
+ schema_name(op.schema_id) as parent_schema,
2739
+ object_name(referenced_object_id) as parent_table,
2740
+ cp.name as parent_column,
2741
+ cp.column_id as parent_column_id
2742
+ into #ups_fkcol_refs
2743
+ from
2744
+ sys.tables as t
2745
+ inner join sys.foreign_key_columns as fk on fk.parent_object_id=t.object_id
2746
+ inner join sys.columns as cc on fk.parent_object_id=cc.object_id and fk.parent_column_id=cc.column_id
2747
+ inner join sys.columns as cp on fk.referenced_object_id=cp.object_id and fk.referenced_column_id=cp.column_id
2748
+ inner join sys.objects as op on op.object_id=cp.object_id
2749
+ where
2750
+ schema_name(t.schema_id)='!!#base_schema!!'
2751
+ and t.name = '!!#table!!'
2752
+ ;
2753
+
2754
+ -- Narrow the list down to ONLY dependencies that affect PK columns
2755
+ -- Include not just the PK columns themselves, but ALL columns included in FKs
2756
+ -- that include ANY PK columns (probably rare/unlikely that a non-PK column would be
2757
+ -- part of the same foreign key as a PK column, but this ensures that ALL columns of the FK
2758
+ -- are included, whether or not the column is part of the PK)
2759
+ if object_id('tempdb..#ups_pkcol_deps') is not null drop table #ups_pkcol_deps;
2760
+ select
2761
+ refs.*
2762
+ into #ups_pkcol_deps
2763
+ from
2764
+ #ups_fkcol_refs as refs
2765
+ inner join
2766
+ --Distinct list of FK constraints on the table that include ANY PK columns
2767
+ (
2768
+ select distinct
2769
+ fk_constraint, r.table_schema, r.table_name
2770
+ from
2771
+ #ups_fkcol_refs as r
2772
+ inner join #ups_pkcol_info as p on r.table_schema=p.TABLE_SCHEMA and r.table_name=p.table_name and r.column_name=p.column_name
2773
+ ) as const on refs.fk_constraint=const.fk_constraint and refs.table_schema=const.table_schema and refs.table_name=const.table_name
2774
+ ;
2775
+
2776
+ -- Create a control table for looping to check each fk
2777
+ -- Include (for later use) some of the constructed strings that apply to the entire PK (not
2778
+ -- just the FK being checked)
2779
+ if object_id('tempdb..#ups_pkfk_ctrl') is not null drop table #ups_pkfk_ctrl;
2780
+ select
2781
+ fk_constraint,
2782
+ staging_schema, table_schema, table_name, parent_schema, parent_table,
2783
+ min(parent_column) as any_referenced_column,
2784
+ '!!~old_pkcol_aliased!!' as old_pkcol_aliased,
2785
+ '!!~new_pkcol!!' as new_pkcol,
2786
+ '!!~all_newpk_col_not_empty!!' as all_newpk_col_not_empty,
2787
+ cast(0 as bit) as processed
2788
+ into #ups_pkfk_ctrl
2789
+ from #ups_pkcol_deps
2790
+ group by
2791
+ fk_constraint,
2792
+ staging_schema, table_schema, table_name, parent_schema, parent_table;
2793
+
2794
+ -- Create a script to select one constraint to process
2795
+ -- !x! begin script ups_get_next_fk
2796
+ if object_id('tempdb..#ups_next_fk') is not null drop table #ups_next_fk;
2797
+ select top 1 *
2798
+ into #ups_next_fk
2799
+ from #ups_pkfk_ctrl
2800
+ where not processed=1;
2801
+ -- !x! end script
2802
+
2803
+ --Process all constraints: check every foreign key
2804
+ --!x! execute script updtpkqa_one_innerloop with (qaerror_table=!!#qaerror_table!!, display_errors=!!#display_errors!!)
2805
+ -- !x! endif
2806
+ -- !x! endif
2807
+ -- !x! endif
2808
+ -- !x! END SCRIPT
2809
+ -- ################### UPDTPKQA_ONE ########################
2810
+ -- ################################################################
2811
+ -- Script UPDTPKQA_ONE_INNERLOOP
2812
+ -- ----------------------------------------------------------------
2813
+ -- !x! BEGIN SCRIPT UPDTPKQA_ONE_INNERLOOP with parameters(qaerror_table, display_errors)
2814
+
2815
+ -- !x! execute script ups_get_next_fk
2816
+ -- !x! if(hasrows(#ups_next_fk))
2817
+
2818
+ -- !x! select_sub #ups_next_fk
2819
+
2820
+ -- Compile FK info for the selected constraint
2821
+ if object_id('tempdb..#ups_sel_fk_cols') is not null drop table #ups_sel_fk_cols;
2822
+ select
2823
+ staging_schema, fk_constraint, table_schema, table_name, column_name, column_id, parent_schema,
2824
+ parent_table, parent_column, parent_column_id,
2825
+ 's.new_' + column_name + '=' + 'b.' + parent_column as join_condition
2826
+ into #ups_sel_fk_cols
2827
+ from #ups_pkcol_deps
2828
+ where fk_constraint='!!@fk_constraint!!'
2829
+ ;
2830
+
2831
+ -- Construct SQL to check the selected FK
2832
+
2833
+ -- !x! sub_empty ~referenced_cols
2834
+ -- !x! execute script string_agg with (table_name=#ups_sel_fk_cols, string_col=parent_column, order_col=column_id, delimiter=", ", string_var=+referenced_cols)
2835
+
2836
+ -- !x! sub_empty ~join_condition
2837
+ -- !x! execute script string_agg with (table_name=#ups_sel_fk_cols, string_col=join_condition, order_col=column_id, delimiter=" and ", string_var=+join_condition)
2838
+
2839
+
2840
+ -- !x! sub ~select_stmt select !!@old_pkcol_aliased!!, !!@new_pkcol!! into #ups_pk_fk_check from !!@staging_schema!!.!!@table_name!! as s
2841
+ -- !x! sub ~join_stmt left join !!@parent_schema!!.!!@parent_table!! as b on !!~join_condition!!
2842
+ -- !x! sub ~where_clause where !!@all_newpk_col_not_empty!! and b.!!@any_referenced_column!! is null
2843
+
2844
+ -- !x! sub ~fk_check if object_id('tempdb..#ups_pk_fk_check') is not null drop table #ups_pk_fk_check;
2845
+ -- !x! sub_append ~fk_check !!~select_stmt!!
2846
+ -- !x! sub_append ~fk_check !!~join_stmt!!
2847
+ -- !x! sub_append ~fk_check !!~where_clause!!
2848
+
2849
+ -- Write the SQL to the log file if requested.
2850
+ -- !x! if(sub_defined(logfile))
2851
+ -- !x! andif(sub_defined(log_sql))
2852
+ -- !x! andif(is_true(!!log_sql!!))
2853
+ -- !x! write "" to !!logfile!!
2854
+ -- !x! write "SQL for checking foreign key !!@fk_constraint!! for PK update to !!@table_schema!!.!!@table_name!!:" to !!logfile!!
2855
+ -- !x! write [!!~fk_check!!] to !!logfile!!
2856
+ -- !x! endif
2857
+
2858
+ -- Run the check
2859
+ !!~fk_check!!;
2860
+
2861
+ -- !x! if(hasrows(#ups_pk_fk_check))
2862
+
2863
+ if object_id('tempdb..#ups_pk_fk_check_rwcnt') is not null drop table #ups_pk_fk_check_rwcnt;
2864
+ select count(*) as rwcnt
2865
+ into #ups_pk_fk_check_rwcnt
2866
+ from #ups_pk_fk_check
2867
+ ;
2868
+ -- !x! subdata ~rowcount #ups_pk_fk_check_rwcnt
2869
+ -- !x! sub ~error_description !!@parent_schema!!.!!@parent_table!! (!!~referenced_cols!!): !!~rowcount!! row(s)
2870
+
2871
+ -- !x! write " Violation of foreign key !!@fk_constraint!! in new primary key columns in !!@staging_schema!!.!!@table_name!! referencing !!@parent_schema!!.!!@parent_table!!: !!~rowcount!! row(s)"
2872
+
2873
+ if object_id('tempdb..#ups_pk_fk_qa_error') is not null drop table #ups_pk_fk_qa_error;
2874
+ select
2875
+ error_code, error_description
2876
+ into #ups_pk_fk_qa_error
2877
+ from !!#qaerror_table!!
2878
+ where error_code='Invalid reference to parent table(s)';
2879
+ -- !x! if(hasrows(#ups_pk_fk_qa_error))
2880
+ update !!#qaerror_table!!
2881
+ set error_description=error_description + '; !!~error_description!!'
2882
+ where error_code='Invalid reference to parent table(s)';
2883
+
2884
+ -- !x! else
2885
+ insert into !!#qaerror_table!! (error_code, error_description)
2886
+ values ('Invalid reference to parent table(s)', '!!~error_description!!')
2887
+ ;
2888
+ -- !x! endif
2889
+
2890
+
2891
+ -- !x! if(sub_defined(logfile))
2892
+ -- !x! write "" to !!logfile!!
2893
+ -- !x! write "Violation of foreign key !!@fk_constraint!! in new primary key columns in !!@staging_schema!!.!!@table_name!! referencing !!@parent_schema!!.!!@parent_table!!: !!~rowcount!! row(s)" to !!logfile!!
2894
+ -- !x! if(sub_defined(log_errors))
2895
+ -- !x! andif(is_true(!!log_errors!!))
2896
+ -- !x! export #ups_pk_fk_check append to !!logfile!! as txt
2897
+ -- !x! endif
2898
+ -- !x! endif
2899
+ -- !x! if(is_true(!!#display_errors!!))
2900
+ -- !x! prompt message "Violation of foreign key !!@fk_constraint!! in new primary key columns in !!@staging_schema!!.!!@table_name!! referencing !!@parent_schema!!.!!@parent_table!!" display #ups_pk_fk_check
2901
+ -- !x! endif
2902
+
2903
+ -- !x! endif
2904
+
2905
+ -- Mark constraint as processed
2906
+ update #ups_pkfk_ctrl
2907
+ set processed=1
2908
+ where fk_constraint='!!@fk_constraint!!';
2909
+
2910
+ --LOOP
2911
+ -- !x! execute script updtpkqa_one_innerloop with (qaerror_table=!!#qaerror_table!!, display_errors=!!#display_errors!!)
2912
+
2913
+ -- !x! endif
2914
+
2915
+ -- !x! END SCRIPT
2916
+ -- #################### End of UPDTPKQA_ONE ########################
2917
+ -- ################################################################