Selezione efficiente dei record correlati tramite ArcPy?


14

Di seguito è riportato il codice che sto usando per replicare il pulsante "tabelle correlate" in ArcMap. In ArcMap quel pulsante seleziona le funzionalità in una classe o tabella di funzionalità in base alla selezione di funzioni in un'altra classe o tabella di funzionalità correlate.

In ArcMap posso usare quel pulsante per "spingere" la mia selezione nella tabella correlata in pochi secondi. Non sono riuscito a trovare nulla incorporato in arcpy che replica il pulsante, quindi ho usato alcuni loop nidificati per fare lo stesso compito.

Il codice seguente scorre attraverso una tabella di "trattamenti". Per ogni trattamento, scorre un elenco di "alberi". Quando viene trovata una corrispondenza tra i campi ID del trattamento e gli alberi, si verifica una selezione nel livello dell'albero. Una volta trovata una corrispondenza per un trattamento, il codice non continua a cercare ulteriori livelli nel livello dell'albero. Torna alla tabella dei trattamenti, seleziona il trattamento successivo e cerca nuovamente nella classe di caratteristiche dell'albero.

Il codice stesso funziona bene, ma è stranamente lento. La "tabella di trattamento" in questo caso ha 16.000 record. La classe di funzionalità "albero" ha 60.000 record.

C'è un altro modo più efficace per ricreare ciò che ESRI sta facendo quando spinge la selezione da una tabella all'altra? Devo creare un indice per le tabelle? NOTA: questi dati sono memorizzati in un SDE.

 # Create search cursor to loop through the treatments
treatments = arcpy.SearchCursor(treatment_tv)
treatment_field = "Facility_ID"

for treatment in treatments:

    #Get ID of treatment
    treatment_ID = treatment.getValue(treatment_field)

    # Create search cursor for looping through the trees
    trees = arcpy.SearchCursor(tree_fl)
    tree_field = "FACILITYID"

    for tree in trees:

        # Get FID of tree
        tree_FID = tree.getValue(tree_field)

        if tree_FID == treatment_FID:
            query = "FACILITYID = " + str(tree_FID)
            arcpy.SelectLayerByAttribute_management(tree_fl, "REMOVE_FROM_SELECTION", query)
            break

2
Stai usando ArcGIS 10.1? In tal caso, è probabile che arcpy.da.SearchCursor sia molto più veloce (forse 10X) di arcpy.SearchCursor. Inoltre, potresti voler considerare l'uso di un dizionario Python. Sospetto che una "selezione di file di chiavi" come questa potrebbe trarre grandi benefici dall'approccio qui
PolyGeo

Il tuo database SDE su Oracle è casuale?
blah238,

Risposte:


12

Prima di tutto, sì, vorrai sicuramente assicurarti che i campi della chiave primaria ed esterna siano indicizzati su entrambe le tabelle. Ciò consente al DBMS di pianificare ed eseguire query su questi campi in modo molto più efficiente.

In secondo luogo, stai chiamando SelectLayerByAttribute_management in un ciclo stretto e annidato (una volta per albero per trattamento). Questo è altamente inefficiente, per diversi motivi:

  • Non hai bisogno di due anelli, uno annidato nell'altro, per questo, per quanto posso dire. Uno sarà sufficiente.
  • Le funzioni di geoprocessing sono "pesanti" e richiedono molto tempo per essere chiamate rispetto alle tipiche funzioni Python integrate. Dovresti evitare di chiamarli in un circuito chiuso.
  • Chiedere un record / ID alla volta si traduce in un numero considerevolmente maggiore di viaggi di andata e ritorno nel database.

Invece, refactoring il codice in modo da chiamare SelectLayerByAttribute_managementRifatti una sola volta con un clausola di ubicazione costruita per selezionare tutti i record correlati.

Prendendo in prestito una funzione da un'altra risposta per la logica costruttiva del luogo, immagino che sarebbe simile a questa:

def selectRelatedRecords(sourceLayer, targetLayer, sourceField, targetField):
    sourceIDs = set([row[0] for row in arcpy.da.SearchCursor(sourceLayer, sourceField)])
    whereClause = buildWhereClauseFromList(targetLayer, targetField, sourceIDs)
    arcpy.AddMessage("Selecting related records using WhereClause: {0}".format(whereClause))
    arcpy.SelectLayerByAttribute_management(targetLayer, "NEW_SELECTION", whereClause)

Potresti chiamarlo così: selectRelatedRecords(treatment_tv, tree_fl, "Facility_ID", "FACILITYID")

Appunti:

  • Questo utilizza un arcpy.da.SearchCursor, disponibile solo a 10.1. Come menzionato da @PolyGeo, questi cursori sono molto più veloci dei loro predecessori ( arcpy.SearchCursor). Potrebbe essere facilmente modificato per utilizzare il vecchio SearchCursor:

    sourceIDs = set([row.getValue(sourceField) for row in arcpy.SearchCursor(sourceLayer, "", "", sourceField)])
  • Se il tuo geodatabase SDE è su Oracle, tieni presente che l' INistruzione utilizzata nella funzione dalla risposta collegata è limitata a 1000 elementi. Una possibile soluzione è descritta in questa risposta , ma dovresti modificare la funzione per dividerla in più INistruzioni di lunghezza 1000 anziché una.


5

La soluzione sopra funziona alla grande per me ed è stata molto veloce. Utilizzando il codice sopra e il codice di riferimento dell'altro post, è così che l'ho costruito:

# Local Variables
OriginTable = "This must be a Table View or Feature Layer"
DestinationTable = "This must be a Table View or Feature Layer"
PrimaryKeyField = "Matching Origin Table Field"
ForiegnKeyField = "Matching Destination Table Field"

def buildWhereClauseFromList(OriginTable, PrimaryKeyField, valueList):
  """Takes a list of values and constructs a SQL WHERE
       clause to select those values within a given PrimaryKeyField
       and OriginTable."""

    # Add DBMS-specific field delimiters
    fieldDelimited = arcpy.AddFieldDelimiters(arcpy.Describe(OriginTable).path, PrimaryKeyField)

    # Determine field type
    fieldType = arcpy.ListFields(OriginTable, PrimaryKeyField)[0].type

    # Add single-quotes for string field values
    if str(fieldType) == 'String':
    valueList = ["'%s'" % value for value in valueList]

    # Format WHERE clause in the form of an IN statement
    whereClause = "%s IN(%s)" % (fieldDelimited, ', '.join(map(str, valueList)))
    return whereClause

def selectRelatedRecords(OriginTable, DestinationTable, PrimaryKeyField, ForiegnKeyField):
    """Defines the record selection from the record selection of the OriginTable
      and applys it to the DestinationTable using a SQL WHERE clause built
      in the previous defintion"""

    # Set the SearchCursor to look through the selection of the OriginTable
    sourceIDs = set([row[0] for row in arcpy.da.SearchCursor(OriginTable, PrimaryKeyField)])

    # Establishes the where clause used to select records from DestinationTable
    whereClause = buildWhereClauseFromList(DestinationTable, ForiegnKeyField, sourceIDs)

    # Process: Select Layer By Attribute
    arcpy.SelectLayerByAttribute_management(DestinationTable, "NEW_SELECTION", whereClause)

# Process: Select related records between OriginTable and DestinationTable
selectRelatedRecords(OriginTable, DestinationTable, PrimaryKeyField, ForiegnKeyField)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.