嵌入式數據庫--sqlite

關於數據庫

1、客戶端、服務器和數據庫

  • 服務器:保存、處理、分發數據。
  • 客戶端:收集、展示數據。
  • 數據庫:規範地管理和存儲數據。
  • 三者之間的關係:

在這裏插入圖片描述

2、目前國內比較流行的數據庫管理系統

  • Mysql
    Mysql是開源的、快速的、可靠的、易於使用的關係型數據庫管理系統,Mysql可以工作在嵌入式系統中,功能比較強大,內存佔用比較大。

  • sql server
    微軟開發的用於web服務器上最流行的數據庫管理系統,以其簡便的操作和友好的界面深受廣大用戶的喜愛。

  • oracle
    甲骨文公司研發的開放的數據庫管理系統,操作較爲複雜,功能比較強大,在世界上應用最廣泛的數據庫。

  • 總結
    在當今大數據形勢的推動下,數據的管理極爲重要,事物的誕生往往是因爲需求,數據庫就是用來管理和保護數據的。數據庫技術的發展越來越完善,各個數據庫之間沒有誰強誰弱之說,只能說各個數據庫的使用場合不一樣,比如在嵌入式領域,數據庫往往是用來暫時存儲小量數據的,基本輕量級的數據庫就可以滿足需求,大型複雜的數據庫管理系統反而會影響嵌入式系統的運行效率。

sqlite介紹

  • sqlite的背景
    sqlite是全開源的,可以免費用作任何目的。SQLite代碼庫由一支全職從事SQLite工作的國際開發人員團隊支持 。開發人員繼續擴展SQLite的功能並增強其可靠性和性能,同時保持與已發佈的接口規範, SQL語法和數據庫文件格式的向後兼容性 。源代碼對於任何想要它的人都是絕對免費的,但是也可以提供專業支持。

  • 爲什麼適用於嵌入式
    sqlite是c語言編寫的嵌入式數據庫管理系統,代碼量少於3萬行,庫小於600KB,稱其爲輕量級數據庫,它僅僅是一個“庫”,並不是數據庫服務器,因此不需要單獨的服務器進程,sqlite直接讀取和寫入磁盤文件,根據系統提供的內存大小,運行速度不一樣,一般情況下,內存越大,運行速度越快,可能比直接文件系統IO–fwrite、fread快(參考鏈接)。

  • 特點
    (1)無服務器:大多少數據庫基於客戶端/服務器的,通過網絡訪問數據庫服務器。sqlite直接存取磁盤,沒有中間服務器層,主要優點是無需安裝,設置,配置,初始化,管理和排除故障的單獨服務器進程,正所謂"零配置"數據庫引擎。

    (2)單個數據庫文件:sqlite數據庫是單個普通磁盤文件,可以位於目錄層次結構中的任何位置。其他數據庫引擎傾向於將數據存儲爲大量文件,並只能存放在數據庫本身標準的位置,這樣做是爲了保證數據的安全性,同時也給訪問帶來了困難。

    (3)穩定的可跨平臺數據庫文件:可以在不同平臺間複製並使用相同的數據庫文件。

    (4)清單輸入:sqlite允許用戶將任意類型的值存放在任意列中,這種數據類型叫清單類型,sqlite所特有。

    (5)可變長度記錄:sqlite僅使用一行記錄所佔用的磁盤空間,比如將單個字符存放在VARCHAR(100)列中,磁盤空間僅僅被消耗一個字符的空間,而不是100個字符的空間。這是sqlite數據庫比較小的原因之一,數據庫越小,運行越快。

