Mysql同一個事務內記錄成功插入後查詢不出來

背景:
1)mysql:Ver 14.12 Distrib 5.0.45, for Win32 (ia32)
2)mysql odbc驅動:3.51.22
3)vs2005
4)客戶端用ado,odbc驅動連接mysql數據庫
5)表tb_5100定義如下:
CREATE TABLE `tb_5100` (
  `account_id` BIGINT(20) NOT NULL,
  `service_id` INT(11) NOT NULL,
  `f002d_5105` DATE NOT NULL,
  `object_id` INT(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY  (`object_id`)
) ENGINE=INNODB DEFAULT CHARSET=gbk;
object_id字段AUTO_INCREMENT(自增長)
f002d_5105字段date類型,不能爲空

問題:
開啓一個事務
往tb_5100表插入一條記錄,f002d_5105爲空或者''(違反非空約束);
獲取新記錄的自增長字段object_id的值
根據自增長字段object_id查詢新記錄查不出來
提交事務
記錄已經插入到數據庫中,用mysql客戶端工具可以查詢出記錄。
示例代碼如下所示:
pdbor->BeginTrans();
string sql = "INSERT INTO tb_5100(account_id,service_id,f002d_5105) VALUES(10068,300,'')";
if( !pdbor->Execute(adCmdText,sql.c_str()) ){
pdbor->RollbackTrans();
return -1;
}
///<獲得自增長字段值
unsigned __int64 service_serial;
if( pdbor->GetDBExt()->GetLastID(service_serial,1,"") ){
pdbor->RollbackTrans();
return -2;
}
sql = LogMsg("SELECT * FROM tb_5100 where object_id=%d",service_serial);
CRecordset *prs = pdbor->Query(adCmdText,sql.c_str());
if( prs == NULL ){
pdbor->RollbackTrans();
return -3;
}
if( prs->IsEof() ){
pdbor->RollbackTrans();
return -4;///<查詢不出來,函數返回-4
}
pdbor->CommitTrans();
return 0;

經多次實驗,
將f002d_5105字段改爲允許爲NULL,記錄可以成功插入,在事務內,可以查詢出新記錄。
將數據庫模式設置爲STRICT_TRANS_TABLES嚴格模式(修改my.ini,在[mysqld]段,設置sql-mode="STRICT_TRANS_TABLES"),insert失敗。

分析:
導致問題的原因爲日期型字段f002d_5105數據非法的問題。新記錄插入後,f002d_5105字段的值爲'0000-00-00',爲非法的日期型數據。MySQL是允許無效數據的存在的。詳見http://it.china-b.com/sjk/mysql/20090828/179324_1.html(mysql中對無效數據的約束)。
而由於非法數據,導致同一個事務內能插入成功,但查詢不來的問題,確實不解,這可能跟驅動有關,但只是個猜測。
解決此問題的辦法應該是避免出現無效數據,應該使用嚴格的約束模式,即STRICT_TRANS_TABLES。參見http://www.zhixing123.cn/php/MYSQL%20STRICT_TRANS_TABLES.html。

參考:
http://it.china-b.com/sjk/mysql/20090828/179324_1.html
http://www.zhixing123.cn/php/MYSQL%20STRICT_TRANS_TABLES.html

在MySQL5.0.2之前,MySQL對非法或不當值並不嚴厲,而且爲了數據輸入還會強制將它們變爲合法值。在MySQL5.0.2和更高版本中,保留了以前的默認行爲,但你可以爲不良值選擇更傳統的處理方法,從而使得服務器能夠拒絕並放棄出現不良值的語句。本節介紹了MySQL的默認行爲(寬大行爲),新的嚴格的SQL模式,以及它們的區別。如果你未使用嚴格模式,下述情況是真實的。如果將“不正確”的值插入到列,如將NULL值插入非NULL列,或將過大的數值插入數值列,MySQL會將這些列設置爲“最可能的值”,而不是生成錯誤信息。如果試圖將超範圍的值保存到數值列,MySQL服務器將保存0(最小的可能值)取而代之,或最大的可能值。對於字符串,MySQL或保存空字符串,或將字符串儘可能多的部分保存到列中。如果打算將不是以數值開頭的字符串保存到數值列,MySQL將保存0。MySQL允許將特定的不正確日期值保存到DATE和DATETIME列(如“2000-02-31”或“2000-02-00”)。其觀點在於,驗證日期不是SQL服務器的任務。如果MySQL能保存日期值並準確檢索相同的值,MySQL就能按給定的值保存它。如果日期完全不正確(超出服務器能保存的範圍)將在列中保存特殊的日期值“0000-00-00”取而代之。如果試圖將NULL值保存到不接受NULL值的列,對於單行INSERT語句,將出現錯誤。對於多行INSERT語句或INSERTINTO...SELECT語句,MySQL服務器會保存針對列數據類型的隱含默認值。一般情況下,對於數值類型,它是0,對於字符串類型,它是空字符串(‘‘),對於日期和時間類型是“zero”。如果INSERT語句未爲列指定值,如果列定義包含明確的DEFAULT子句,MySQL將插入默認值。如果在定義中沒有這類DEFAULT子句,MySQL會插入列數據類型的隱含默認值。採用前述規則的原因在於,在語句開始執行前,無法檢查這些狀況。如果在更新了數行後遇到這類問題,我們不能僅靠回滾解決,這是因爲存儲引擎可能不支持回滾。中止語句並不是良好的選擇,在該情況下,更新完成了“一半”,這或許是最差的情況。對於本例,較好的方法是“僅可能做到最好”,然後就像什麼都未發生那樣繼續。在MySQL5.0.2和更高版本中,可以使用STRICT_TRANS_TABLES或STRICT_ALL_TABLESSQL模式,選擇更嚴格的處理方式。STRICT_TRANS_TABLES的工作方式:對於事務性存儲引擎,在語句中任何地方出現的不良數據值均會導致放棄語句並執行回滾。對於非事務性存儲引擎,如果錯誤出現在要插入或更新的第1行,將放棄語句。(在這種情況下,可以認爲語句未改變表,就像事務表一樣)。首行後出現的錯誤不會導致放棄語句。取而代之的是,將調整不良數據值,並給出告警,而不是錯誤。換句話講,使用STRICT_TRANS_TABLES後,錯誤值會導致MySQL執行回滾操作,如果可以,所有更新到此爲止。要想執行更嚴格的檢查,請啓用STRICT_ALL_TABLES。除了非事務性存儲引擎,它與STRICT_TRANS_TABLES等同,即使當不良數據出現在首行後的其他行,所產生的錯誤也會導致放棄語句。這意味着,如果錯誤出現在非事務性表多行插入或更新過程的中途,僅更新部分結果。前面的行將完成插入或更新,但錯誤出現點後面的行則不然。對於非事務性表,爲了避免這種情況的發生,可使用單行語句,或者在能接受轉換警告而不是錯誤的情況下使用STRICT_TRANS_TABLES。要想在第1場合防止問題的出現,不要使用MySQL來檢查列的內容。最安全的方式(通常也較快)是,讓應用程序負責,僅將有效值傳遞給數據庫。有了嚴格的模式選項後,可使用INSERTIGNORE或UPDATEIGNORE而不是不帶IGNORE的INSERT或UPDATE,將錯誤當作告警對待。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章