Ефективний вибір відповідних записів за допомогою ArcPy?


14

Нижче наведено код, який я використовую для копіювання кнопки "пов'язані таблиці" в ArcMap. У ArcMap ця кнопка вибирає функції одного класу характеристик або таблиці на основі вибору функцій в іншому пов'язаному класі чи таблиці функцій.

В ArcMap я можу скористатися цією кнопкою, щоб "натиснути" свій вибір на відповідну таблицю за лічені секунди. Мені не вдалося знайти нічого вбудованого в архпію, яка копіює кнопку, тому я використав кілька вкладених циклів, щоб виконати те саме завдання.

Код нижче перебирає таблицю "процедур". Для кожної обробки вона перебирає список «дерев». Коли між полями обробки ідентифікаторів і деревами знайдено збіг, у шарі дерева відбувається виділення. Після того, як для обробки буде знайдено збіг, код не продовжує шукати в шарі дерева додаткові збіги. Він повертається до таблиці обробки, вибирає наступну обробку і знову проводить пошук по дереву класу функцій.

Сам код працює чудово, але він мучить повільно. "Таблиця лікування" в цьому випадку має 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

2
Ви використовуєте ArcGIS 10.1? Якщо так, arcpy.da.SearchCursor, ймовірно, буде набагато швидшим (можливо, у 10 разів), ніж arcpy.SearchCursor. Крім того, ви можете розглянути можливість використання словника Python. Я підозрюю, що такий "вибір
ключових файлів

Чи випадково ваша база даних SDE в Oracle?
blah238

Відповіді:


12

По-перше, так, ви обов'язково хочете, щоб ваші поля первинного та зовнішнього ключа були індексовані в обох таблицях. Це дозволяє СУБД планувати та виконувати запити щодо цих полів набагато ефективніше.

По-друге, ви дзвоните SelectLayerByAttribute_managementв тісний, вкладений цикл (один раз на дерево за обробку). Це вкрай неефективно з кількох причин:

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

Натомість перефактуруйте свій код, щоб ви зателефонували SelectLayerByAttribute_managementлише один раз за допомогою зауваження, побудованого для вибору всіх пов'язаних записів.

Запозичивши функцію з іншої відповіді на логіку побудови зауважень, я думаю, що це виглядатиме приблизно так:

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 замість одного.


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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.