Why UpdateCursor Returns- RuntimeError: workspace already in transaction mode

550
10
Jump to solution
08-19-2020 01:32 PM
DevinUnderwood3
New Contributor III

I am trying to use an UpdateCursor to update a feature class by referencing a Search Cursor.

Here is a sample of my code. Note that I consulted the following ESRI document https://support.esri.com/en/technical-article/000019111 to troubleshoot. The feature class is registered as versioned and I created the cursor as an object as opposed to a loop. Edit.startEditing is set to False ,False as suggested in the ESRI document.

import arcpy

arcpy.env.workspace = r'Database Connections\THIS IS A VERSION'

# Create variables
FC1 = r'Database Connections\...
FC2 = r'Database Connections\...
fields = ['A','B','C'] expression = "B = 6"

#Search Cursor for item 1
scursor = arcpy.da.SearchCursor(FC2,fields,where_clause=expression)

# Process: Start Edit
edit = arcpy.da.Editor(arcpy.env.workspace)
edit.startEditing(False,False)
edit.startOperation()

# UpdateCursor for item 2
cursor = arcpy.da.UpdateCursor(FC1, fields)
for row in cursor:
row[1] == scursor[row[1]]
cursor.updateRow(row)
print(u'{0},{1},{2}'.format(row[0],row[1],row[2]))

del row
del cursor

edit.stopOperation()
edit.stopEditing(True)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
1 Solution

Accepted Solutions
JasonBartley1
New Contributor

Hi Devin,

Not sure exactly how you're trying to update fc1 from fc2, but if the B field is the unique identifier between the two this could be a solution for you.

import arcpy
arcpy.env.workspace = r'Database Connections\THIS IS A VERSION'

# Create variables
FC1 = r'Database Connections\...
FC2 = r'Database Connections\...
fields = ['A','B','C']

# removed expression to pull all values from FC2
# expression = "B = 6"

# empty dictionary to hold values from FC2
# assuming B field is unique and is used as field to update FC1
# use with, populate dictionary, then search cursor closes automatically
fc2_lookup = {}
with arcpy.da.SearchCursor(FC2, fields) as cursor:
   for row in cursor:
      # create variables to hold row values
      # not necessary, but helps with readability
      a = row[0]
      b = row[1]
      c = row[2]

      # B field is key to find other field values in list
      fc2_lookup[b] = ["A": a, "C": c]

# Process: Start Edit
edit = arcpy.da.Editor(arcpy.env.workspace)
edit.startEditing(False,False)
edit.startOperation()

# use with again for updatecursor
with arcpy.da.UpdateCursor(FC1, fields) as cursor:
   for row in cursor:

      # FC1 fields to variables
      a = row[0]
      b = row[1]
      c  = row[2]
      print("initial row: A: {}, B: {}, C:{}".format(a, b, c))

      # check B column to see if it's in lookup
      if b in fc2_lookup:

         # update a field in row using values from dictionary
         row[0] = fc2_lookup[b]["A"]
         row[2] = fc2_lookup[b]["C"]
         print("updated row: A: {}, B: {}, C:{}".format(row[0], row[1], row[2]))
         cursor.updateRow(row)
      else:
         # depending on data you might want to investigate why it's missing
         print('{} not found in lookup'.format(b))

# don't need to delete, with statement cleans these up
# del row
# del cursor

edit.stopOperation()
edit.stopEditing(True)

View solution in original post

0 Kudos
10 Replies
JohannesLindner
Occasional Contributor II

It's not really clear to me what you're trying to do. You extract 3 fields from FC2 with a query. What do you want to do to FC1 and how does FC1 relate to FC2?

Anyway, this won't work:

row[1] == scursor[row[1]]     

arcpy.da.SearchCursor returns a generator object, not an iterable (like a list). You can turn the generator into a list, though:

generator = arcpy.da.SearchCursor(FC2,fields,where_clause=expression)
print(generator)

actual_rows = [row for row in generator]
print(actual_rows)

# or short, because you probably don't need the generator:
actual_rows = [row for row in arcpy.da.SearchCursor(FC2, fields, expression)]

