MySQL在服务器端提供只读的、单向的游标,而且只能在存储过程或者更底层的客户端API中使用。因为MySQL游标中指向的对象都是存储在临时表中而不是实际查询到的数据,所以MySQL游标总是只读的。它可以逐行指向查询结果,然后让程序做进一步的处理。在一个存储过程中,可以有多个游标,也可以在循环中“嵌套”地使用游标。
MySQL的游标设计也为粗心的人“准备”了陷阱。因为是使用临时表实现的,所以它在效率上给开发人员一个错觉。需要记住的最重要的一点是:当你打开一个游标的时候需要执行整个査询。考虑下面的存储过程:
1 CREATE PROCEDURE bad_cursor() 2 BEGIN 3 DECLARE film_id INT; 4 DECLARE f CURSOR FOR SELECT film_id FROM sakila.film; 5 OPEN f; 6 FETCH f INTO film_id; 7 CLOSE f; 8 END
从这个例子中可以看到,不用处理完所有的数据就可以立刻关闭游标。使用Oracle或 者SQL Server的用户不会认为这个存储过程有什么问题,但是在MySQL中,这会带来很多的不必要的额外操作。使用SHOW STATUS来诊断这个存储过程,可以看到它需要做1000个索引页的读取,做1000个写入。这是因为在表sakila.film中有1000条记录,而所有这些读和写都发生在第五行的打开游标动作。
这个案例告诉我们,如果在关闭游标的时候你只是扫描一个大结果集的一小部分,那么存储过程可能不仅没有减少开销,相反带来了大量的额外开销。这时,你需要考虑使用LIMIT来限制返回的结果集。
游标也会让MySQL执行一些额外的I/O操作,而这些操作的效率可能非常低。因为临时内存表不支持BLOB和TEXT类型,如果游标返回的结果包含这样的列的话,MySQL就必须创建临时磁盘表来存放,这样性能可能会很糟。即使没有这样的列,当临时表大于tmp_table_size的时候,MySQL也还是会在磁盘上创建临时表。
MySQL不支持客户端的游标,不过客户端API可以通过缓存全部查询结果的方式模拟客户端的游标。这和直接将结果放在一个内存数组中来维护并没有什么不同。