4.1 其他用户操作oracle数据库需要做的事
项目不会在oracle用户下开发,因为oracle用户可以进入dba,权限太大不安全。那么如果在xianwu用户下执行sqlplus命令会未找到命令,无法操作oracle。
解决:
1)需要设置xianwu环境变量.bash_profile。将oracle用户的以下环境变量复制到xianwu用户:
export ORACLE_BASE=/oracle/base
export ORACLE_HOME=/oracle/home
# 以下的ORACLE_SID,snoracl是字母,11是数字,g是字母。
export ORACLE_SID=snorcl11g
export NLS_LANG='Simplified Chinese_China.AL32UTF8'
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib:/usr/lib:.
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin:.
之后可以通过命令:
sqlplus scott/tiger@snorcl11g_136
登录oracle用户(需要在后面加上数据库服务名@snorcl11g_136)。
如果想要不加数据库服务名登录,需要修改一个oracle用户文件的权限。
2)修改/oracle/home/bin/oracle权限
chmod +s oracle
那么xianwu用户可以直接执行sqlplus scott/tiger登录本机oracle数据库。
4.2 C++封装的ooci类
Oracle提供了C接口(oci库),但是源码使用对于初学者及其不友好,因此从网上找到一大佬的库,将oci封装成两个类,使用方便(如需要封装的cpp库,请私信)。
一、基本操作crud
1.createtable.cpp
/*
* 程序名:createtable.cpp,此程序演示开发框架操作Oracle数据库(创建表)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
sqlstatement stmt; // 操作SQL语句的对象。
stmt.connect(&conn); // 指定stmt对象使用的数据库连接。
// 准备创建表的SQL语句。
// 如果SQL语句有错误,prepare()不会返回失败,所以,prepare()不需要判断返回值。
// 超女表girls,超女编号id,超女姓名name,体重weight,报名时间btime,超女说明memo,超女图片pic。
stmt.prepare("\
create table girls(id number(10),\
name varchar2(30),\
weight number(8,2),\
btime date,\
memo varchar2(300),\
pic blob,\
primary key (id))");
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
printf("create table girls ok.\n");
conn.disconnect(); // 在connection类的析构函数中会自动调用disconnect()方法。
return 0;
}
2.inserttable.cpp
/*
* 程序名:inserttable.cpp,此程序演示开发框架操作Oracle数据库(向表中插入数据)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
// sqlstatement stmt; // 操作SQL语句的对象。
// stmt.connect(&conn); // 指定stmt对象使用的数据库连接。
sqlstatement stmt(&conn);
// stmt.prepare("\
// insert into girls(id,name,weight,btime,memo) \
// values(1,'西施',48.5,to_date('2000-01-01 12:30:35','yyyy-mm-dd hh24:mi:ss'),\
// '中国排名第一的美女')");
// if (stmt.execute() != 0)
// {
// printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
// }
// printf("成功插入了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
// 准备插入表的SQL语句。
/*
// 静态SQL语句,适用于一次性执行的SQL。1)效率不如动态SQL语句高;2)特殊字符不方便处理;3)安全性(SQL注入)。
for (int ii=10;ii<15;ii++)
{
stmt.prepare("\
insert into girls(id,name,weight,btime,memo) \
values(%d,'西施%05dgirl',%.1f,to_date('2000-01-01 12:30:%02d','yyyy-mm-dd hh24:mi:ss'),\
'这是''第%05d个超级女生的备注。')",ii,ii,45.35+ii,ii,ii);
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
printf("成功插入了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
}
*/
// 1)如果字段是字符串型,绑定的变量可以用char[],也可以用string,推荐用char[]。
// 2)如果字段是字符串型,bindin()的第三个参数填字段的长度,太小可能会有问题,不推荐缺省值2000。
// 3)动态SQL语句的字段也可以填静态的值。
// 4)绑定的变量,一般用结构体。
struct st_girl
{
long id; // 超女编号,用long数据类型对应Oracle无小数的number(10)。
char name[31]; // 超女姓名,用char[31]对应Oracle的varchar2(30)。
double weight; // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
char btime[20]; // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
char memo[301]; // 备注,用char[301]对应Oracle的varchar2(300)。
} stgirl;
// 动态SQL语句,适用于多次执行的SQL。
stmt.prepare("insert into girls(id,name,weight,btime,memo) \
values(:1,:2,:3,to_date(:4,'yyyy-mm-dd hh24:mi:ss'),:5)"); // :1,:2,...,:n可以理解为输入参数。
stmt.bindin(1,stgirl.id);
stmt.bindin(2,stgirl.name,30);
stmt.bindin(3,stgirl.weight);
stmt.bindin(4,stgirl.btime,19);
stmt.bindin(5,stgirl.memo,300); // 字符串的长度可以不指定,缺省是2000,这种用法不严瑾,不建议。
// 对变量赋值,执行SQL语句。
for (int ii=15;ii<20;ii++)
{
// 初始化变量。
memset(&stgirl,0,sizeof(struct st_girl));
// 为变量赋值。
stgirl.id=ii; // 超女编号。
sprintf(stgirl.name,"西施%05dgirl",ii); // 超女姓名。
stgirl.weight=45.35+ii; // 超女体重。
sprintf(stgirl.btime,"2021-08-25 10:33:%02d",ii); // 报名时间。
sprintf(stgirl.memo,"这是'第%05d个超级女生的备注。",ii); // 备注。
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute()!=0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
printf("成功插入了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL语句影响的记录数。
}
conn.commit(); // 提交事务。
return 0;
}
3.updatetable.cpp
/*
* 程序名:updatetable.cpp,此程序演示开发框架操作Oracle数据库(修改表中的数据)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
sqlstatement stmt(&conn);
// 静态SQL语句。
/*
stmt.prepare("\
update girls set name='冰冰',weight=45.2,btime=to_date('2008-01-02 12:30:22','yyyy-mm-dd hh24:mi:ss') where id=10");
*/
struct st_girl
{
long id; // 超女编号,用long数据类型对应Oracle无小数的number(10)。
char name[31]; // 超女姓名,用char[31]对应Oracle的varchar2(30)。
double weight; // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
char btime[20]; // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
char memo[301]; // 备注,用char[301]对应Oracle的varchar2(300)。
} stgirl;
// 动态SQL语句。
stmt.prepare("\
update girls set name=:1,weight=:2,btime=to_date(:3,'yyyy-mm-dd hh24:mi:ss') where id=:4"); // :1,:2,...,:n可以理解为输入参数。
stmt.bindin(1,stgirl.name,30);
stmt.bindin(2,stgirl.weight);
stmt.bindin(3,stgirl.btime,19);
stmt.bindin(4,stgirl.id);
// 初始化结构体,为变量赋值。
memset(&stgirl,0,sizeof(struct st_girl));
stgirl.id=11; // 超女编号。
sprintf(stgirl.name,"幂幂"); // 超女姓名。
stgirl.weight=43.85; // 超女体重。
strcpy(stgirl.btime,"2021-08-25 10:33:35");
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
printf("成功修改了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
conn.commit(); // 提交事务。
return 0;
}
4.deletetable.cpp
/*
* 程序名:deletetable.cpp,此程序演示开发框架操作Oracle数据库(删除表中的数据)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
sqlstatement stmt(&conn);
// 静态SQL语句。
stmt.prepare("delete from girls where id=10");
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
printf("成功删除了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
int minid=11,maxid=13;
// 动态SQL语句。
stmt.prepare("delete from girls where id>=:1 and id<=:2"); // :1,:2,...,:n可以理解为输入参数。
stmt.bindin(1,minid);
stmt.bindin(2,maxid);
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
printf("成功删除了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
conn.commit(); // 提交事务。
return 0;
}
5.selecttable.cpp
/*
* 程序名:selecttable.cpp,此程序演示开发框架操作Oracle数据库(查询表中的数据)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
sqlstatement stmt(&conn);
int minid=14,maxid=16;
struct st_girl
{
long id; // 超女编号,用long数据类型对应Oracle无小数的number(10)。
char name[31]; // 超女姓名,用char[31]对应Oracle的varchar2(30)。
double weight; // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
char btime[20]; // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
char memo[301]; // 备注,用char[301]对应Oracle的varchar2(300)。
} stgirl;
// 准备查询表的SQL语句,prepare()方法不需要判断返回值。
stmt.prepare("select id,name,weight,to_char(btime,'yyyy-mm-dd hh24:mi:ss'),memo from girls where id>=:1 and id<=:2");
// 为SQL语句绑定输入变量的地址,bindin()方法不需要判断返回值。
stmt.bindin(1,minid);
stmt.bindin(2,maxid);
// 把查询语句的结果集与变量的地址绑定,bindout()方法不需要判断返回值。
stmt.bindout(1,stgirl.id);
stmt.bindout(2,stgirl.name,30);
stmt.bindout(3,stgirl.weight);
stmt.bindout(4,stgirl.btime,19);
stmt.bindout(5,stgirl.memo,300);
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
// 本程序执行的是查询语句,执行stmt.execute()后,将会在数据库的缓冲区中产生一个结果集。
while (true)
{
memset(&stgirl,0,sizeof(stgirl)); // 先把结构体变量初始化。
// 从结果集中获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
// 在实际开发中,除了0和1403,其它的情况极少出现。
if (stmt.next() !=0) break;
// 把获取到的记录的值打印出来。
printf("id=%ld,name=%s,weight=%.02f,btime=%s,memo=%s\n",stgirl.id,stgirl.name,stgirl.weight,stgirl.btime,stgirl.memo);
}
// 请注意,stmt.m_cda.rpc变量非常重要,它保存了SQL被执行后影响的记录数。
printf("本次查询了girls表%ld条记录。\n",stmt.rpc());
return 0;
}
6.通用步骤
connection conn; // 创建数据库连接类的对象。
conn.connecttodb(); // 登录数据库,返回值:0-成功,其它-失败。
sqlstatement stmt(&conn);
stmt.prepare(); // 准备SQL语句
stmt.execute() ; // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
二、存取大型对象
LOB类型(Large object)
-
CLOB 字符型大型对象(CharacterLarge Object),专用于存储文本文件(受字符集影响、可检索)。varchar2
-
BLOB 二进制大型对象(BinaryLarge Object),可存储任意格式的文件(文本、图、音视频)((不受字符集影响、不可检索)。
-
如果把大量的文件存放数据库中,浪费存储空间,降低磁盘I/0的效率。数据库采用高性能存储,非普通磁盘。高价聘请美女干苦力。
关于BLOB、CLOB字段的操作稍微麻烦点,一般不用。
等到工作中需要用到的时候再回来参考代码即可。
1.filetoclob.cpp
/*
* 程序名:filetoclob.cpp,此程序演示开发框架操作Oracle数据库(把文本文件存入数据库表的CLOB字段中)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
// 修改girls表结构,增加memo1字段,用于测试。 alter table girls add memo1 clob;
sqlstatement stmt(&conn);
stmt.prepare("insert into girls(id,name,memo1) values(1,'冰冰',empty_clob())"); // 注意:不可用null代替empty_clob()。
if (stmt.execute()!=0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
// 使用游标从girls表中提取记录的memo1字段
stmt.prepare("select memo1 from girls where id=1 for update");
stmt.bindclob();
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
if (stmt.next() != 0) return 0;
// 把磁盘文件memo_in.txt的内容写入CLOB字段,一定要判断返回值,0-成功,其它-失败。
if (stmt.filetolob("/project/public/db/oracle/memo_in.txt") != 0)
{
printf("stmt.filetolob() failed.\n%s\n",stmt.message()); return -1;
}
printf("文本文件已存入数据库的CLOB字段中。\n");
conn.commit();
return 0;
}
2.clobtofile.cpp
/*
* 程序名:clobtofile.cpp,此程序演示开发框架操作Oracle数据库(把数据库的CLOB字段提取到文件)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
sqlstatement stmt(&conn);
stmt.prepare("select memo1 from girls where id=1");
stmt.bindclob();
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
if (stmt.next() != 0) return 0;
// 把CLOB字段中的内容写入磁盘文件,一定要判断返回值,0-成功,其它-失败。
if (stmt.lobtofile("/project/public/db/oracle/memo_out.txt") != 0)
{
printf("stmt.lobtofile() failed.\n%s\n",stmt.message()); return -1;
}
printf("已把数据库的CLOB字段提取到文件。\n");
return 0;
}
3.filetoblob.cpp
/*
* 程序名:filetoblob.cpp,此程序演示开发框架操作Oracle数据库(把二进制文件存入数据库的BLOB字段中)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
sqlstatement stmt(&conn);
stmt.prepare("insert into girls(id,name,pic) values(1,'冰冰',empty_blob())"); // 注意:不可用null代替empty_blob()。
if (stmt.execute()!=0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
// 使用游标从girls表中提取记录的pic字段
stmt.prepare("select pic from girls where id=1 for update");
stmt.bindblob();
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
if (stmt.next() != 0) return 0;
// 把磁盘文件pic_in.jpeg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。
if (stmt.filetolob("/project/public/db/oracle/pic_in.jpeg") != 0)
{
printf("stmt.filetolob() failed.\n%s\n",stmt.message()); return -1;
}
printf("二进制文件已存入数据库的BLOB字段中。\n");
conn.commit();
return 0;
}
4.blobtofile.cpp
/*
* 程序名:blobtofile.cpp,此程序演示开发框架操作Oracle数据库(把数据库的BLOB字段提取到文件)。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
sqlstatement stmt(&conn);
stmt.prepare("select pic from girls where id=1");
stmt.bindblob();
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
if (stmt.next() != 0) return 0;
// 把BLOB字段中的内容写入磁盘文件,一定要判断返回值,0-成功,其它-失败。
if (stmt.lobtofile("/project/public/db/oracle/pic_out.jpeg") != 0)
{
printf("stmt.lobtofile() failed.\n%s\n",stmt.message()); return -1;
}
printf("已把数据库的BLOB字段提取到文件。\n");
return 0;
}
细节
sqlstatement类的isopen()函数:sql语句已准备好返回true!
三、其他的细节
1.静态SQL语句的安全性问题(SQL注入)。
静态执行:select * from operator where 1=1 or ''='';也能查询到,永远成立。
动态sql语句就不会出现这个问题。
2.char和varchar2的问题。
①、固定长度的字符串
固定长度字符串用char表示,当存入数据内容的长度不够时,Oracle将在数据内容后面自动填充空格以达到其固定的长度,例如char(10)总是包含10字节信息。
char字段最多可以存储2000字节的内容。
②、可变长度的字符串
变长度字符串用varchar2表示,与char类型不同,Oracle不会在数据内容后面填充任何内容。
varchar2字段最多可以存储4000字节的内容,从Oracle 12c版本开始,可以存储32767字节的内容。
③、char和varchar2的比较
char(10),如果存入'freecplus',在数据库中将存储'freecplus ',在最后补了一个空格。
varchar2(10),如果存入'freecplus',在数据库中将存储'freecplus',什么也不会补。
在实际开发中,我们并不希望Oracle给字符串后面补空格,那是不是可以弃用char类型呢?不是,我们一般用char类型存放固定大小的数据内容,例如身份证号码,固定是18位的,用char(18)就非常合适,能用varchar2(18)可以存放身份证号码吗?当然可以,但是,char(18)的效率比varchar2(18)的效率要高很多。
总结一下,如果确定、肯定、一定、保证存入字符串的长度是固定不变的,例如性别、身份证号码、手机号码,用char类型,否则用varchar2类型,例如姓名、学历、地址、兴趣爱好等,char虽然死板,但是效率高。但是char有坑~
🔴小小的坑,害了很多人!
create table tt( c1 char(5),c2 varchar2(5) );
insert into tt values('abs','abc');
select * from tt;
在c1字段中,abc后面又两个空格!而c2字段后面没有空格,也就是:
c1='abc '; c2='abc';
那么执行:
select * from tt where c2='abc';
select * from tt where c2='abc ';
select * from tt where c1='abc'; --这个也能查到!!!
select * from tt where c1='abc ';
1 2 4句都好理解,3也能查询成功!说明char类型比较的时候不比较后面的空格!
而在C++中运行:
a.静态语句与sql直接执行效果一样。
b.动态语句(bindin)执行3语句查询不到。
3.如果处理数字字段的空值。
0不是空,空不是0!
如果想在表中插入空值,那么可以利用oracle的强制类型转换,将数据字段插入空字符串:
insert into tt values('',''); --在c++中操作。
4.PL/SQL的过程语句demo4.cpp
只需在多条sql语句前后加上begin和end。
/*
* 程序名:demo4.cpp,此程序演示执行PL/SQL过程语句。
* 作者:张咸武。
*/
#include "_ooci.h" // 开发框架操作Oracle的头文件。
using namespace idc;
int main(int argc,char *argv[])
{
connection conn; // 创建数据库连接类的对象。
// 登录数据库,返回值:0-成功,其它-失败。
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
{
printf("connect database failed.\n%s\n",conn.message()); return -1;
}
printf("connect database ok.\n");
struct st_girl
{
long id; // 超女编号,用long数据类型对应Oracle无小数的number(10)。
char name[31]; // 超女姓名,用char[31]对应Oracle的varchar2(30)。
double weight; // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
char btime[20]; // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
char memo[301]; // 备注,用char[301]对应Oracle的varchar2(300)。
} stgirl;
// 准备PL/SQL语句,如果SQL语句有错误,prepare()不会返回失败,所以,prepare()不需要判断返回值。
sqlstatement stmt(&conn); // 操作SQL语句的对象。
// PL/SQL语句的优点:减少了客户端与数据库的通讯次数,提高了效率。
stmt.prepare("\
begin\
delete from girls where id=:1;\
insert into girls(id,name) values(:2,:3);\
update girls set weight=:4 where id=:5;\
end;");
stmt.bindin(1,stgirl.id);
stmt.bindin(2,stgirl.id);
stmt.bindin(3,stgirl.name,30);
stmt.bindin(4,stgirl.weight);
stmt.bindin(5,stgirl.id);
memset(&stgirl,0,sizeof(struct st_girl));
stgirl.id=1;
strcpy(stgirl.name,"冰冰");
stgirl.weight=49.5;
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
// 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
}
printf("exec pl/sql ok.\n");
printf("影响了%ld条记录。\n",stmt.rpc()); // pl/sql中最后一条SQL语句记录影响记录的行数。
conn.commit();
return 0;
}
4.3 oracle与C++的一些关系(封装的oci为ooci.cpp)
long id; // 编号,用long数据类型对应Oracle无小数的number(10)。
char name[31]; // 姓名,用char[31]对应Oracle的varchar2(30)。
double weight; // 体重,用double数据类型对应Oracle有小数的number(8,2)。
char btime[20]; // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ss'。
char memo[301]; // 备注,用char[301]对应Oracle的varchar2(300)。
1.静态sql语句效率不如动态sql语句(采用bind绑定)效率高。原因是静态sql语句每次都需要准备sql语句(stmt.prepare(...)),底层是tcp通信,因此效率不高。
2.单引号中想用单引号需要用两个单引号。
stmt.prepare("\
insert into girls(id,name,weight,btime,memo) \
values(%d,'西施%05dgirl',%.1f,to_date('2000-01-01 12:30:%02d','yyyy-mm-dd hh24:mi:ss'),'这是''第%05d个女生的备注。')",ii,ii,45.35+ii,ii,ii);
3.sqlstatement类的bindin方法
① 字符类型推荐用char[],不推荐string,但可以用。
char[]:如果字段是字符串型,bindin()的第三个参数填字段的长度,填大一些没关系。太小可能会有问题,不推荐缺省值2000。
string:
1)如果SQL语句没有改变,只需要bindin一次就可以了;
2)如果value的类型是string,bindin()函数中会resize(len);
3)如果value的类型是string,那么,在用户的程序代码中,不可改变它内部buffer的地址。
② 绑定的变量用结构体看起来更舒服。