sqlite的shell命令

  • ubuntu上安裝sqlite3:只需要一條命令即可:sudo apt-get install sqlite3; 中間可能會出現缺少依賴包這種情況,我們只需下載相應的依賴包就好了。

  • 啓動sqlite3控制檯:sqlite3
    在這裏插入圖片描述

  • 創建一個test的表:create table test (varchar,int);
    在這裏插入圖片描述

  • 查看數據庫中所有的表:.table;
    在這裏插入圖片描述

  • 向表中插入兩條數據:insert into test values();
    在這裏插入圖片描述

  • 查看錶中所有數據:select * from test;
    在這裏插入圖片描述

  • 刪除表:drop table test;
    在這裏插入圖片描述

  • 更多的SQL語句:點擊學習

sqlite C API接口

Linux下使用編程操作sqlite數據庫時,會在c文件中包含<sqlite3.h>頭文件,如果編譯出現下圖所示錯誤,說明沒有相應的函數庫。在這裏插入圖片描述
解決辦法:

sudo apt-get install libsqlite3-dev

1、連接數據庫

連接sqlite3數據庫,如果數據庫不存在,則創建並打開數據庫,如果此數據庫存在,則打開數據庫。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sqlite3.h>

#define database_name "test.db"//數據庫的名字

int main(int argc, char **argv)
{

	sqlite3				*db;//定義數據庫操作句柄

	int					rv = -1;//函數返回值

	rv = sqlite3_open(database_name,&db);//打開數據庫
	printf("rv=%d\n",rv);//打印返回值
	if(rv < 0)
	{
		printf("Can't create or open database!:%s\n",sqlite3_errmsg(db));
		exit(0);
	}
	else
	{
		printf("Creating or opening database successfully!\n");
	}

	rv = sqlite3_close(db);//關閉數據庫
	printf("rv=%d\n",rv);//打印返回值
	if(rv < 0)
	{
		 printf("Closing database failly!:%s\n",sqlite3_errmsg(db));
		 exit(0);
	}
	else
	{
		printf("select table successfully!\n");
	}
}

代碼解析:

  • sqlite3_open
int sqlite3_open(
 			 	const char *filename,   /*數據庫的名字*/
				sqlite3 **ppDb          /*輸出數據庫的操作句柄*/
 				);

功能:按文件名稱打開一個數據庫,如果文件不存在,則自動創建。
返回:創建或打開成功返回SQLITE_OK(0),錯誤返回錯誤碼。
返回值列表:

#define SQLITE_OK 0 /* Successful result /
/
beginning-of-error-codes /
#define SQLITE_ERROR 1 /
SQL error or missing database /
#define SQLITE_INTERNAL 2 /
Internal logic error in SQLite /
#define SQLITE_PERM 3 /
Access permission denied /
#define SQLITE_ABORT 4 /
Callback routine requested an abort /
#define SQLITE_BUSY 5 /
The database file is locked /
#define SQLITE_LOCKED 6 /
A table in the database is locked /
#define SQLITE_NOMEM 7 /
A malloc() failed /
#define SQLITE_READONLY 8 /
Attempt to write a readonly database /
#define SQLITE_INTERRUPT 9 /
Operation terminated by sqlite3_interrupt()/
#define SQLITE_IOERR 10 /
Some kind of disk I/O error occurred /
#define SQLITE_CORRUPT 11 /
The database disk image is malformed /
#define SQLITE_NOTFOUND 12 /
Unknown opcode in sqlite3_file_control() /
#define SQLITE_FULL 13 /
Insertion failed because database is full /
#define SQLITE_CANTOPEN 14 /
Unable to open the database file /
#define SQLITE_PROTOCOL 15 /
Database lock protocol error /
#define SQLITE_EMPTY 16 /
Database is empty /
#define SQLITE_SCHEMA 17 /
The database schema changed /
#define SQLITE_TOOBIG 18 /
String or BLOB exceeds size limit /
#define SQLITE_CONSTRAINT 19 /
Abort due to constraint violation /
#define SQLITE_MISMATCH 20 /
Data type mismatch /
#define SQLITE_MISUSE 21 /
Library used incorrectly /
#define SQLITE_NOLFS 22 /
Uses OS features not supported on host /
#define SQLITE_AUTH 23 /
Authorization denied /
#define SQLITE_FORMAT 24 /
Auxiliary database format error /
#define SQLITE_RANGE 25 /
2nd parameter to sqlite3_bind out of range /
#define SQLITE_NOTADB 26 /
File opened that is not a database file /
#define SQLITE_ROW 100 /
sqlite3_step() has another row ready /
#define SQLITE_DONE 101 /
sqlite3_step() has finished executing /
/
end-of-error-codes */

  • sqlite3_errmsg
