I recently got stuck with this problem for seemingly no reason, since my cursor was not declared read-only. I had to spend hours googling around and experimenting for a solution.
The Problem
I use Microsoft Visio to manage my SQL Server database. When you add/edit columns, indices, constraints etc., Visio will generate a script to update the database. Depending on the situation, usually the script generated creates a back-up of the current table, removes all constraints, indices on the table, then drops the table and recreates its. Then puts back the data from backed-up table and finally reinstates the indices, constraints etc.
I had a situation where I had to add an indexed column to a table. I decided to put the data for the existing records for this column using cursor since I had to traverse row-by-row.
I manually inserted the following code in the script file.
-- visio generated script upto create new table before this line --=========================================================================== -- Migrate old data to newly created table Set identity_insert "dbo"."SubTicket" on go insert into "dbo"."SubTicket" ("SubTicketId", "TicketId", OtherColumnsHere) select "SubTicketId", "TicketId", OtherColumnsHere from "dbo"."SubTicket_IM0" go Set identity_insert "dbo"."SubTicket" off go -- update the data in new column for existing records DECLARE @TicketId bigint DECLARE @TicketIdPrev bigint DECLARE @SubTicketId bigint DECLARE @SubTicketNumber int DECLARE @RowNum int DECLARE SubTicketList CURSOR LOCAL FOR SELECT SubTicketId, TicketId, SubTicketNumber FROM SubTicket ORDER BY TicketId, SubTicketId FOR UPDATE OF SubTicketNumber OPEN SubTicketList FETCH NEXT FROM SubTicketList INTO @SubTicketId, @TicketId, @SubTicketNumber WHILE @@FETCH_STATUS <> -1 BEGIN IF (@TicketId = @TicketIdPrev) SET @RowNum = @RowNum + 1 ELSE SET @RowNum = 1 SET @TicketIdPrev = @TicketId UPDATE SubTicket SET SubTicketNumber = @RowNum WHERE CURRENT OF SubTicketList FETCH NEXT FROM SubTicketList INTO @SubTicketId, @TicketId, @SubTicketNumber END CLOSE SubTicketList DEALLOCATE SubTicketList go --=========================================================================== -- visio generated script after create new table (restore constraints etc.) after this line
But after putting this code, trying to run the code causes a series of errors with message
Msg 16929, Level 16, State 1, Line 16
The cursor is READ ONLY.
The statement has been terminated.
The Solution
Found out by collecting information from various online resources and experimenting with the code that unless the table has a primary key defined, the update cursor statement with "WHERE CURRENT OF" won’t work, and will fail with the error mentioned above, irrespective of whether the table contains duplicate records or not.
Once the problem was found, the solution was easy. I moved the Visio generated code that reinstates the primary key on the table above this code, which was otherwise after this.
How this works
If a cursor is cursor opened for update and has a "WHERE CURRENT OF" specified in its UPDATE statement, it won’t work unless the table has a primary key. You may put a primary key column on the table. If that is not possible, use UPDATE statements without the "WHERE CURRENT OF" part, just like any other t-sql UPDATE statement preferably with a WHERE clause.
I hope this tip saves someone’s day.
February 10, 2011 at 4:54 pm
It saved my day. Thank you for the tip!
/Martin
March 22, 2011 at 3:44 pm
Saved my day, too!
How ridiculous is that? If it hasn’t got a primary key, does it matter if there are duplicates? If there are, that might be the reason it has not been given a primary key.
March 22, 2011 at 3:46 pm
Whether or not there are duplicates, primary key is a must. In my case, there were no duplicates but no primary key either.
April 10, 2012 at 2:26 am
Thanks for the tip. Saved my day too!
May 22, 2012 at 8:14 am
Thanks a lot!!!
June 15, 2012 at 9:11 pm
opening a cursor for a table with a new “order by” will make it read only; sort the original the way you want the cursor to use it and you don’t need a primary key
July 24, 2012 at 4:45 pm
Wow. This really saved me a lot of time! Thanks!
July 25, 2012 at 7:50 am
I’m glad it helped you 🙂
September 28, 2012 at 5:09 am
This is the best tip I have been read because I lost more six hours to resolve this problem. I will send to Microsoft Support this bug. Thanks!
October 3, 2012 at 1:50 pm
Great find, two years later it saved my day too. Thanks
August 22, 2013 at 3:51 pm
Saved my day!
March 24, 2015 at 5:16 pm
You will go to the sky…! Saved my life, jeje…! TKS.
August 2, 2017 at 2:20 pm
That was such a great tip, I was pulling the little hair that I have left…. excellent advise
February 8, 2018 at 7:56 am
TxU, this helps me a lot
April 26, 2018 at 5:11 pm
thanks you. Helped me as well. Nicolai’s comment also added to the solution.
June 17, 2019 at 7:49 am
Thanks a lot!