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,将错误当作告警对待。

你可能感兴趣的:(mysql,object,服务器,service,null,insert)