def input_function( params, table ):
       
     # Pick all targets that have more than one person
     with get_temp_table( self.db_context ) as temp_table_1, get_temp_table( self.db_context ) as temp_table_2:
         query = """
         SELECT DISTINCT {parentgroups}, {targetgroups}
         INTO {temp_table_1:qualified}
         FROM {table:qualified}
         WHERE ({where}) AND ({wheret})
             AND {parentgroups} IS NOT NULL
             AND {targetgroups} IS NOT NULL
         GROUP BY {parentgroups}, {targetgroups}
         HAVING COUNT(1) > 1
         """.format( table=table, temp_table_1=temp_table_1, **params )
         self.db_context.executeNoResults(query)
           
         # From those, pick the first target in each parent
         query= """
         SELECT {parentgroups}, {targetgroups},
         ROW_NUMBER() OVER( PARTITION BY {parentgroups} ORDER BY {targetgroups} ) AS r1
         INTO {temp_table_2:qualified}
         FROM {temp_table_1:qualified}
         """.format( temp_table_1=temp_table_1, temp_table_2=temp_table_2, **params )
         self.db_context.executeNoResults(query)
           
         # For each selected target, pick the first two observations
         in_ds = get_temp_table( self.db_context )
         query = """
         SELECT {columns:itemfmt='C.{{}}'}
         INTO {in_ds}
         FROM (
             SELECT {columns:itemfmt='A.{{}}'},
             ROW_NUMBER() OVER( PARTITION BY A.{parentgroups}, A.{targetgroups} ORDER BY A.{targetgroups} ) AS r2
             FROM (
                 SELECT {columns}
                 FROM {table:qualified}
                 WHERE ({where}) AND ({wheret})
                     AND {parentgroups} IS NOT NULL
                     AND {targetgroups} IS NOT NULL
             ) AS A INNER JOIN (
                 SELECT {parentgroups}, {targetgroups}
                 FROM {temp_table_2:qualified}
                 WHERE r1=1
             ) AS B
             ON A.{parentgroups}=B.{parentgroups} AND A.{targetgroups}=B.{targetgroups}
         ) AS C
         WHERE C.r2<=2
         """.format( in_ds=in_ds, temp_table_2=temp_table_2,
                     table=table,
                     columns=Joiner( table ), **params )
         self.db_context.executeNoResults(query)
     in_ds.populate_from_connection()
     return in_ds
Пример #2
0
 def execute( self ):
     
     self.run_context.debug( "Running ttests" )
     with get_temp_table( self.db_context ) as ttest_input_table, get_temp_table( self.db_context ) as ttest_output_table:
         ttest_level = TTestLevel( ds_in=ttest_input_table, ds_out=ttest_output_table, db_context=self.db_context,
                                   round_value=1 )
         
         for target_level in self.target_levels:
             drop_table_if_exists( target_level.output_table )
             self.db_context.executeNoResults( target_level.output_table.definition )
             where_t = target_level.get_where_expression()
             self.db_context.executeNoResults( _FILL_OUTPUT_TABLE_QUERY.format(
                     output_table=target_level.output_table,
                     input_table=self.ds_in,
                     target_id=target_level.output_table[ target_level.id ],
                     where_t=where_t,
             ))
             vars_target = set( target_level.where )
             vars_target.add( target_level.id )
             for parent_level in target_level.contents:
                 where_p = parent_level.get_where_expression()
                 vars_parent = set( parent_level.where )
                 vars_parent.add( parent_level.id )
                 vars_parent.update( vars_target )
                 for row in parent_level.contents:
                     vars_row = set( vars_parent )
                     vars_row.add( row.inputvar )
                     
                     self.run_context.debug( "Running ttest for variable {} comparing {} to {}".format(
                             row.inputvar, target_level.level, parent_level.level ) )
                     drop_table_if_exists( ttest_input_table )
                     query = _GET_INPUT_VARIABLES_QUERY.format( ds_in=self.ds_in, ttest_input_table=ttest_input_table,
                             vars=Joiner( vars_row ),
                             where_t=where_t, where_p=where_p )
                     self.db_context.executeNoResults( query )
                     
                     ttest_level.input_col_name=row.inputvar
                     ttest_level.output_col_name=row.outputvar
                     ttest_level.critical_value=row.critval
                     ttest_level.target_group_cols=[ target_level.id ]
                     ttest_level.parent_group_cols=[ parent_level.id ]
                     ttest_level.target_where_expression = where_t
                     ttest_level.parent_where_expression = where_p
                     
                     ttest_level.execute()
                     
                     query = _ACCUMULATE_RESULTS_QUERY.format( output_table=target_level.output_table,
                             ttest_table=ttest_output_table, output_var=row.outputvar, target_id=target_level.id )
                     self.db_context.executeNoResults( query )
    def test1(self):
        run_context = SuiteContext('unittest')
        db_context = run_context.getDBContext('remote')
        self.LOGGER = run_context.get_logger()

        # Set up a test table definition
        with get_temp_table(db_context) as table:
            for i in xrange(100):
                table.add(FieldSpec("col_{}".format(i), 'NVARCHAR', 8))

            fts = FastTableStream(table, use_names=False, raw=True)
            data = [[unicode(100 * j + i) for i in xrange(100)]
                    for j in xrange(1000)]

            def do_write():
                with fts:
                    for j in xrange(5):
                        fts.write_many(data)

            pr = cProfile.Profile()
            pr.enable()
            pr.runcall(do_write)
            pr.disable()

            filename = os.path.join(
                run_context.logs_dir,
                'fast_table_write_remote_unicode_raw_profile.txt')
            with open(filename, 'w') as f:
                stats = pstats.Stats(pr, stream=f)
                stats.print_stats()
            self.LOGGER.info("Table name {}".format(table))