Have a great day!
Johannes
DevinUnderwood3
New Contributor III

I wanted to copy all records information from FC1 field 1 to FC2 matching field.

0 Kudos
DevinUnderwood3
New Contributor III

Thank you for providing your response. Can you explain how your example includes feature class 1 in a search cursor which will be referenced in the update cursor feature class 2 ? Copying from feature class 1 to feature class 2 attribute information.

0 Kudos
JohannesLindner
Occasional Contributor II

You can't use multiple cursors at the same time. it MIGHT be possible, but in most cases it raises an error like the RuntimeError you got.

The way to do this is to first pull the information from FC2 with a SearchCursor and then use an UpdateCursor to write the matching records into FC1.

To find the matching records, a dictionary is the best way. See Jason's answer below.


Have a great day!
Johannes
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

I think the following over simplifies the situation:

You can't use multiple cursors at the same time.

For starters, you can have as many search cursors as you like accessing the same data set at the same time.  It may be your statement was meant for update cursors, but it is also possible to have many update and insert cursors pointing to the same data set at the same time with one big caveat.  The way Esri has implemented its update and insert cursors, a user needs to have an edit session open (arcpy.da.Editor) to manipulate data simultaneously through multiple cursors.

0 Kudos
JohannesLindner
Occasional Contributor II

Thanks, you're right, of course.

I just realized that I used this caveat myself in some old code to check whether I had an open edit session in ArcMap (point two UpdateCursors at the same dataset, if that produces an error, there is no edit session for that workspace).


Have a great day!
Johannes
0 Kudos
JasonBartley1
New Contributor

Hi Devin,

Not sure exactly how you're trying to update fc1 from fc2, but if the B field is the unique identifier between the two this could be a solution for you.

import arcpy
arcpy.env.workspace = r'Database Connections\THIS IS A VERSION'

# Create variables
FC1 = r'Database Connections\...
FC2 = r'Database Connections\...
fields = ['A','B','C']

# removed expression to pull all values from FC2
# expression = "B = 6"

# empty dictionary to hold values from FC2
# assuming B field is unique and is used as field to update FC1
# use with, populate dictionary, then search cursor closes automatically
fc2_lookup = {}
with arcpy.da.SearchCursor(FC2, fields) as cursor:
   for row in cursor:
      # create variables to hold row values
      # not necessary, but helps with readability
      a = row[0]
      b = row[1]
      c = row[2]

      # B field is key to find other field values in list
      fc2_lookup[b] = ["A": a, "C": c]

# Process: Start Edit
edit = arcpy.da.Editor(arcpy.env.workspace)
edit.startEditing(False,False)
edit.startOperation()

# use with again for updatecursor
with arcpy.da.UpdateCursor(FC1, fields) as cursor:
   for row in cursor:

      # FC1 fields to variables
      a = row[0]
      b = row[1]
      c  = row[2]
      print("initial row: A: {}, B: {}, C:{}".format(a, b, c))

      # check B column to see if it's in lookup
      if b in fc2_lookup:

         # update a field in row using values from dictionary
         row[0] = fc2_lookup[b]["A"]
         row[2] = fc2_lookup[b]["C"]
         print("updated row: A: {}, B: {}, C:{}".format(row[0], row[1], row[2]))
         cursor.updateRow(row)
      else:
         # depending on data you might want to investigate why it's missing
         print('{} not found in lookup'.format(b))

# don't need to delete, with statement cleans these up
# del row
# del cursor

edit.stopOperation()
edit.stopEditing(True)

View solution in original post

0 Kudos
JohannesLindner
Occasional Contributor II
# Just FYI:

# instead of this:
with ... as cursor:
for row in cursor:
a = row[0]
b = row[1]
c = row[2]
...

# you can just do this:
with ... as cursor:
for a, b, c in cursor:
...‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Have a great day!
Johannes
0 Kudos
DevinUnderwood3
New Contributor III

Thank you for providing the sample code. I incorporated it into my script and it worked exactly as I wanted.

0 Kudos