游標介紹:游標是一種從表中檢索數據並進行操作的靈活手段,游標主要用在服務器上,處理由客戶端發送給服務端的sql語句,或是批處理、存儲過程、觸發器中的數據處理請求。
游標的優點在於它允許應用程序對查詢語句select 返回的行結果集中每一行進行相同或不同的操作,而不是一次對整個結果集進行同一種操作;它還提供對基於游標位置而對表中數據進行刪除或更新的能力。缺點是處理大數據量時,效率低下,占用內存大。一般來說,能使用其他方式處理數據時,最好不要使用游標,除非是當你使用while循環,子查詢,臨時表,表變量,自建函數或其他方式都無法處理某種操作的時候,再考慮使用游標。
游標是系統為用戶開設的一個數據緩沖區,存放SQL語句的執行結果。游標的一個常見用途就是保存查詢結果,以便以后使用。游標的結果集是由SELECT語句產生,如果處理過程需要重復使用一個記錄集,那么創建一次游標而重復使用若干次,比重復查詢數據庫要快的多。
PostgreSQL游標可以封裝查詢並對其中每一行記錄進行單獨處理。當我們想對大量結果集進行分批處理時可以使用游標,因為一次性處理可能造成內存溢出。另外我們可以定義函數返回游標類型變量,這是函數返回大數據集的有效方式,函數調用者根據返回游標對結果進行處理。
游標使用順序:聲明游標 > 打開游標 > 使用游標 > 關閉游標 。
先展示一個游標的示例,以下get_film_titles(integer)函數接受代表電影發行年份的參數。在函數內部,我們查詢所有發行年份等於傳遞給該函數的發行年份的電影。我們使用光標在各行之間循環,並連接標題和標題包含ful 單詞的電影發行年份。
CREATE OR REPLACE FUNCTION get_film_titles(p_year INTEGER) RETURNS text AS $$ -- 聲明游標 DECLARE titles TEXT DEFAULT ''; rec_film RECORD; cur_films CURSOR(p_year INTEGER) FOR SELECT * FROM film WHERE release_year = p_year; BEGIN -- 打開游標 OPEN cur_films(p_year); LOOP -- 獲取記錄放入film FETCH cur_films INTO rec_film; -- exit when no more row to fetch EXIT WHEN NOT FOUND; -- 構建輸出 IF rec_film.title LIKE '%ful%' THEN titles := titles || ',' || rec_film.title || ':' || rec_film.release_year; END IF; END LOOP; -- 關閉游標 CLOSE cur_films; RETURN titles; END; $$ LANGUAGE plpgsql; SELECT get_film_titles(2006); --返回結果 ,Grosse Wonderful:2006,Day Unfaithful:2006,Reap Unfaithful:2006,Unfaithful Kill:2006,Wonderful Drop:2006
一、聲明游標
PostgreSQL聲明游標有兩種方法,一種是使用特殊類型REFCURSOR聲明游標變量,另一種是聲明和查詢綁定使用。
-- 第一種方式 DECLARE my_cursor REFCURSOR; -- 第二種方式 cursor_name [ [NO] SCROLL ] CURSOR [( name datatype, name data type, ...)] FOR query;
首先,為光標指定一個變量名。接着指定是否可以使用向后滾動光標SCROLL,如果使用NO SCROLL,則光標無法向后滾動。然后,在CURSOR關鍵字后面加上一個逗號分隔的參數列表(name datatype),這些參數定義了查詢的參數。打開游標時,這些參數將被值替換。最后,可以在FOR關鍵字之后指定查詢,使用任何有效的SELECT語句。
示例:
DECLARE cur_films CURSOR FOR SELECT * FROM film; cur_films2 CURSOR (year integer) FOR SELECT * FROM film WHERE release_year = year; -- 該cur_films 包含film表所有行。 -- 本cur_films2 包含film表特定發行年份的記錄。
二、打開游標
PostgreSQL提供了用於打開未綁定和綁定的游標的語法。
1.打開未綁定的游標
OPEN unbound_cursor_variable [ [ NO ] SCROLL ] FOR query;
-- 由於聲明時未綁定的游標變量未綁定到任何查詢,因此在打開它時必須指定查詢。請參見以下示例: OPEN my_cursor FOR SELECT * FROM city WHERE counter = p_country; -- PostgreSQL允許我們打開游標並將其綁定到動態查詢。語法如下: OPEN unbound_cursor_variable[ [ NO ] SCROLL ] FOR EXECUTE query_string [USING expression [, ... ] ]; -- 在下面的示例中,我們構建一個動態查詢,該動態查詢根據一個sort_field參數對行進行排序,並打開執行該動態查詢的游標。 query := 'SELECT * FROM city ORDER BY $1'; OPEN cur_city FOR EXECUTE query USING sort_field;
2.打開綁定的游標
因為綁定游標在聲明時已經綁定到查詢,所以當我們打開它時,只需要在必要時將參數傳遞給查詢。
OPEN cursor_variable[ (name:=value,name:=value,...)];
-- 在下面的示例中,我們打開了綁定游標,cur_films並cur_films2在上面聲明了該游標: OPEN cur_films; OPEN cur_films2(year:=2005);
三、使用游標
使用FETCH,MOVE,UPDATE或DELETE語句操作游標。
1.獲取下一行
FETCH [ direction { FROM | IN } ] cursor_variable INTO target_variable;
該FETCH語句從游標中獲取下一行,並為其分配一個target_variable,它可以是記錄,行變量或逗號分隔的變量列表。如果找不到更多行,則將target_variable其設置為NULL(s)。
如果不顯示指定方向,方向缺省為NEXT。可以有下面值:
- NEXT
- LAST
- PRIOR
- FIRST
- ABSOLUTE count
- RELATIVE count
- FORWARD
- BACKWARD
請注意,FORWARD和BACKWARD方向僅適用於用SCROLL option 聲明的游標。
示例:
FETCH cur_films INTO row_film; FETCH LAST FROM row_film INTO title, release_year;
2.移動光標
MOVE [ direction { FROM | IN } ] cursor_variable;
如果只想移動游標而不檢索任何行,則使用該MOVE語句。方向接受與FETCH語句相同的值。
MOVE cur_films2; MOVE LAST FROM cur_films; MOVE RELATIVE -1 FROM cur_films; MOVE FORWARD 3 FROM cur_films;
3.刪除和更新行
使用DELETE WHERE CURRENT OF或UPDATE WHERE CURRENT OF語句刪除或更新游標標識的行。
UPDATE table_name SET column = value, ... WHERE CURRENT OF cursor_variable; DELETE FROM table_name WHERE CURRENT OF cursor_variable;
示例: UPDATE film SET release_year = p_year WHERE CURRENT OF cur_films;
四、關閉游標
使用CLOSE關閉打開的游標,CLOSE語句釋放資源或釋放游標變量,以允許使用該OPEN語句再次打開它。
CLOSE cursor_variable;
五、其他
-- 臨時表返回結果例子 BEGIN; DO $$ DECLARE temp_geometry st_geometry; geometry_record RECORD; cur_geometry CURSOR FOR SELECT shape as shape FROM mainbasin; BEGIN OPEN cur_geometry; FETCH cur_geometry INTO temp_geometry; LOOP FETCH cur_geometry INTO geometry_record; EXIT WHEN NOT FOUND; temp_geometry := st_union(temp_geometry,geometry_record.shape); END LOOP; CLOSE cur_geometry; DROP TABLE IF EXISTS temp_table; CREATE TEMP TABLE temp_table AS SELECT st_envelope(temp_geometry) shape; END; $$; COMMIT; SELECT st_astext(shape) FROM temp_table;