Пример #4
0
    def _process_required_key_dups_and_missing(self, db_context, input_table,
                                               side, allow_dups):

        # Define duplicates table
        dup_table = get_temp_table(db_context)
        dup_table.create_foreign_key(input_table, True, 'fk_{}_'.format(side))
        dup_table.add_all(self.required_merge_keys)
        dup_table.add(FieldSpec(basic_type="TINYINT", field_name="has_dups"))
        dup_table.add(FieldSpec(basic_type="TINYINT",
                                field_name="has_missing"))
        dup_table.add(
            FieldSpec(basic_type="TINYINT", field_name="has_dups_both"))
        db_context.executeNoResults(dup_table.definition)

        # Populate table
        query = _FIND_DUPLICATES_QUERY.format(
            dup_table=dup_table,
            input_table=input_table,
            foreign_key=Joiner(dup_table.foreign_keys[0]),
            required_key=Joiner(self.required_merge_keys),
            side=side)
        db_context.executeNoResults(query)

        # Define rejects table
        reject_table = get_temp_table(db_context)
        reject_table.create_foreign_key(input_table, True,
                                        'fk_{}_'.format(side))
        reject_table.add_all(self.required_merge_keys)
        reject_table.add(
            FieldSpec(basic_type="NVARCHAR",
                      data_length=4000,
                      field_name="merge_report"))
        db_context.executeNoResults(reject_table.definition)

        # Move missing keys to rejects table
        self._move_rejects(dup_table, reject_table, 0, "has_missing > 0",
                           "'Missing required key on {}'".format(side))

        # If required, move duplicates to rejects table
        if not allow_dups:
            self._move_rejects(dup_table, reject_table, 0, "has_dups > 0",
                               "'Duplicate required key on {}'".format(side))

        return dup_table, reject_table
