ArcPyを使用して関連レコードを効率的に選択しますか?


14

以下は、ArcMapの「関連テーブル」ボタンを複製するために使用しているコードです。ArcMapでは、そのボタンは、別の関連フィーチャクラスまたはテーブルのフィーチャの選択に基づいて、あるフィーチャクラスまたはテーブルのフィーチャを選択します。

ArcMapでは、そのボタンを使用して、選択を関連テーブルに数秒で「プッシュ」できます。ボタンを複製するarcpyに組み込まれたものを見つけることができなかったため、同じタスクを実行するためにいくつかのネストされたループを使用しました。

以下のコードは、「治療」のテーブルをループします。各処理について、「ツリー」のリストをループします。治療のIDフィールドとツリーの間で一致が見つかると、ツリーレイヤーで選択が行われます。治療に一致するものが見つかると、コードは追加の一致のためにツリーレイヤーを検索し続けません。処理テーブルに戻り、次の処理を選択して、再びツリーフィーチャクラスを検索します。

コード自体は正常に機能しますが、非常に遅くなります。この場合の「治療テーブル」には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はarcpy.SearchCursorよりもはるかに高速(おそらく10倍)になる可能性があります。また、Python辞書の使用を検討することもできます。私はアプローチが使用さから、このような「キーファイルの選択は、」大いに恩恵を受ける可能性があることを疑うこちら
PolyGeo

SDEデータベースは偶然Oracleにありますか?
blah238

回答:


12

まず、主キーと外部キーのフィールドが両方のテーブルでインデックス付けされていることを確認する必要があります。これにより、DBMSはこれらのフィールドに対するクエリをより効率的に計画および実行できます。

第二SelectLayerByAttribute_managementに、タイトなネストされたループを呼び出しています(1回の処理につきツリーごとに1回)。これは、いくつかの理由により、非常に非効率的です。

  • 私が知る限り、このために、一方が他方の中にネストされている2つのループは必要ありません。1つで十分です。
  • ジオプロセシング関数は「チャンキー」であり、典型的な組み込みPython関数と比較して呼び出すのに多くの時間がかかります。タイトなループでそれらを呼び出すことは避けてください。
  • 一度に1つのレコード/ IDを要求すると、データベースへのラウンドトリップが非常に多くなります。

代わりに、コードをリファクタリングしてSelectLayerByAttribute_management、関連レコードをすべて選択するように構築されたwhereclauseを使用して1回だけ呼び出すようにします。

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.SearchCursor10.1でのみ使用可能なを使用します。@PolyGeoが言及したように、これらのカーソルはそれらの先行カーソル(arcpy.SearchCursor)よりもはるかに高速です。ただし、古いSearchCursorを使用するように簡単に変更できます。

    sourceIDs = set([row.getValue(sourceField) for row in arcpy.SearchCursor(sourceLayer, "", "", sourceField)])
  • SDEジオデータベースがOracle上にある場合IN、リンクされた回答の関数で使用されるステートメントは1000要素に制限されていることに注意してください。この回答では解決策の1つを説明しいますが、関数を変更してIN、1つではなく複数の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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.