def updateFGDBfromSDE(fgdb, sde, logger=None):
    global changes
    """
    fgdb: file geodatabase
    sde: sde geodatabase connection
    logger: agrc.logging.Logger (optional)

    returns: String[] - the list of errors

    Loops through the file geodatabase feature classes and looks for
    matches in the SDE database. If there is a match, it does a schema check
    and then updates the data.
    """

    def log(msg):
        if logger:
            logger.logMsg(msg)
        else:
            print msg

    def updateData(isTable):
        try:
            # validate that there was not a schema change
            arcpy.env.workspace = fgdb
            layer = sdeFC + '_Layer'
            if not isTable:
                arcpy.MakeFeatureLayer_management(sdeFC, layer, '1 = 2')
            else:
                arcpy.MakeTableView_management(sdeFC, layer, '1 = 2')

            try:
                arcpy.Append_management(layer, f, 'TEST')
                log('schema test passed')
                passed = True
            except arcpy.ExecuteError as e:
                if '000466' in e.message:
                    log(e.message)
                    msg = 'schema change detected'
                    msg += '\n\n{0}'.format(getFieldDifferences(sdeFC, f))
                    errors.append('{}: {}'.format(f, msg))
                    log(msg)
                    passed = False
                    return passed
                else:
                    raise e
            arcpy.Delete_management(layer)

            log('checking for changes...')
            if checkForChanges(f, sdeFC, isTable) and passed:
                log('updating data...')
                arcpy.TruncateTable_management(f)

                # edit session required for data that participates in relationships
                editSession = arcpy.da.Editor(fgdb)
                editSession.startEditing(False, False)
                editSession.startOperation()

                fields = [fld.name for fld in arcpy.ListFields(f)]
                fields = filter_fields(fields)
                if not isTable:
                    fields.append('SHAPE@')
                    outputSR = arcpy.Describe(f).spatialReference
                else:
                    outputSR = None
                with arcpy.da.InsertCursor(f, fields) as icursor, \
                    arcpy.da.SearchCursor(sdeFC, fields, sql_clause=(None, 'ORDER BY OBJECTID'),
                                          spatial_reference=outputSR) as cursor:
                    for row in cursor:
                        icursor.insertRow(row)

                editSession.stopOperation()
                editSession.stopEditing(True)

                changes.append(f.upper())
            else:
                log('no changes found')
        except:
            errors.append('Error updating: {}'.format(f))
            if logger:
                logger.logError()

    log('** Updating {} from {}'.format(fgdb, sde))
    errors = []

    # loop through local feature classes
    arcpy.env.workspace = fgdb
    fcs = arcpy.ListFeatureClasses() + arcpy.ListTables()
    totalFcs = len(fcs)
    i = 0
    for f in fcs:
        i = i + 1
        log('{} of {} | {}'.format(i, totalFcs, f))

        found = False

        # search for match in stand-alone feature classes
        arcpy.env.workspace = sde
        matches = arcpy.ListFeatureClasses('*.{}'.format(f)) + arcpy.ListTables('*.{}'.format(f))
        if matches is not None and len(matches) > 0:
            match = matches[0]
            sdeFC = join(sde, match)
            found = True
        else:
            # search in feature datasets
            datasets = arcpy.ListDatasets()
            if len(datasets) > 0:
                # loop through datasets
                for ds in datasets:
                    matches = arcpy.ListFeatureClasses('*.{}'.format(f), None, ds)
                    if matches is not None and len(matches) > 0:
                        match = matches[0]
                        sdeFC = join(sde, match)
                        found = True
                        break
        if not found:
            msg = 'no match found in sde'
            errors.append("{}: {}".format(f, msg))
            log(msg)
            continue

        updateData(arcpy.Describe(join(fgdb, f)).datasetType == 'Table')

    return (errors, changes)
fgdb: file geodatabase
    sde: sde geodatabase connection
    logger: agrc.logging.Logger (optional)

    returns: String[] - the list of errors

    Loops through the file geodatabase feature classes and looks for
    matches in the SDE database. If there is a match, it does a schema check
    and then updates the data.