Пример #5
0
    def create_output( self, parent_summary, target_summary ):
        
        self.ds_out.add_all( self.parent_group_cols )
        self.ds_out.add_all( self.extra_target_group_cols )

        with get_temp_table(self.db_context) as ds_temp:
            ds_temp.add_all( self.ds_out )
            ds_temp.add( FieldSpec( field_name="numerator", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="ttest_se", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="ttest_value", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="p_css", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="t_css", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="p_t", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="t_t", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="p_n", basic_type="FLOAT" ) )
            ds_temp.add( FieldSpec( field_name="t_n", basic_type="FLOAT" ) )
            self.db_context.executeNoResults( ds_temp.definition )
            
            query = _TTEST_QUERY_1.format(
                    ds_out=ds_temp,
                    group_cols=Joiner( self.parent_group_cols, self.extra_target_group_cols ),
                    parent_summary=parent_summary,
                    target_summary=target_summary,
                    parent_group_cols=Joiner( self.parent_group_cols),
                    target_group_cols=Joiner( self.target_group_cols) )
            self.db_context.executeNoResults( query )
            
            query = _TTEST_QUERY_2.format( ds_out=ds_temp )
            self.db_context.executeNoResults( query )
    
            query = _TTEST_QUERY_3.format( ds_out=ds_temp )
            self.db_context.executeNoResults( query )
    
            query = _TTEST_QUERY_4.format( ds_out=ds_temp )
            self.db_context.executeNoResults( query )
    
            query = _TTEST_QUERY_5.format( ds_out=ds_temp,
                                           critval=self.critical_value )
            self.db_context.executeNoResults( query )
    
            query = _TTEST_QUERY_6.format( ds_out=ds_temp,
                                           critval=self.critical_value,
                                           output_col=self.output_col )
            self.db_context.executeNoResults( query )
            
            query = _TTEST_QUERY_7.format( ds_out=ds_temp,
                                           rrv=self.reciprocal_round_value,
                                           output_col=self.output_col )
            self.db_context.executeNoResults( query )
            
            self.db_context.executeNoResults( self.ds_out.definition )
            query = _FINAL_OUTPUT_QUERY.format( ds_out=self.ds_out,
                                                ds_temp=ds_temp,
                                                columns=Joiner( self.ds_out ) )
            self.db_context.executeNoResults( query )
    def setUp(self):

        self.run_context = SuiteContext('unittest')
        self.db_context = self.run_context.getDBContext('unittest')
        self.LOGGER = self.run_context.get_logger()

        # Set up a test table definition
        self.table = get_temp_table(self.db_context)
        (self.table.add(FieldSpec("col1", "NVARCHAR", 8)).add(
            FieldSpec("col2", "FLOAT")).add(FieldSpec("col3", "TINYINT")).add(
                FieldSpec("col4", "INT")).add(FieldSpec("col5", "BIGINT")))
