游标cursor,我想大多数人都在sql server里面用过。当一个表数据量不太大的时候,游标还是可以用的,毕竟游标是循环一个表中每一行数据的最简便办法。但是如果你用一个游标去循环一个没有主键或唯一键的表会发生什么呢?
我们来看看这个例子,我们声明了一个临时表#Foo并插入了一行数据,这个表没有主键,然后我们使用了一个名叫ID的游标去更新这个表[Name]列的数据,执行下面的语句看看会发生什么?
CREATE TABLE #Foo( [ID] [smallint] IDENTITY(1,1), [Code] [char](3) NULL, [Name] [varchar](50) NULL, [ProvinceID] [tinyint] NULL ) go Insert #Foo(Code, Name, ProvinceID) Select 'A', 'New York', 3; Declare ID Cursor For Select top 5 ID From #Foo Order by ID Open ID; Fetch From ID ; Update #Foo Set Name+='1' Where Current OF ID Close ID ; Deallocate ID Select * From #Foo go Drop Table #Foo
执行结果如下:
你会发现sql server报错了,提示你声明的游标ID是一个READ_ONLY的只读游标。READ_ONLY游标意味着声明的游标只能读取数据,游标不能做任何更新操作,而我们上面的语句使用了游标ID来更新表#Foo的数据,所以报错了。
现在我们把上面的语句改成下面的,主要就是在声明表#Foo的时候将列[ID]声明为了主键,再执行下面的语句:
CREATE TABLE #Foo( [ID] [smallint] IDENTITY(1,1), [Code] [char](3) NULL, [Name] [varchar](50) NULL, [ProvinceID] [tinyint] NULL CONSTRAINT [PK_Foo] PRIMARY KEY CLUSTERED ( [ID] ASC ) ) go Insert #Foo(Code, Name, ProvinceID) Select 'A', 'New York', 3; Declare ID Cursor For Select top 5 ID From #Foo Order by ID Open ID; Fetch From ID ; Update #Foo Set Name+='1' Where Current OF ID Close ID ; Deallocate ID Select * From #Foo go Drop Table #Foo
执行结果如下:
这一次游标ID没有报错了,语句顺利执行。
为什么将游标用于不带主键或唯一键的表后,游标会变为READ_ONLY的?
If your table does not have a unique index (or a primary key constraint or unique key constraint, both of which create a unique index behind the scenes), then you dynamic cursor is converted to a static cursor. And all static cursors must be read only.
If one of the tables referenced by the CURSOR has no unique index, the CURSOR will be converted to STATIC. And STATIC cursors are READ-ONLY. See Using Implicit Cursor Conversions for more information.
所以使用游标的时候最好都给表加上主键或唯一键,这样使用游标更新数据的时候就不会报错了。