Thanks everybody. The data dictionary method was superfast and has definitely solved a very thorny problem. I did use makequerytable first to get the data into a layer i could interrogate in testing and then used this to populate the data dictionary. All works very wellCheers
message = "[INFO] Create table of general GGF data started at " + str(datetime.datetime.now()) print message viewName = "DWOP.DBO.VWO_GenerateGeorouteFilesNonSpatial" fullViewName = arcpy.env.workspace + os.sep + viewName tableLayerName = "GGF_Data" + OfficeID fieldList = [[viewName + ".OBJECTID"],[viewName + ".TOID"],[viewName + ".OFFICE_ID"],[viewName + ".SEG_ID"],[viewName + ".STL_NAME"],[viewName + ".STR_NAME"],[viewName + ".ST_CLASS"],[viewName + ".ORIG_LVL"],[viewName + ".DEST_LVL"],[viewName + ".ONEWAY"],[viewName + ".SPEED1"],[viewName + ".SPEED2"],[viewName + ".SPEED3"],[viewName + ".SPEED4"],[viewName + ".TRN_MODE"],[viewName + ".BTH_MODE"],[viewName + ".RESTR_ORI"],[viewName + ".RESTR_DEST"]] whereClause = "OFFICE_ID = " + OfficeID arcpy.MakeQueryTable_management(fullViewName,tableLayerName,"USE_KEY_FIELDS",viewName + ".OBJECTID",fieldList,whereClause) message = "[INFO] Create Data Dictionary of general GGF data started at " + str(datetime.datetime.now()) print message mySqlFieldsToReturnList = ["OBJECTID",viewName + ".TOID",viewName + ".OFFICE_ID",viewName + ".SEG_ID",viewName + ".STL_NAME",viewName + ".STR_NAME",viewName + ".ST_CLASS",viewName + ".ORIG_LVL",viewName + ".DEST_LVL",viewName + ".ONEWAY",viewName + ".SPEED1",viewName + ".SPEED2",viewName + ".SPEED3",viewName + ".SPEED4",viewName + ".TRN_MODE",viewName + ".BTH_MODE",viewName + ".RESTR_ORI",viewName + ".RESTR_DEST"] rdlkDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(tableLayerName, mySqlFieldsToReturnList)} #Would like to check the rdlkDict values here message = "[INFO] Looping through selected RoadNetwork started at " + str(datetime.datetime.now()) print message updateFieldsList = ["OBJECTID","SEG_ID","STL_NAME","STR_NAME","ST_CLASS", "ORIG_LVL","DEST_LVL","ONEWAY","SPEED1","SPEED2","SPEED3","SPEED4","TRN_MODE","BTH_MODE","RESTR_ORI","RESTR_DEST"] with arcpy.da.UpdateCursor(FinalNetworkLayerName, updateFieldsList) as updateRows: for updateRow in updateRows: ObjectIDVal = updateRow[0] currentRow = currentRow + 1 if currentRow%100 == 0: message = "[INFO] Processed " + str(currentRow) + " of " + str(RoadNetworkCount) + " rows at " + str(datetime.datetime.now()) print message if ObjectIDVal in rdlkDict: updateRow[1] = rdlkDict[ObjectIDVal][3] updateRow[2] = rdlkDict[ObjectIDVal][4] updateRow[3] = rdlkDict[ObjectIDVal][5] updateRow[4] = rdlkDict[ObjectIDVal][6] updateRow[5] = rdlkDict[ObjectIDVal][7] updateRow[6] = rdlkDict[ObjectIDVal][8] updateRow[7] = rdlkDict[ObjectIDVal][9] updateRow[8] = rdlkDict[ObjectIDVal][10] updateRow[9] = rdlkDict[ObjectIDVal][11] updateRow[10] = rdlkDict[ObjectIDVal][12] updateRow[11] = rdlkDict[ObjectIDVal][13] updateRow[12] = rdlkDict[ObjectIDVal][14] updateRow[13] = rdlkDict[ObjectIDVal][15] updateRow[14] = rdlkDict[ObjectIDVal][16] updateRow[15] = rdlkDict[ObjectIDVal][17] updateRows.updateRow(updateRow)
updateFieldsList = ["FROM_OBJECTID","FROM_ROUTE_NAME","FROM_MEASURE","FROM_MEAS","FROM_X_COORDINATE","FROM_Y_COORDINATE","FROM_X_Y_LINK","FROM_STREET_NAME","FROM_CROSS_STREETS","FROM_X_Y_ROUTE_NAME"] valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(outputFC, updateFieldsList)} readFieldsList = ["OBJECTID", "RID", "MEAS", "X_COORD", "Y_COORD", "X_Y_LINK", "STNAME", "STNAMES", "X_Y_ROUTE"] with arcpy.da.SearchCursor(readFC, readFieldsList) as readRows: for readRow in readRows: ObjectIDVal = readRow[0] if ObjectIDVal in valueDict: valueDict[ObjectIDVal][0] = readRow[1] valueDict[ObjectIDVal][1] = readRow[2] valueDict[ObjectIDVal][2] = readRow[2] valueDict[ObjectIDVal][3] = readRow[3] valueDict[ObjectIDVal][4] = readRow[4] valueDict[ObjectIDVal][5] = readRow[5] valueDict[ObjectIDVal][6] = readRow[6] valueDict[ObjectIDVal][7] = readRow[7] valueDict[ObjectIDVal][8] = readRow[8] with arcpy.da.UpdateCursor(outputFC, updateFieldsList) as updateRows: for updateRow in updateRows: ObjectIDVal = updateRow[0] updateRow[1] = valueDict[ObjectIDVal][0] updateRow[2] = valueDict[ObjectIDVal][1] updateRow[3] = valueDict[ObjectIDVal][2] updateRow[4] = valueDict[ObjectIDVal][3] updateRow[5] = valueDict[ObjectIDVal][4] updateRow[6] = valueDict[ObjectIDVal][5] updateRow[7] = valueDict[ObjectIDVal][6] updateRow[8] = valueDict[ObjectIDVal][7] updateRow[9] = valueDict[ObjectIDVal][8] updateRows.updateRow(updateRow)
Given that there was mention of using a list of objectIDs from the FinalNetworkLayerName feature layer, it would be possible for the function to be part of a SQL Select statement and the objectids passed into this as a Where clause. And then I guess this can be called to create a data dictionary but still unsure about the code.If this was the solution (and given that I would need to deal with chunks of data as I don't believe SQL Server 2008 can deal with over 60000 entries in a single WHERE.....IN (....)... statement), how would I code:1. Create a data Dictionary for the first chunk of objectIDs from FinalNetworkLayerName, passing these to the WHERE clause of a SQL statement 2. Update the same Data Dictionary for x chunks of objectIDs, also passing to a WHERE clause, until the end of FinalNetworkLayerName 3. Then update FinalNetworkLayerName by matching to the objectIDs in the Data DictionaryCheersMark
readFieldsList = ["OBJECTID", "RID", "MEAS", "X_COORD", "Y_COORD", "X_Y_LINK", "STNAME", "STNAMES", "X_Y_ROUTE"] valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(readFC, readFieldsList)} updateFieldsList = ["FROM_OBJECTID","FROM_ROUTE_NAME","FROM_MEASURE","FROM_MEAS","FROM_X_COORDINATE","FROM_Y_COORDINATE","FROM_X_Y_LINK","FROM_STREET_NAME","FROM_CROSS_STREETS","FROM_X_Y_ROUTE_NAME"] with arcpy.da.UpdateCursor(outputFC, updateFieldsList) as updateRows: for updateRow in updateRows: ObjectIDVal = updateRow[0] if ObjectIDVal in valueDict: updateRow[1] = valueDict[ObjectIDVal][0] updateRow[2] = valueDict[ObjectIDVal][1] updateRow[3] = valueDict[ObjectIDVal][1] updateRow[4] = valueDict[ObjectIDVal][2] updateRow[5] = valueDict[ObjectIDVal][3] updateRow[6] = valueDict[ObjectIDVal][4] updateRow[7] = valueDict[ObjectIDVal][5] updateRow[8] = valueDict[ObjectIDVal][6] updateRow[9] = valueDict[ObjectIDVal][7] updateRows.updateRow(updateRow)
I think I need to build the data dictionary first based on the list of rows already in the feature layer FinalNetworkLayerName as this restricts the number of row to be processed to between 3000 and 64000, whereas, in total, there are millions of potential rows to be processed. This still looks to me like I need to build the data dictionary row by row, which seems to me will probably result in the same amount of time taken if that time is taken up by mostly the SQL call istelf. However, if it is the update of the feature layer that is the majority of the 2 seconds taken, then there may be a saving, assuming that the building of the data dictionary is quick.That would mean attempting to build the data dictionary row by row using the list of all objectIDs from FinalNetworkLayerName. So is there efficient code for this process?CheersMark Wingfield
mySqlTable = r"\\mynetwork\myconnectionfile.sde" mySqlFieldsToReturnList = ["OBJECTID", "SEG_ID", "STL_NAME", "STR_NAME", ...] rdlkDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(mySqlTbl, mySqlFieldsToReturnList)}
fields = ("SEG_ID", "STL_NAME", "STR_NAME", "ST_CLASS", "ORIG_LVL", "DEST_LVL", "ONEWAY", "SPEED1", "SPEED2", "SPEED3", "SPEED4", "TRN_MODE", "BTH_MODE", "RESTR_ORI", "RESTR_DEST", "TOID", "OBJECTID") cursor = arcpy.da.UpdateCursor(FinalNetworkLayerName, fields) for featurerow in cursor: CurrentObjectID = featurerow[17] # Establishing connection to MSSQL database sql = "SELECT * FROM [DWOP].[dbo].[ufunc_ReturnGGFDataObjectID] (" + CurrentObjectId + "," + OfficeIDParm # Call function sde_return = sdeConn2.execute(sql) if isinstance(sde_return, list): for row in sde_return: featurerow[0] = row[0] featurerow[1] = row[1] featurerow[2] = row[2] featurerow[3] = row[3] featurerow[4] = row[4] featurerow[5] = row[5] featurerow[6] = row[6] featurerow[7] = row[7] featurerow[8] = row[8] featurerow[9] = row[9] featurerow[10] = row[10] featurerow[11] = row[11] featurerow[12] = row[12] featurerow[13] = row[13] featurerow[14] = row[14] cursor.updateRow(featurerow) del cursor, featurerow
rdlkDict = dict([(r.OBJECTID, (r.SEG_ID, r.STL_NAME, r.STR_NAME, r.ST_CLASS, r.ORIG_LVL, r.DEST_LVL, r.ONEWAY, r.SPEED1, r.SPEED2, r.SPEED3, r.SPEED4, r.TRN_MODE, r.BTH_MODE, r.RESTR_ORI, r.RESTR_DEST, r.TOID)) for r in arcpy.SearchCursor(FinalNetworkLayerName)])
After reading your original post, all you are doing is basically a join/calc... the performance of which still can be improved using a dictionary/update cursor. This is some older code (doesn't use .da cursors, dictionary comprehensions, etc.) but it illustrates the traditional join and calc sort of thing you are trying to accomplish.http://forums.arcgis.com/threads/9555-Modifying-Permanent-Sort-script-by-Chris-Snyder?p=30010&viewfull=1#post30010
Some comments and examples:1. Use a dictionary, as Richard said its way faster than using imbedded cursors, and your bosses will of course give you a raise, since you improved the performance so much.2. If the dictionary gets "too big"... that is it occupies more than ~2.1 GB of RAM, which is the (sort of) limit of 32-bit Python, you can use 64-bit Python along with the 64-bit "ArcGIS_BackgroundGP_for_Desktop_101sp1.exe", which basically turns arcpy (v10.1+) into a 64-bit bad a$$ capable of using gobs of RAM.Here's an (untested) example of that dictionary calculaty sort of thing... It sucks and existing table into a dictionary, and then updates the table by calcing the ID field value = to the sum of all the (original) ID values that are >= the ID value being processed. Why would you would want to do that? I don't know, but it illustrates the general concept.myTable = r"C:\temp\test.gdb\test" valueDict = {r[0]:(r[1]) for r in arcpy.da.SearchCursor(myTable, ["OID@","ID"])} updateRows = arcpy.da.UpdateCursor(myTable, ["OID@","ID"]) for updateRow in updateRows: oidVal, idVal = updateRow updateRow[1] = sum([i[0] for i in valueDict if valueDict[0] >= idVal]) updateRows.updateRow(updateRow) del updateRow, updateRows
myTable = r"C:\temp\test.gdb\test" valueDict = {r[0]:(r[1]) for r in arcpy.da.SearchCursor(myTable, ["OID@","ID"])} updateRows = arcpy.da.UpdateCursor(myTable, ["OID@","ID"]) for updateRow in updateRows: oidVal, idVal = updateRow updateRow[1] = sum([i[0] for i in valueDict if valueDict[0] >= idVal]) updateRows.updateRow(updateRow) del updateRow, updateRows
This could certainly throw a wrench into the "IN" statement approach --- is there any issues with chunk processing part? One potential alternative idea is to use a Table Variable instead. If the list of OID's is really large you could first run an insert into the table variable then simply JOIN this to the table in your main query. This design approach would be best fit into a StoredProcedure rather than dynamic SQL though (not a bad plan either as the SQL statement will have already been compiled on the server).
If the ObjectID list is crazy huge you may need to chunk it into selection groups with the modulus operator so that the SQL where clause is not excessively large. Then query that group back to a separate layer copy of the input selection layer. The layer copy would be created outside of your loop using the Make Feature Layer operation.
Currently, I have a feature layer that I step through row by row and update each row in the layer with output data from a SQL function. This works but is slow. I have been asked to try and speed this up and the suggestion was to attempt some form of global update in one go tat could link the feature class to a function or view and update all rows in one go. The data returned from the current function relies on inputs from the current row to create the returned data. I have attached the current code below: message = "[INFO] Looping through selected RoadNetwork started at " + str(datetime.datetime.now()) print message arcpy.AddMessage(message) LogFile.write(message + "\n") # Call function to get extra columns cursor = arcpy.UpdateCursor(FinalNetworkLayerName) for featurerow in cursor: currentRow = currentRow + 1 if currentRow%100 == 0: message = "[INFO] Processed " + str(currentRow) + " of " + str(RoadNetworkCount) + " rows at " + str(datetime.datetime.now()) print message arcpy.AddMessage(message) LogFile.write(message + "\n") CurrentToid = featurerow.TOID # Establishing connection to MSSQL database sql = "SELECT * FROM [DWOP].[dbo].[ufunc_ReturnGGFData] ('" + CurrentToid + "'," + OfficeIDParm # Call function sde_return = sdeConn2.execute(sql) if isinstance(sde_return, list): for row in sde_return: featurerow.SEG_ID = row[0] featurerow.STL_NAME = row[1] featurerow.STR_NAME = row[2] featurerow.ST_CLASS = row[3] featurerow.ORIG_LVL = row[4] featurerow.DEST_LVL = row[5] featurerow.ONEWAY = row[6] featurerow.SPEED1 = row[7] featurerow.SPEED2 = row[8] featurerow.SPEED3 = row[9] featurerow.SPEED4 = row[10] featurerow.TRN_MODE = row[11] featurerow.BTH_MODE = row[12] featurerow.RESTR_ORI = row[13] featurerow.RESTR_DEST = row[14] cursor.updateRow(featurerow) del cursor, featurerow As I stated above. this works but is quite slow (averaging over 2 seconds per row update).It seems unlikely to me that such a global update could be done but I am new to ArcPy so may be missing something.CheersMark
message = "[INFO] Looping through selected RoadNetwork started at " + str(datetime.datetime.now()) print message arcpy.AddMessage(message) LogFile.write(message + "\n") # Call function to get extra columns cursor = arcpy.UpdateCursor(FinalNetworkLayerName) for featurerow in cursor: currentRow = currentRow + 1 if currentRow%100 == 0: message = "[INFO] Processed " + str(currentRow) + " of " + str(RoadNetworkCount) + " rows at " + str(datetime.datetime.now()) print message arcpy.AddMessage(message) LogFile.write(message + "\n") CurrentToid = featurerow.TOID # Establishing connection to MSSQL database sql = "SELECT * FROM [DWOP].[dbo].[ufunc_ReturnGGFData] ('" + CurrentToid + "'," + OfficeIDParm # Call function sde_return = sdeConn2.execute(sql) if isinstance(sde_return, list): for row in sde_return: featurerow.SEG_ID = row[0] featurerow.STL_NAME = row[1] featurerow.STR_NAME = row[2] featurerow.ST_CLASS = row[3] featurerow.ORIG_LVL = row[4] featurerow.DEST_LVL = row[5] featurerow.ONEWAY = row[6] featurerow.SPEED1 = row[7] featurerow.SPEED2 = row[8] featurerow.SPEED3 = row[9] featurerow.SPEED4 = row[10] featurerow.TRN_MODE = row[11] featurerow.BTH_MODE = row[12] featurerow.RESTR_ORI = row[13] featurerow.RESTR_DEST = row[14] cursor.updateRow(featurerow) del cursor, featurerow
サインインしたメンバーは投稿、更新のフォローなどができます。初めてですか?無料アカウントを登録してください。
Find useful guides, FAQs, and documents to help you navigate and make the most of Esri Community.