Пример #7
0
 def generate_summary_stats( self, where_expression, *args ):
     # (SAS 188-192; 228-233)
     group_cols = Joiner( *args )
     summary_table = get_temp_table( self.db_context )
     query = _SUMMARY_QUERY.format( input_col=self.input_col,
                                    summary_table=summary_table,
                                    ds_in=self.ds_in,
                                    group_cols=group_cols,
                                    where=where_expression )
     self.db_context.executeNoResults( query )
     summary_table.populate_from_connection
     return summary_table
 def test_13(self):
     # data locds;
     #     set ttest.g03;
     #     where courtesyTestedFlag=0 and TransferFlag=0 and AttemptFlagCRTmath=1;
     # run;
     with get_temp_table( self.db_context ) as loc_ds:
         g03=self.static_context.getTableSpec( 'g03' )
         query="""
             SELECT *
             INTO {loc_ds}
             FROM {table:qualified}
             WHERE courtesyTestedFlag=0 AND TransferFlag=0 AND AttemptFlagCRTmath=1;
         """.format( loc_ds=loc_ds, table=g03)
         self.db_context.executeNoResults(query)
         ttest = TTestLevel(ds_in=loc_ds,
                            ds_out="test13_testresult",
                            db_context=self.db_context,
                            input_col_name='upmxscor',
                            output_col_name='outvar',
                            target_group_cols=['schoolcode'],
                            parent_group_cols=['areacode'],
                            critical_value=1.96,
                            round_value=1 )
         ttest.execute()
     
     test_columns = (
         ( 'outvar', 'outvar', integer_compare ),
         ( 'schoolcode', 'schoolcode', mixed_compare ),
         ( 'areacode', 'areacode', mixed_compare ),
     )
     
     table_key_function = lambda row:( row.areacode, row.schoolcode )
     specimen_key_function = lambda row:( row[ 'areacode' ],
                                          row[ 'schoolcode' ] )
     
     answer_dir = os.path.join( self.answer_dir, 'test_13' )
     if not os.path.exists( answer_dir ):
         os.makedirs( answer_dir )
     answer_file = os.path.join( answer_dir, 'comparison.log' )
     result = compare_tables(
             answer_file,
             table="test13_testresult",
             specimen_name= os.path.join( self.specimen_dir, 'test_13/testresult.XLS' ),
             columns=test_columns,
             table_key_function=table_key_function,
             specimen_key_function=specimen_key_function,
             db_context=self.db_context)
     
     self.assertTrue( result, "Test 13 FAILED" )
    def test_1(self):
        # Replicates the SAS test 1
        g3 = self.static_context.getTableSpec('student_g3')
        with get_temp_table(self.db_context) as in_ds:
            query = "SELECT TOP 100 * INTO {in_ds:qualified} FROM {g3:qualified}".format(
                in_ds=in_ds, g3=g3)
            self.db_context.executeNoResults(query)
            query = "UPDATE {} REPLACE [ucrx_teachername]='---CLARK PATRISE---' WHERE [import_order]=98"
            out_ds = self.db_context.getTableSpec('g3_ready')
            id_generator = IDGenerator(ds_in=in_ds,
                                       ds_out=out_ds,
                                       db_context=self.db_context,
                                       grade_var='grade',
                                       district_var='dcrxid_attend',
                                       school_var='bcrxid_attend',
                                       subject_char_lst=SUBJECT_LST,
                                       teacher_var_lst=TEACHER_LST,
                                       teacher_label_lst=TEACHER_LABEL_LST,
                                       teacher_id_lst=TEACHER_ID_LST,
                                       class_var_lst=CLASS_LST,
                                       section_var_lst=SECTION_LST,
                                       class_label_lst=CLASS_LABEL_LST,
                                       class_id_lst=CLASS_ID_LST,
                                       test_date='0509',
                                       err_var_name='errvar')
            id_generator.execute()

        key_function = lambda row: (row.studentid
                                    if row.studentid is not None else 0,
                                    int(row.serial_number), row.ssid
                                    if row.ssid is not None else '')

        answer_dir = os.path.join(self.answer_dir, 'test_10')
        if not os.path.exists(answer_dir):
            os.makedirs(answer_dir)
        answer_file = os.path.join(answer_dir, 'comparison.log')
        result = compare_tables(answer_file,
                                table="g3_ready",
                                specimen_name=os.path.join(
                                    self.specimen_dir, 'G3_READY.XLS'),
                                columns=COLUMNS,
                                table_key_function=key_function,
                                specimen_key_function=key_function,
                                db_context=self.db_context)
        self.assertTrue(
            result,
            "Table comparison failed. See log in {}".format(answer_file))
    def assemble_input_data_n_in_group( self, n_in_group, params, table ):

        in_ds = get_temp_table( self.db_context )
        query = """
        SELECT {columns:itemfmt='A.{{}}'}
        INTO {in_ds}
        FROM (
            SELECT {columns},
            DENSE_RANK() OVER( PARTITION BY {parentgroups} ORDER BY {targetgroups} ) AS r1,
            ROW_NUMBER() OVER( PARTITION BY {parentgroups}, {targetgroups} ORDER BY {targetgroups} ) AS r2
            FROM {table_name:qualified}
            WHERE ({where}) AND ({wheret})
                AND {parentgroups} IS NOT NULL
                AND {targetgroups} IS NOT NULL
        ) AS A
        WHERE A.r1<={n_in_group} AND A.r2=1
        """.format( table_name=table,
                    columns=Joiner( table ),
                    n_in_group=n_in_group,
                    in_ds=in_ds,
                    **params )
        self.db_context.executeNoResults(query)
        in_ds.populate_from_connection()
        return in_ds
