¿Está seleccionando eficientemente los registros relacionados usando ArcPy?

14

Abajo está el código que estoy usando para replicar el botón de "tablas relacionadas" en ArcMap. En ArcMap, ese botón selecciona entidades en una clase de entidad o tabla en función de la selección de entidades en otra clase de entidad o tabla relacionada.

En ArcMap puedo usar ese botón para "empujar" mi selección a la tabla relacionada en cuestión de segundos. No pude encontrar nada integrado en arcpy que replique el botón, así que utilicé algunos bucles anidados para hacer la misma tarea.

El siguiente código recorre una tabla de "tratamientos". Para cada tratamiento, recorre una lista de "árboles". Cuando se encuentra una coincidencia entre los campos de ID del tratamiento y los árboles, se produce una selección en la capa del árbol. Una vez que se encuentra una coincidencia para un tratamiento, el código no continúa buscando coincidencias adicionales en la capa del árbol. Regresa a la tabla de tratamiento, selecciona el siguiente tratamiento y nuevamente busca en la clase de entidad del árbol.

El código en sí funciona bien, pero es extremadamente lento. La "tabla de tratamiento" en este caso tiene 16,000 registros. La clase de entidad "árbol" tiene 60,000 registros.

¿Hay otra manera más eficiente de recrear lo que hace ESRI cuando empuja la selección de una tabla a otra? ¿Debo estar creando un índice para las tablas? NOTA: Estos datos se almacenan en 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
    
pregunta WatsonP 07.02.2013 - 17:53

2 respuestas

12

Primero que nada, sí, definitivamente querrás asegurarte de que los campos de la clave principal y la clave externa estén indexados en ambas tablas. Esto permite que el DBMS planifique y ejecute consultas en estos campos de manera mucho más eficiente.

En segundo lugar, está llamando a SelectLayerByAttribute_management en un bucle cerrado y anidado (una vez por árbol por tratamiento). Esto es altamente ineficiente, por varias razones:

  • No necesitas dos bucles, uno anidado dentro del otro, para esto, por lo que puedo decir. Una será suficiente.
  • Las funciones de geoprocesamiento son "complejas" y requieren mucho tiempo para llamar en comparación con las funciones típicas de Python incorporadas. Debe evitar llamarlos en un bucle cerrado.
  • Solicitar un registro / ID a la vez da como resultado muchos más viajes de ida y vuelta a la base de datos.

En su lugar, refactorice su código para que llame a SelectLayerByAttribute_management solo una vez con un paradero construido para seleccionar todos los registros relacionados.

Tomando prestada una función de otra respuesta para la lógica de construcción de la cláusula, me imagino que se vería como esto:

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)

Puedes llamarlo así: selectRelatedRecords(treatment_tv, tree_fl, "Facility_ID", "FACILITYID")

Notas :

  • Esto utiliza un arcpy.da.SearchCursor , solo disponible en 10.1 . Como mencionó @PolyGeo, estos cursores son mucho más rápidos que sus predecesores (arcpy.SearchCursor ). Aunque podría modificarse fácilmente para usar el SearchCursor antiguo:

    sourceIDs = set([row.getValue(sourceField) for row in arcpy.SearchCursor(sourceLayer, "", "", sourceField)])
    
  • Si su geodatabase SDE está en Oracle, tenga en cuenta que la declaración IN utilizada en la función de la respuesta vinculada está limitada a 1000 elementos. Una posible solución se describe en esta respuesta , pero tendría que modificar la función para dividirla en múltiples 1000 -length IN en lugar de una.

respondido por el blah238 09.02.2013 - 13:46
5

La solución anterior funciona muy bien para mí y fue muy rápida. Usando el código anterior y el código referenciado de la otra publicación, así es como lo construí:

# 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)
    
respondido por el user1714326 27.02.2014 - 18:45

Lea otras preguntas en las etiquetas