Эффективно ли выбирать связанные записи с помощью ArcPy?

14

Ниже приведен код, который я использую для репликации кнопки «связанные таблицы» в ArcMap. В ArcMap эта кнопка выбирает объекты в одном классе пространственных объектов или таблице на основе выбора объектов в другом связанном классе пространственных объектов или таблице.

В ArcMap я могу использовать эту кнопку, чтобы «перенести» свой выбор в связанную таблицу за считанные секунды. Я не смог найти ничего встроенного в arcpy, который бы дублировал кнопку, поэтому я использовал несколько вложенных циклов для выполнения той же задачи.

Код ниже просматривает таблицу «процедур». Для каждой процедуры он просматривает список «деревьев». Когда найдено совпадение между полями идентификаторов обработки и деревьями, выбор происходит в слое дерева. После того, как для обработки найдено совпадение, код не продолжает поиск дополнительных совпадений в слое дерева. Он возвращается к таблице процедур, выбирает следующую процедуру и снова просматривает древовидный класс пространственных объектов.

Сам код работает нормально, но мучительно медленно. «Таблица обработки» в этом случае имеет 16 000 записей. Класс объектов «дерево» содержит 60 000 записей.

Есть ли другой более эффективный способ воссоздать то, что делает ESRI, когда он перемещает выбор из одной таблицы в другую? Должен ли я создавать индекс для таблиц? ПРИМЕЧАНИЕ. Эти данные хранятся в 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
WatsonP
источник
2
Вы используете ArcGIS 10.1? Если это так, arcpy.da.SearchCursor, вероятно, будет намного быстрее (возможно, в 10 раз), чем arcpy.SearchCursor. Также вы можете рассмотреть возможность использования словаря Python. Я подозреваю, что подобный «выбор ключевого файла» может очень выиграть от подхода, использованного здесь
PolyGeo
Ваша база данных SDE на Oracle случайно?
blah238

Ответы:

12

Во-первых, да, вы определенно захотите убедиться, что поля первичного и внешнего ключей проиндексированы в обеих таблицах. Это позволяет СУБД гораздо эффективнее планировать и выполнять запросы к этим полям.

Во-вторых, вы вызываете SelectLayerByAttribute_managementв тесном, вложенном цикле (один раз на дерево за обработку). Это крайне неэффективно по нескольким причинам:

  • Для этого, насколько я могу судить, вам не нужны две петли, одна вложена в другую. Одного будет достаточно.
  • Функции геообработки "короткие" и требуют много времени для вызова по сравнению с типичными встроенными функциями Python. Вы должны избегать называть их в узком кругу.
  • Запрос одной записи / идентификатора за раз приводит к значительно большему количеству обращений к базе данных.

Вместо этого SelectLayerByAttribute_managementвыполните рефакторинг вашего кода, чтобы вы вызывали его только один раз с предложением where, созданным для выбора всех связанных записей.

Заимствуя функцию из другого ответа для логики построения whereclause, я бы предположил, что это будет выглядеть примерно так:

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)

Вы могли бы назвать это так: selectRelatedRecords(treatment_tv, tree_fl, "Facility_ID", "FACILITYID")

Примечания:

  • Это использует arcpy.da.SearchCursor, доступно только в 10.1. Как упомянул @PolyGeo, эти курсоры намного быстрее, чем их предшественники ( arcpy.SearchCursor). Это может быть легко изменено, чтобы использовать старый SearchCursor, хотя:

    sourceIDs = set([row.getValue(sourceField) for row in arcpy.SearchCursor(sourceLayer, "", "", sourceField)])
  • Если ваша база геоданных SDE находится в Oracle, имейте в виду, что INоператор, используемый в функции из связанного ответа, ограничен 1000 элементами. В этом ответе описано одно из возможных решений , но вам придется изменить функцию, чтобы разделить ее на несколько INоператоров длиной 1000 вместо одного.

blah238
источник
5

Вышеупомянутое решение прекрасно работает для меня и было очень быстрым. Используя приведенный выше код и ссылочный код из другого поста, вот как я его построил:

# 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)
user1714326
источник