Пример #11
0
    def _create_merge_table(self):
        merge_table = get_temp_table(self.db_context)
        merge_table.add_all(self.required_merge_keys)
        i = 1
        left_fields = []
        right_fields = []
        for key in self.optional_merge_keys:
            lkey = MergeFieldSpec(key.left_field, None, PRIORITY_LEFT_ONLY)
            lkey.field_name = "LOptKey_" + str(i)
            merge_table.add(lkey)
            left_fields.append(lkey)
            rkey = MergeFieldSpec(None, key.right_field, PRIORITY_RIGHT_ONLY)
            rkey.field_name = "ROptKey_" + str(i)
            merge_table.add(rkey)
            right_fields.append(rkey)
            i += 1

        i = 1
        for keyset in self.fuzzy_merge_keys:
            if isinstance(keyset, MergeFieldSpec):
                keyset = (keyset, None)
            if len(keyset) == 1:
                keyset = (keyset[0], None)
            if len(keyset) != 2:
                raise ValueError(
                    "Fuzzy keys must be supplied singly or in pairs; received {}"
                    .format(len(keyset)))
            similarity_column = FieldSpec(field_name="Similarity_{}".format(i),
                                          basic_type="FLOAT")
            merge_table.add(similarity_column)
            j = 1
            for key in keyset:
                if key is None:
                    lkey = MergeFieldSpec(None, None, PRIORITY_LEFT_ONLY)
                    lkey.field_name = "LFuzzyKey_{}_{}".format(i, j)
                    lkey.basic_type = "NVARCHAR"
                    lkey.data_length = 1
                    merge_table.add(lkey)
                    left_fields.append(lkey)
                    rkey = MergeFieldSpec(None, None, PRIORITY_RIGHT_ONLY)
                    rkey.field_name = "RFuzzyKey_{}_{}".format(i, j)
                    rkey.basic_type = "NVARCHAR"
                    rkey.data_length = 1
                    merge_table.add(rkey)
                    right_fields.append(rkey)
                else:
                    lkey = MergeFieldSpec(key.left_field, None,
                                          PRIORITY_LEFT_ONLY)
                    lkey.field_name = "LFuzzyKey_{}_{}".format(i, j)
                    merge_table.add(lkey)
                    left_fields.append(lkey)
                    rkey = MergeFieldSpec(None, key.right_field,
                                          PRIORITY_RIGHT_ONLY)
                    rkey.field_name = "RFuzzyKey_{}_{}".format(i, j)
                    merge_table.add(rkey)
                    right_fields.append(rkey)
                j += 1

            i += 1

        merge_table.create_foreign_key(self.left_input_table, True, 'fk_left_')
        merge_table.create_foreign_key(self.right_input_table, True,
                                       'fk_right_')
        merge_table.add(FieldSpec('has_dups_l', 'TINYINT'))
        merge_table.add(FieldSpec('has_dups_r', 'TINYINT'))
        merge_table.add(FieldSpec('reject', 'TINYINT'))
        merge_table.add(FieldSpec('merge_report', 'NVARCHAR', 4000))
        self.db_context.executeNoResults(merge_table.definition)
        return merge_table, left_fields, right_fields
