MySQL遊標修改記錄——解決最後一行重複的問題
在MySQL儲存過程或函式中,已知fetch語句用來從遊標中提取一條記錄,配合迴圈語句可以 實現整個結果集的遍歷。
- 儲存過程或函式
- declare 遊標:申明遊標
- open 遊標:開啟遊標 –>建立結果集
- fetch 遊標:提取資料 –>讀取第一行記錄
- 處理結果集中的資料
- close 遊標:關閉遊標 –>回收結果集
但是如果不使用正確的語法來控制結果集的遍歷,就會出現一些意想不到的麻煩。接下來以一個具體的應用場景來說明我在使用遊標時遇到的問題以及是如何解決該問題的。現在已知資料庫mytest中有一個學生成績表score:
select * from mytest.score;
+----------+------------+-------------+-------+
| score_id | course_num | student_num | score |
+----------+------------+-------------+-------+
| 1 | 1 | 15001 | 89 |
| 2 | 1 | 15002 | 78 |
| 3 | 1 | 15003 | 80 |
| 5 | 2 | 15001 | 85 |
| 6 | 2 | 15002 | 60 |
| 7 | 2 | 15003 | 75 |
| 9 | 3 | 15001 | 87 |
| 10 | 3 | 15002 | 77 |
| 11 | 3 | 15003 | 88 |
| 13 | 4 | 15001 | 90 |
| 14 | 4 | 15002 | 98 |
| 15 | 4 | 15003 | 89 |
| 16 | 4 | 16005 | 52 |
| 17 | 4 | 16007 | 83 |
+----------+------------+-------------+-------+
接下來需要做的事情:將該課程所有學生的成績加5分,但是總分不能超過100分,修改後的成績介於55分~59分之間時將這些學生的成績修改為60分。
最初使用下面的程式碼可以建立update_course_score_proc()的儲存過程,該儲存過程接受c_no作為輸入引數,該輸入引數決定了需要修改那一門課程的成績。儲存過程中申明瞭一個score_cursor的遊標,儲存過程的具體程式碼如下:
delimiter $
CREATE procedure update_course_score_proc(in c_no int)
modifies sql data
BEGIN
declare s_num ,grade int;
declare state int;
declare score_cursor cursor for SELECT student_num,score FROM score WHERE c_no = score.course_num;
DECLARE CONTINUE HANDLER FOR 1329 SET state = 1;##自定義錯誤程式
open score_cursor;
repeat
fetch score_cursor into s_num,grade;
SET grade = grade + 5;
if (grade > 100) then
set grade = 100;
end if;
if (grade >= 55 && grade <= 59) then
set grade = 60;
end if;
UPDATE score set score = grade WHERE score.student_num = s_num and score.course_num = c_no;
until state = 1
end repeat;
close score_cursor;
end
$
delimiter ;
其中的state是正對MySQL錯誤程式碼1329自定義的錯誤程式,當遍歷遊標結果集時,最後一行提取後再提取下一行時將丟擲該錯誤,據此可以用來結束結果集的遍歷。
執行下面的select語句,查詢課程course_num=4的所有學生的成績,呼叫“call update_course_score_proc()”之後課程course_num=4的所有學生成績如下:
select * from mytest.score;
+----------+------------+-------------+-------+
| score_id | course_num | student_num | score |
+----------+------------+-------------+-------+
| 1 | 1 | 15001 | 89 |
| 2 | 1 | 15002 | 78 |
| 3 | 1 | 15003 | 80 |
| 5 | 2 | 15001 | 85 |
| 6 | 2 | 15002 | 60 |
| 7 | 2 | 15003 | 75 |
| 9 | 3 | 15001 | 87 |
| 10 | 3 | 15002 | 77 |
| 11 | 3 | 15003 | 88 |
| 13 | 4 | 15001 | 90 |
| 14 | 4 | 15002 | 100 |
| 15 | 4 | 15003 | 94 |
| 16 | 4 | 16005 | 60 |
| 17 | 4 | 16007 | 93 |
+----------+------------+-------------+-------+
結果發現course_num=4的最後一行記錄和預期的不一樣,正常情況下應該是score=88,但是現在是score=93,從結果可以推斷出最後一行的記錄被多修改了一次,即score被重複加上一次5。但是我儲存過程中定義了錯誤處理變數state,而且執行最後一行後沒有記錄了,state=1將會退出repeat迴圈。重新檢查一次repeat部分程式碼,發現當提取最後一行記錄並修改後,此時的state並不為1,因為下一行的fetch執行還沒有執行,所以repeat還會再執行一次;當提取下一行時,有已經沒有了結果,所以state會被賦值為1,但後面的語句並沒有停止執行,s_num和grade的值仍然是上一次記錄即結果集中的最後一行記錄的值,因而最後一行記錄的grade被多修改了一次,score結果不是88而是93。
repeat
fetch score_cursor into s_num,grade;
SET grade = grade + 5;
if (grade > 100) then
set grade = 100;
end if;
if (grade >= 55 && grade <= 59) then
set grade = 60;
end if;
UPDATE score set score = grade WHERE score.student_num = s_num and score.course_num = c_no;
until state = 1
end repeat;
解決該問題的方法有多種,最簡單的方法是將自定義處理錯誤程式的continue型別改成exit型別,另一種是在執行repeat迴圈之前提取一第一行記錄,再在update更新語句之後提取下一行記錄:
delimiter $
CREATE procedure update_course_score_proc(in c_no int)
modifies sql data
BEGIN
declare s_num,grade int;
declare state int;
declare score_cursor cursor for SELECT student_num,score FROM score WHERE c_no = score.course_num;
DECLARE CONTINUE HANDLER FOR 1329 SET state = 1;
open score_cursor;
fetch score_cursor into s_num,grade;
repeat
SET grade = grade + 5;
if (grade > 100) then
set grade = 100;
end if;
if (grade >= 55 && grade <= 59) then
set grade = 60;
end if;
UPDATE score set score = grade WHERE score.student_num = s_num and score.course_num = c_no;
fetch score_cursor into s_num,grade;
until state = 1
end repeat;
close score_cursor;
end
$
delimiter ;
修改之前應該刪除之前的儲存結構,最後對修改後的儲存結構進行檢驗,set @c_no = 1;然後呼叫儲存過程,得到以下結果:
select * from mytest.score;
+----------+------------+-------------+-------+
| score_id | course_num | student_num | score |
+----------+------------+-------------+-------+
| 1 | 1 | 15001 | 94 |
| 2 | 1 | 15002 | 83 |
| 3 | 1 | 15003 | 85 |
| 5 | 2 | 15001 | 85 |
| 6 | 2 | 15002 | 60 |
| 7 | 2 | 15003 | 75 |
| 9 | 3 | 15001 | 87 |
| 10 | 3 | 15002 | 77 |
| 11 | 3 | 15003 | 88 |
| 13 | 4 | 15001 | 90 |
| 14 | 4 | 15002 | 100 |
| 15 | 4 | 15003 | 94 |
| 16 | 4 | 16005 | 60 |
| 17 | 4 | 16007 | 93 |
+----------+------------+-------------+-------+
course_num=1中的最後一行記錄score=85,與預期的結果一致。
相關推薦
MySQL遊標修改記錄——解決最後一行重複的問題
在MySQL儲存過程或函式中,已知fetch語句用來從遊標中提取一條記錄,配合迴圈語句可以 實現整個結果集的遍歷。 儲存過程或函式 declare 遊標:申明遊標 open 遊標:開啟遊標 –>建立結果集 fetch 遊標:提取資料 –>讀取第一
mysql遊標最後一行重複的問題
最近寫儲存的時候,使用遊標迴圈插入記錄時,發現最後一行重複了一次,在百度和google搜尋了一下,有個回答比較靠譜,但沒有說出所以然來( ),後來又用英文單詞在google上 mysql cursor last line repeat (英文不好,請見諒)搜尋了一下,sta
plsql遊標最後一行重複的問題
大家仔細看一下,下面第一個儲存過程,test01,有沒問題? 看似沒問題,其實會造成重複行,test02將exit when的語句放到合適的位置上來。就不會出現最後一行重複列印的問題。 create or replace procedure test01 as c
《MySQL必知必會》第24章 使用遊標 中的bug:最後一行被重複INSERT
在看《MySQL必知必會》第24章 使用遊標的時候,手打儲存過程processorders()的時候,發現ordertotals總是有重複的一行: 經仔細校對原書的程式碼,沒有發現問題;上網看別人的部落格,讀書筆記,暫時沒有發現有人提到這個問題,還有的人程式碼跟書裡一
fgets()重複讀取最後一行的分析及解決方法
使用 fgets() 讀取一個文字檔案的時候,如果讀取的 方法不恰當,就有可能造成重複讀取最後一行的問題。具體如下。 (1) 在 Windows 平臺上, 假設有 test.txt ,內容如下: this is line 1 this is lin
MySQL 查詢表中某個欄位值重複的記錄
MySQL中,查詢表(dat_bill_2018_11)中欄位(product_id)值重複的記錄: SELECT product_id, COUNT(*) AS COUNT FROM dat_bill_201811 GROUP BY product_id HAVING COUNT > 1;
mysql中刪除重複記錄,並保留重複資料中的一條資料的SQL語句理解
正好想寫一條刪除重複語句並保留一條資料的SQL,網上查了一部分資料寫的很詳細,但還是在這裡寫下自己的理解,以遍後續學習 。如下: 表字段和資料: SQL語句: DELETE FROM `user` WHERE id NOT IN(SELECT * FROM(
[問題記錄]解決RabbitMQ訊息丟失與重複消費問題
本文僅記錄排查和問題定位、解決的過程。 1. 背景 最近使用者反饋提交的SQL查詢一直處於長時間等待狀態,經過排查觀察,發現部分查詢請求丟失,導致使用者提交的查詢未被正常接收,繼而長時間無響應。 現象:集市SQL控制檯提交10個簡單SQL查詢 -&
Oracle 如何將某一行記錄放在查詢結果的第一行【最後一行】
場景:已知藥品費用所佔比例的公式是藥品費用與總費用之間的比例,通過如下SQL語句已求出各個機構的藥品費用所佔比例, 查詢各機構藥品費用所佔比例的SQL語句: select t1.parent_id org_id, --機構id dec
python中讀取txt檔案,windows下麼有毛病,到Linux下總是隻有最後一行有效,怎麼解決?
比如:這個txt有5行, fin = open('F:\\temp\\name.txt','r') for line in fin: strsname = line.rstrip('\n') AAA('F:\\temp','F:\\temp\\2',str
解決sql 過濾重複資料記錄的方法(Oracle)
分別建立了兩個臨時表,並按不同的欄位作查詢示例: create table table1 (id int, ip varchar(15), city char(20)); insert into
重灌Mysql失敗,卡在最後一步解決辦法
重灌mysql的時候,總是在提交配置後的最後一步,安裝失敗,程序管理器裡顯示程式無響應,mysql服務啟動時報1067錯誤.整了好幾天,用了網上好幾種方法都沒能成功,最後抱著試一試的態度用了以下步驟
mysql裡面的while裡面為什麼會重複輸出最後一個數據
mysql裡面的while執行機制是先執行再判斷,當監聽語句判斷tag=1的時候,while裡面的語句會繼續執行,但是監聽語句裡面的tag=1會讓fetch語句失效,進而接著執行1select uid語句,所以會輸出兩次最後一個 eg: create procedure
安裝MySql時,卡在最後一步,Attempting to start service 的解決辦法
今天,由於專案的需要,給自己的電腦安裝mysql 5.6.11,第一次的時候安裝成功了,但是由於我沒有選擇安裝 client prograss,所以就沒辦法通過命令列登入到mysql, 然後我就解除安裝了重灌,從第二次開始,一共重試了十幾次吧,還是沒有成功,包括什麼刪除安裝
命令列下mysql資料庫插入記錄中包含中文1366錯誤問題解決方法及其他
出於方便國際化考慮,我把java開發環境和資料庫及表的編碼設定都設定成了utf8。這樣的設定在eclipse環境中向mysql存取資料時沒遇到問題。 但今天在使用命令列向mysql插入中文資料時遇到了1366錯誤。google到以下答案: mysql#1366錯誤是在my
mysql權限修改記錄
uri courier pri sele all mysq span mysql pda p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 16.0px Courier; color: #4d2f2d; background-c
mysql 在修改新增欄位(alter table 表名 add column 或者 modify column)且帶unique時提示duplicate entry for key的原因以及解決方案
今天在公司臨時維護一張表時,我作了一個小動作,新增一個欄位,並且設定為unique時,盡然無法新增欄位,我當時就納悶了,寫了這麼多sql,這麼奇怪的問題還是第一次見,不多說,直接看圖 【我的sql檔案如下】 【執行sql語句報錯:alter table smart_
mysql密碼修改與密碼丟失的解決方案
密碼的修改 方法一:命令列中修改 mysqladmin -uroot -p password 'your_password' 方法二:進入mysql命令列用sql語句進行修改 這種方法適合於不記得root密碼之後進行修改。 注意這裡需使用passwor
關於mysql中刪除重複記錄,並保留重複資料中的一條資料的SQL語句理解
正好想寫一條刪除重複語句並保留一條資料的SQL,網上查了一部分資料寫的很詳細,但還是在這裡寫下自己的理解,以遍後續學習 。如下: 表字段和資料: SQL語句: DELETE FROM `user` WHERE id NOT IN(SELECT * FROM(SELE
Mysql分組統計、排序、取前N條記錄解決方案
今日根據專案需求,需要在mysql中解決記錄的分組統計、排序,並抽取前10條記錄的功能。現已解決,解決方案如下: 1)表結構 <span style="font-size:18px;">CREATE TABLE `policy_keywords_rel` (