一、数据库的介绍
数据库(Database,简称DB)是一种应用程序,主要用于对数据进行存储、管理和统计。它通常与服务器搭配使用,为各类信息系统提供数据支持。在数据库中,数据以表的形式组织,一张表由多个记录(行)组成,每条记录又包含多个字段(列)。
根据规模和性能,数据库可分为大型、中型和小型。常见的关系型数据库包括 Oracle、MySQL/MSSQL、SQLite、DB2、PostgreSQL 等。相关的名词概念有:DB(数据库)、DBMS(数据库管理系统)、MIS(管理信息系统)、OA(办公自动化)等。
对数据库的操作主要通过 SQL(结构化查询语言)完成。SQL 语句可细分为三类:
-
DDL(data defination language 建表):用于建表等结构定义;
-
DML(data modifiy):用于新增、修改、删除行(如 update);
-
DQL(data query language):用于查询(如 select)。
本篇文章用到的是SQLite3。它是一个开源、采用 C 语言开发的数据库,代码量约 1 万行,总大小在 10M 以内,无需安装(绿色软件),以文件形式存在且可移动,最大支持 2T 数据。更多信息可访问官网 www.sqlite.org,代码托管于 www.kernel.org。
特点:
1、开源 C语言开发
2、代码量少 1万行左右,总大小10M以内
3、绿色软件无需安装
4、文件型数据库,可以移动。
5、数据容量最大 2T
二、数据库的相关指令
1.安装和启动:
Linux终端下输入:
安装:
sudo apt-get install sqlite3
sudo apt-get install libsqlite3-dev
启动:
sqlite3 xxx.db
2.数据库的指令:
Sqlite3下输入:(注意数据库是不区分大小写的)
.database 列出当前库和系统中那个文件在关联
.tables 列出当期数据库中的所有表
.schema xxx 列出当前指定的xxx表结构.headers on 查询数据的时候显示列名
.exit 或者.quit或者**.q 退出数据库控制台**
创建表:
create table 表名 (表字段 类型,表字段 类型,。。。。);
create table user(id int ,name char,age int);
CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY,word char,mean char);//若有表就创建,没有就跳过
删除一个表:
drop table 表名;
drop table user;
插入数据:
insert into 表名 (字段名称 ) values (值名称);
insert into user (id,age) values (1,10);
insert into user values(3,"wang",11);
insert into user (age) values ( 12);
查询表中的数据:
数据库的导入导出
select 列名 from 表名 条件;
select * from user ; //所有 的列select id from user;
select id,name from user where not age <30
(可以使用&& or and增加条件)
通配符:
% 任意多个任意字符
_ 1个任意字符select * from user where name like '张_ ' and id <5;
select * from user where name like '张%' and id <5;
修改表中数据:
update 表名 set 表字段 = 值 满足条件:
eg: update user set id = 1 where name = 'li';
update user set id = 1 where name = "li" and passwd = "123";
update user set id = 2 where name = "li" or name = "zhao";
删除表中数据:
delete from 表名 满足条件:
eg:delete from user ; ///删除表中所有数据
delete from user where id = 1; ///删除id=1 的数据;
delete from user where id =1 and name = "zhang";
delete from user where id = 1 or id = 2;
插入时间列 int int:
CREATE TABLE user1(id int,name char,age int,dt datetime);
insert into user1 values (2,'张三',23,datetime('now','+8 hours'));
自动增长列:
CREATE TABLE user3(id INTEGER PRIMARY KEY ,name char,age int,dt datetime);
insert into user3 values (NULL,'李四',23,datetime('now'));
PRIMARY KEY 主键 ,唯一性, 如果使用主键作为搜索条件,比普通字段块。
三、数据库的导入导出
linux终端中输入:
1、数据的导出 :
sqlite3 xxx.db .dump > xxx.sql将数据库名称为xxx的数据库整体导出到脚本中。
2、数据的导入 :
sqlite3 xxx.db < xxx.sql
四、数据库的编程步骤:
打开数据库 ==》读写数据库(增,删,改,查) ==》关闭数据库
相关函数:
打开数据库:
int sqlite3_open(char * path,sqlite3 ** db);
功能:打开指定path路径+文件名称的数据库,并将
打开的地址指向db变量的句柄。
参数:path 要打开的数据库路径+名称
db 要打开的数据库地址指针
返回值:成功 0
失败 -1;
关闭数据库:
int sqlite3_close(sqlite3 *db);
功能:关闭指定的数据库
参数:要关闭的数据库地址
返回值:成功 0
失败 -1;
执行sql 语句
int sqlite3_exec(sqlite3 *db,char *sql,callback fun,void * arg,char **errmsg);
功能:在db数据库上执行sql语句。
并将结果返回。
参数:db 要执行sql的数据库
sql 要执行的非查询sql语句。
fun 如果sql语句是查询操作,需要 这个函数,则该回调函数用来回收查询的结果。
arg 回调函数的参数,如果没有回调函数则该参数为NULL;
errmsg 执行过程中的错误信息。//通常定义一个char *errmsg = NULL;传入函数返回值:执行成功 0(SQLITE_OK)
失败 非0 ;
返回值的宏:
|-----------------|---|----------|----------------|
|SQLITE_ERROR| 1 | 通用错误 | SQL 语法错误、表不存在等 |
|SQLITE_BUSY| 5 | 数据库文件被锁定 | 其他进程正在写入数据库 |
|SQLITE_LOCKED| 6 | 表被锁定 | 同一连接内的并发访问冲突 |
编程数据库的查询:
当需要对数据库中的数据,进行查询的时候。
sqlite3_exec() 中的回调函数(第三个参数)就不能为空了。
通过回调函数反馈查询到结果。 回调函数是用来返回结果。
如果结果集有5条记录,那么回调函数会调用5次。如果结果集有0条记录,那么回调函数会调用0次。(会根据结果集的记录来调用函数)show函数中,result 参数每次都会执行对应的记录。
如果有多条记录,数据库会分多次把数据传递给result 指针。
注意:show函数中必须返回0,如果不返回0,sqlite3_exec()会返回错误信息
示例1:给已有的数据库插入数据
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
int main(int argc, char **argv)
{
sqlite3 *db = NULL;
// 1 打开数据库,获得数据的句柄(相当于linux 中的文件描述符)
int ret = sqlite3_open("aaa.db", &db);
if (SQLITE_OK != ret)
{
fprintf(stderr, "open db error %s\n", sqlite3_errstr(ret));
sqlite3_close(db);
return 1;
}
char *errmsg = NULL;
//需要执行的sql语句, 不要执行查询语句(select),
char sql_cmd[512] = "insert into user values(7,'帅哥',20)";
// 2 执行sql语句(对数据库的读写)
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
// 3. 关闭数据 释放资源
sqlite3_close(db);
return 0;
}
示例2:创建新的数据库插入数据
#include <errno.h>
#include <fcntl.h>
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
// 1.创建db文件,没有就直接到第二步
int dbfd = open("dict.db", O_RDONLY | O_TRUNC);
if (-1 == dbfd) //出错
{
if (errno == ENOENT) //错误原因:文件不存在
{
// fork,创建db文件
pid_t pid;
pid = fork();
if (pid > 0) //父进程
{
}
else if (0 == pid) //子进程创建db并退出
{
if (execlp("sqlite3", "sqlite3", "dict.db", NULL) < 0)
{
perror("execl fail");
}
exit(1); //创建好后结束进程
}
else // fork失败
{
perror("fork");
return -1;
}
}
}
else // db存在
{
close(dbfd); //文件存在,关闭描述符
}
// 第二步 打开数据库,获得数据的句柄(相当于linux 中的文件描述符)
sqlite3 *db = NULL;
int ret = sqlite3_open("dict.db", &db);
if (SQLITE_OK != ret)
{
fprintf(stderr, "open db error %s\n", sqlite3_errstr(ret));
sqlite3_close(db);
return 1;
}
//第三步创建table
char sql_cmd[512] = {0};
char *errmsg = NULL;
strcpy(sql_cmd, "CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY,word char,mean char)");
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
//第四步把dict.txt插入数据库
FILE *fp = fopen("dict.txt", "r"); //打开文件
if (fp == NULL)
{
perror("fopen fail");
return -1;
}
while (1) //循环 //获取dict.txt内容
{
char buf[1024] = {0};
char *tmp[2] = {0};
if (fgets(buf, sizeof(buf), fp) == NULL) //读取完dict.txt
{
printf("插入完成\n");
break;
}
tmp[0] = strtok(buf, " ");
tmp[1] = strtok(NULL, "\n");
sprintf(sql_cmd, "insert into user values(NULL,\"%s\",\"%s\")", tmp[0], tmp[1]);
// 2 执行sql语句(对数据库的读写)
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
}
// 3. 关闭数据 释放资源
fclose(fp);
sqlite3_close(db);
return 0;
}
五、SQlite事务(Transaction)
事务(Transaction)是以逻辑顺序完成的工作单位或序列,可以是由用户手动操作完成,也可以是由某种数据库程序自动完成
原子性(Atomicity) :确保工作单位内的所有操作都成功完成,否则,事务会在出现故障时终止,之前的操作也会回滚到以前的状态。
一致性(Consistency) :确保数据库在成功提交的事务上正确地改变状态。
隔离性(Isolation) :使事务操作相互独立和透明。
持久性(Durability):确保已提交事务的结果或效果在系统发生故障的情况下仍然存在
命令:
BEGIN TRANSACTION:开始事务处理。
COMMIT:保存更改,或者可以使用:END TRANSACTION 命令。
ROLLBACK:回滚所做的更改。
在上面的示例2中,有批量操作。 如果没有开启事务。 每条插入语句,都会先开启事务,插入结束后在关闭事务。会导致效率下降,插入数据速度非常慢。
解决方案 :在批量插入前开启事务,插入结束后提交事务。 提高插入的效率。
示例2:(更改后:加入了查询功能和解决了插入过慢的问题)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
int find_word(void*arg,int col,char**result,char**title)
{
*(int*)arg = 1;
printf("%s:%s\n",result[1],result[2]);
return 0;
}
int main(int argc, char **argv)
{
sqlite3 *db = NULL;
// 1 打开数据库,获得数据的句柄(相当于linux 中的文件描述符)
int ret = sqlite3_open("dict.db", &db);
if (SQLITE_OK != ret)
{
fprintf(stderr, "open db error %s\n", sqlite3_errstr(ret));
sqlite3_close(db);
return 1;
}
char sql_cmd[1024] = {0};
char *errmsg = NULL;
// 创建表
bzero(sql_cmd, sizeof(sql_cmd));
strcpy(sql_cmd,
"create table IF NOT EXISTS user(id INTEGER PRIMARY KEY ASC,word "
"char,mean text);");
// 2 执行sql语句(对数据库的读写)
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
bzero(sql_cmd, sizeof(sql_cmd)); // 清空表
strcpy(sql_cmd, "delete from user");
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
bzero(sql_cmd, sizeof(sql_cmd)); // 为了提高效率,批量操作 ,开启事务
strcpy(sql_cmd, "BEGIN TRANSACTION;");
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
FILE *fp = fopen("dict.txt", "r");
if (NULL == fp)
{
perror("fopen");
return 1;
}
int i = 1;
while (1)
{
char line_buf[1024] = {0};
if (NULL == fgets(line_buf, sizeof(line_buf), fp))
{
break;
}
char *word = strtok(line_buf, " ");
char *mean = strtok(NULL, "\r");
bzero(sql_cmd, sizeof(sql_cmd));
sprintf(sql_cmd, "insert into user values(NULL,\"%s\",\"%s\");", word,
mean);
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd,
errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
// printf("num:%d\n", i++);
}
bzero(sql_cmd, sizeof(sql_cmd)); // 提交事务 ,相当于保存文件
strcpy(sql_cmd, "COMMIT;");
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
while (1)
{
printf("input word:");
char word[50] = {0};
fgets(word, sizeof(word), stdin);
word[strlen(word) - 1] = '\0';
if(0 == strcmp("#quit",word))
{
break;
}
int flag = 0 ;
bzero(sql_cmd, sizeof(sql_cmd)); // 清空表
sprintf(sql_cmd,"select * from user where word like \"%s\";",word);
ret = sqlite3_exec(db, sql_cmd, find_word,&flag , &errmsg);
if (SQLITE_OK != ret)
{
fprintf(stderr, "sqlite3_exec sql_cmd:[%s] ,%s\n", sql_cmd,
errmsg);
sqlite3_free(errmsg);
sqlite3_close(db);
return 1;
}
if(0 == flag)
{
printf("can't find\n");
}
}
// 3. 关闭数据 释放资源
sqlite3_close(db);
return 0;
}
六、总结
假如要写入sql_cmd命令的字符串里面有单引号了,这时候写成:
sprintf(sql_cmd, "insert into user values(NULL,'%s','%s')", tmp[0], tmp[1]);
tmp[0]或tmp[1]里面有单引号的字符串
这样执行命令肯定会报错,所以可以写成这样:
sprintf(sql_cmd, "insert into user values(NULL,\"%s\",\"%s\")", tmp[0], tmp[1]);