Пример #12
0
    def execute(self):

        # Validate inputs
        self.validate()

        # Delete output tables if they exist
        drop_table_if_exists(self)
        if self.fuzzy_report_table:
            drop_table_if_exists(self.fuzzy_report_table)
        if self.left_remain_table:
            drop_table_if_exists(self.left_remain_table)
        if self.right_remain_table:
            drop_table_if_exists(self.right_remain_table)

        # Scan for illegal duplication of required keys in both tables
        left_dup_table, left_reject_table = self._process_required_key_dups_and_missing(
            self.db_context, self.left_input_table, 'left',
            self.allow_dups_left)
        right_dup_table, right_reject_table = self._process_required_key_dups_and_missing(
            self.db_context, self.right_input_table, 'right',
            self.allow_dups_right)

        # We will create an initial table that contains only the required keys,
        # optional keys, fuzzy keys, foreign keys and the duplicate detection columns
        merge_table, left_fields, right_fields = self._create_merge_table()

        # If necessary, remove duplicates that appear in both tables
        if self.allow_dups_left and self.allow_dups_right and not self.allow_dups_both:
            with get_temp_table(self.db_context) as full_dup_table:

                query = _DUPS_BOTH_QUERY.format(
                    left_dup_table=left_dup_table,
                    right_dup_table=right_dup_table,
                    full_dup_table=full_dup_table,
                    key=Joiner(self.required_merge_keys))
                self.db_context.executeNoResults(query)
                query = _MARK_DUPS_BOTH_QUERY.format(
                    dup_table=left_dup_table,
                    full_dup_table=full_dup_table,
                    key=Joiner(self.required_merge_keys))
                self.db_context.executeNoResults(query)
                self._move_rejects(left_dup_table, left_reject_table, 0,
                                   'has_dups_both<>0',
                                   "'Duplicate required key on both sides'")
                query = _MARK_DUPS_BOTH_QUERY.format(
                    dup_table=right_dup_table,
                    full_dup_table=full_dup_table,
                    key=Joiner(self.required_merge_keys))
                self.db_context.executeNoResults(query)
                self._move_rejects(right_dup_table, right_reject_table, 0,
                                   'has_dups_both<>0',
                                   "'Duplicate required key on both sides'")

        # Perform the first merge (required key)
        query = _FIRST_MERGE_QUERY.format(
            merge_table=merge_table,
            required_key=Joiner(self.required_merge_keys),
            foreign_key_l=Joiner(merge_table.foreign_keys[0]),
            foreign_key_r=Joiner(merge_table.foreign_keys[1]),
            left_dup_table=left_dup_table,
            right_dup_table=right_dup_table)
        self.db_context.executeNoResults(query)
        left_dup_table.drop()
        right_dup_table.drop()

        #Remove rejects after the first merge
        self._move_rejects(merge_table, left_reject_table, 0,
                           'has_dups_r IS NULL', "'No required key match'")
        self._move_rejects(merge_table, right_reject_table, 1,
                           'has_dups_l IS NULL', "'No required key match'")

        # Bring in optional and fuzzy keys
        if len(left_fields) > 0:
            query = _ADD_MORE_KEYS_QUERY.format(
                fields=Joiner(left_fields),
                merge_table=merge_table,
                from_table=self.left_input_table,
                side='left',
                required_key=Joiner(merge_table.foreign_keys[0]))
            self.db_context.executeNoResults(query)
        if len(right_fields) > 0:
            query = _ADD_MORE_KEYS_QUERY.format(
                fields=Joiner(right_fields),
                merge_table=merge_table,
                from_table=self.right_input_table,
                side='right',
                required_key=Joiner(merge_table.foreign_keys[1]))
            self.db_context.executeNoResults(query)

        key_fields = Joiner(self.required_merge_keys,
                            merge_table.foreign_keys[0],
                            merge_table.foreign_keys[1])

        # Flag matches for rejection based on optional keys
        if len(self.optional_merge_keys) > 0:
            indices = Joiner(range(1, len(self.optional_merge_keys) + 1))
            reason_expression = _OPTIONAL_MISMATCH_MESSAGE.format(
                indices=indices)
            query = _OPTIONAL_MISMATCH_QUERY.format(
                merge_table=merge_table,
                indices=indices,
                reason_expression=reason_expression)
            self.db_context.executeNoResults(query)

        # Flag matches for rejection based on fuzzy keys
        if len(self.fuzzy_merge_keys) > 0:
            indices = Joiner(range(1, len(self.fuzzy_merge_keys) + 1))
            query = _FUZZY_MATCH_QUERY.format(merge_table=merge_table,
                                              indices=indices,
                                              schema=self.db_context.schema)
            self.db_context.executeNoResults(query)

            # Create fuzzy report table
            if self.fuzzy_report_table is not None:
                query = _FUZZY_MATCH_REPORT_QUERY.format(
                    key_fields=key_fields,
                    indices=indices,
                    report_table=self.fuzzy_report_table,
                    merge_table=merge_table)
                self.db_context.executeNoResults(query)

            # Drop fuzzy mismatches
            reason_expression = _FUZZY_MISMATCH_MESSAGE.format(indices=indices)
            query = _FUZZY_MISMATCH_QUERY.format(
                merge_table=merge_table,
                thresholds=Joiner(self.similarity_thresholds),
                schema=self.db_context.schema,
                reason_expression=reason_expression)
            self.db_context.executeNoResults(query)

        # Move keys rejected due to optional or fuzzy matches
        reason_expression = "{fld:qualified}".format(
            fld=merge_table['merge_report'])
        self._copy_rejects(merge_table, left_reject_table, 0, '(reject<>0)',
                           reason_expression)
        self._copy_rejects(merge_table, right_reject_table, 1, '(reject<>0)',
                           reason_expression)
        self._delete_rows(merge_table, '(reject<>0)')

        # Meld columns in main merge table, including data columns that did not participate in the merge
        self.left_input_table.alias = "L"
        self.right_input_table.alias = "R"
        merge_table.alias = "A"
        key_field_names = [x.field_name for x in key_fields]
        original_fields = self[:]
        meld_fields = [x for x in self if x.field_name not in key_field_names]
        for key_field in key_fields:
            if key_field not in self:
                self.add(key_field.clone())

        self.db_context.executeNoResults(self.definition)

        query = _MELD_FIELDS_QUERY.format(
            merge_table=merge_table,
            out_table=self,
            meld_fields=Joiner(meld_fields),
            key_fields=key_fields,
            left_table=self.left_input_table,
            left_key=Joiner(merge_table.foreign_keys[0]),
            right_table=self.right_input_table,
            right_key=Joiner(merge_table.foreign_keys[1]))

        self.db_context.executeNoResults(query)

        # Add non-matched records for outer joins
        if self.join_type in (JOIN_TYPE_LEFT, JOIN_TYPE_FULL):
            query = _RESTORE_REJECTS_QUERY.format(
                out_table=self,
                reject_table=left_reject_table,
                input_table=self.left_input_table,
                fields=Joiner(original_fields),
                foreign_key=Joiner(left_reject_table.foreign_keys[0]),
                side='left')
            self.db_context.executeNoResults(query)

        if self.join_type in (JOIN_TYPE_RIGHT, JOIN_TYPE_FULL):
            query = _RESTORE_REJECTS_QUERY.format(
                out_table=self,
                reject_table=right_reject_table,
                input_table=self.right_input_table,
                fields=Joiner(original_fields),
                foreign_key=Joiner(right_reject_table.foreign_keys[0]),
                side='right')
            self.db_context.executeNoResults(query)

        # Bring into the remainder tables the data columns that did not participate in the merge
        if self.left_remain_table is not None and self.join_type in (
                JOIN_TYPE_RIGHT, JOIN_TYPE_INNER):
            # Create the table
            del self.left_remain_table[:]
            self.left_remain_table.add_all(left_reject_table.foreign_keys[0])
            self.left_remain_table.add_all(meld_fields)
            self.left_remain_table.add(left_reject_table.merge_report.clone())
            self.db_context.executeNoResults(self.left_remain_table.definition)

            left_reject_table.alias = "A"
            query = _REMAIN_TABLE_QUERY.format(
                key_fields=Joiner(left_reject_table.foreign_keys[0]),
                data_fields=Joiner(meld_fields),
                remain_table=self.left_remain_table,
                reject_table=left_reject_table,
                input_table=self.left_input_table,
                side='left')

            self.db_context.executeNoResults(query)

        if self.right_remain_table is not None and self.join_type in (
                JOIN_TYPE_LEFT, JOIN_TYPE_INNER):
            del self.right_remain_table[:]
            self.right_remain_table.add_all(right_reject_table.foreign_keys[0])
            self.right_remain_table.add_all(meld_fields)
            self.right_remain_table.add(
                right_reject_table.merge_report.clone())
            self.db_context.executeNoResults(
                self.right_remain_table.definition)

            right_reject_table.alias = "A"
            query = _REMAIN_TABLE_QUERY.format(
                key_fields=Joiner(right_reject_table.foreign_keys[0]),
                data_fields=Joiner(meld_fields),
                remain_table=self.right_remain_table,
                reject_table=right_reject_table,
                input_table=self.right_input_table,
                side='right')
            self.db_context.executeNoResults(query)

        left_reject_table.drop()
        right_reject_table.drop()
        merge_table.drop()