int sqlite3_errmsg(sqlite3 *);

功能:打印數據庫錯誤信息。
參數:數據庫操作句柄
返回:創建或打開成功返回SQLITE_OK(0),錯誤返回錯誤碼。

  • sqlite3_close
int sqlite3_close(sqlite3 *;

功能:關閉數據庫。
參數:數據庫操作句柄
返回:創建或打開成功返回SQLITE_OK(0),錯誤返回錯誤碼。

  • 編譯運行
    1、出現以下錯誤,原因是sqlite3不是標準庫,gcc編譯連接不上sqlite3函數庫
    在這裏插入圖片描述
    解決辦法:加上 -lsqlite3
    在這裏插入圖片描述
    2、運行,成功打開數據庫
    在這裏插入圖片描述
    3、查看剛剛創建的數據庫文件
    在這裏插入圖片描述

2、創建表

在前面創建test.db數據庫中創建一個表。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sqlite3.h>

/*
 功能:回調函數
 參數:
	para:由exec傳入的參數
	col_num:查詢出一條記錄有多少個表頭
	col_value:查詢出的記錄緩存區
	col_name:表示字段的段命
  返回:
	成功返回0
	失敗返回-1
 */
static int callback(void *para, int col_num, char **col_value, char **col_name)
{
	int i;
	for(i = 0; i < col_num; i++)
	{
		printf("%s = %s\n",col_name[i],col_value[i]?col_value[i]:"NULL");
	}
	printf("\n");

	return 0;
}


int main(int argc, char **argv)
{
	sqlite3					*db;//定義數據庫操作句柄
	int						rv;//返回值
	char					*sql;//SQL語句指針
	char					*z_err_msg;//獲取錯誤信息

	rv = sqlite3_open("test.db",&db);//打開數據庫
	if(rv)
	{
		printf("Can't open database!:%s\n",sqlite3_errmsg(db));
		exit(0);
	}
	printf("Opening database successfully!\n");

	//製作SQL語句
	sql = "create table roommates("\
		   "stu_id varchar primary key not NULL,"\
		   "name		   text not NULL,"\
		   "age			   int  not NULL,"\
		   "address        varchar,"\
		   "telephone      varchar);";

	rv = sqlite3_exec(db,sql,callback,0,&z_err_msg);//執行sql語句
	if(rv)
	{
		printf("SQL error!:%s\n",z_err_msg);//打印錯誤信息
		sqlite3_free(z_err_msg);
	}
	else
	{
		printf("select table successfully!\n");
	}

	sqlite3_close(db);//關閉數據庫

	return 0;
}

代碼解析:

  • sqlite3_exec
int  sqlite3_exec{
				sqlite3*            : open 打開的數據庫操作句柄
				const char* sql,    : 執行的sql功能語句
				*callback,          : sql語句對應的回調函數,在下一個實例代碼中細講
				void* data,         : 傳遞給回調函數的參數
				char **errmsq       : 函數內部通過malloc返回錯誤信息
				}

功能:執行sql語句
返回:成功返回SQLITE_OK,錯誤返回錯誤代碼

  • sqlite3_free
int sqlite3_free(char *errmsq);

功能:當執行exec函數時,如果發生錯誤,內部會malloc分配內存存儲錯誤信息,所以需要釋放這塊內存,以免內存泄漏。
返回:成功返回SQLITE_OK,錯誤返回錯誤代碼。

  • 編譯運行
    在這裏插入圖片描述
  • 查看剛剛建的表字段
    在這裏插入圖片描述

3、插入數據

向上面創建的roommates表中插入數據。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sqlite3.h>

/*
 功能:查詢記錄回調函數
 參數:
	para:由exec傳入的參數
	col_num:查詢出一條記錄有多少個表頭
	col_value:查詢出的記錄緩存區
	col_name:表示字段的段命
  返回:
	成功返回0
	失敗返回-1
 */
static int callback(void *para, int col_num, char **col_value, char **col_name)
{
	int i;
	for(i = 0; i < col_num; i++)
	{
		printf("%s = %s\n",col_name[i],col_value[i]?col_value[i]:"NULL");
	}
	printf("\n");

	return 0;
}

int main(int argc, char **argv)
{

	sqlite3					*db;//定義數據庫操作句柄
	int						rv;//返回值
	char					*sql;//SQL語句指針
	char					*z_err_msg;//獲取錯誤信息

	rv = sqlite3_open("test.db",&db);//打開數據庫
	if(rv)
	{
		printf("Can't open database!:%s\n",sqlite3_errmsg(db));
		exit(0);
	}
	printf("Opening database successfully!\n");

	//製作SQL語句
	sql =  "insert into roommates(stu_id,name,age,address,telephone)"\
		   "values('12057','Mr Yang',21,'si chuan','96325874');"\
		   "insert into roommates(stu_id,name,age,address,telephone)"\
		   "values('12060','Mr qing',22,'yun nan','96325789');"\
		   "insert into roommates(stu_id,name,age,address,telephone)"\
		   "values('12059','Mr wang',20,'gui zhou','96325456');"\
		   "insert into roommates(stu_id,name,age,address,telephone)"\
		   "values('12065','Mr qi',22,'shang xi','96325874');";


	rv = sqlite3_exec(db,sql,callback,0,&z_err_msg);//執行sql語句
	if(rv)
	{
		printf("SQL error!:%s\n",z_err_msg);//打印錯誤信息
		sqlite3_free(z_err_msg);//用於釋放從內存中獲取的錯誤信息字符串
	}
	else
	{
		printf("select table successfully!\n");
	}
	sqlite3_close(db);//關閉數據庫

	return 0;
}

  • 編譯運行
    在這裏插入圖片描述
  • 查看錶中數據
    在這裏插入圖片描述

4、查詢數據

通過c語言編程接口查詢出roommates表中得記錄。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sqlite3.h>



/*
 功能:查詢記錄回調函數
 參數:
	para:由exec傳入的參數
	col_num:查詢出一條記錄有多少個表頭
	col_value:查詢出的記錄緩存區
	col_name:表示字段的段命
  返回:
	成功返回0
	失敗返回-1
 */
static int callback(void *para, int col_num, char **col_value, char **col_name)
{
	int i;
	printf("%s\n",(char*)para);
	for(i = 0; i < col_num; i++)
	{
		printf("%s = %s\n",col_name[i],col_value[i]?col_value[i]:"NULL");
	}
	printf("\n");

	return 0;
}


int main(int argc, char **argv)
{

	sqlite3					*db;//定義數據庫操作句柄
	int						rv;//返回值
	char					*sql;//SQL語句指針
	char					*z_err_msg;//獲取錯誤信息
	char					*data = "callback is called:";

	rv = sqlite3_open("test.db",&db);//打開數據庫
	if(rv)
	{
		printf("Can't open database!:%s\n",sqlite3_errmsg(db));
		exit(0);
	}
	printf("Opening database successfully!\n");

	//製作SQL語句
	sql =  "select * from roommates";


	rv = sqlite3_exec(db,sql,callback,(void *)data,&z_err_msg);//執行sql語句
	if(rv)
	{
		printf("SQL error!:%s\n",z_err_msg);//打印錯誤信息
		sqlite3_free(z_err_msg);//用於釋放從內存中獲取的錯誤信息字符串
	}
	else
	{
		printf("select table successfully!\n");
	}
	

	sqlite3_close(db);//關閉數據庫

	return 0;
}

  • 編譯運行
    在這裏插入圖片描述
    咦?我們會發現一個問題,前面連接數據庫、創建表、插入數據例程中都有設置回調函數,但是都沒有被調用,這裏查詢記錄卻調用了四次,這是什麼原因呢?

    回調函數,專門用來處理查詢記錄的結果,比如在這裏,roommates表中一共有四條記錄,所以回調函數被調用了四次,每次調用輸出一條記錄,像連接數據庫、創建表、插入數據例程中不需要回調函數可以直接傳參NULL。回調函數原型如下:

    這裏我想再進一步解釋一下columnValue和columnName這兩個二級指針,其餘兩個參數在上面程序中有說明。

printf("%s = %s\n",col_name[i],col_value[i]?col_value[i]:"NULL");

col_name、col_value:表示一個二級指針,也就是存放指針的數組。col_name表示存放字段的地址,
col_value表示字段下面的值的地址。

col_name[i]、col_value[i]:表示一個一級指針,col_name[i]表示存放字段字符串的首地址,
col_value[i]表示存放字段下面值字符串的地址。因爲字段和值都是字符串。
typedef int (*sqlite_callback)(void* para,int columnCount,char** columnValue,char** columnName);

5、刪除表中所有記錄

c語言實現刪除上面例程插入roommates表中的數據。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sqlite3.h>

/*
 功能:查詢記錄回調函數
 參數:
	para:由exec傳入的參數
	col_num:查詢出一條記錄有多少個表頭
	col_value:查詢出的記錄緩存區
	col_name:表示字段的段命
  返回:
	成功返回0
	失敗返回-1
 */
static int callback(void *para, int col_num, char **col_value, char **col_name)
{
	int i;
	printf("%s\n",(char*)para);
	for(i = 0; i < col_num; i++)
	{
		printf("%s = %s\n",col_name[i],col_value[i]?col_value[i]:"NULL");
	}
	printf("\n");

	return 0;
}

int main(int argc, char **argv)
{

	sqlite3					*db;//定義數據庫操作句柄
	int						rv;//返回值
	char					*sql;//SQL語句指針
	char					*z_err_msg;//獲取錯誤信息
	char					*data = "callback is called:";

	rv = sqlite3_open("test.db",&db);//打開數據庫
	if(rv)
	{
		printf("Can't open database!:%s\n",sqlite3_errmsg(db));
		exit(0);
	}
	printf("Opening database successfully!\n");

	//製作SQL語句
	sql =  "delete from roommates";


	rv = sqlite3_exec(db,sql,callback,(void *)data,&z_err_msg);//執行sql語句
	if(rv)
	{
		printf("SQL error!:%s\n",z_err_msg);//打印錯誤信息
		sqlite3_free(z_err_msg);//用於釋放從內存中獲取的錯誤信息字符串
	}
	else
	{
		printf("delete recodes successfully!\n");
	}

	sqlite3_close(db);//關閉數據庫

	return 0;
}

  • 編譯運行
    在這裏插入圖片描述
  • 再次查看錶中是否有數據存在
    在這裏插入圖片描述
    從圖中可以看出roommates表中已經沒有數據存在了。

總結一下

從以上的五個例程中可以看出,sqlite數據庫最重要的三大API接口分別是sqlite3_open、sqlite3_exec、sqlite3_close。這三個函數就可以輕鬆操作sqlite數據庫,只需要將SQL語句換成相應的操作語句就ok,簡單易行,沒有複雜可言,當然在這裏我只是演示一下sqlite功能中的冰山一角,還有很多很多的數據庫操作沒有去實現,讓我一時間實現一遍也不可能,只能在以後的項目實踐中慢慢探索,仔細深挖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章