SQLite是一種用C語言實現的的SQL資料庫
它的特點有:輕量級、快速、獨立、高可靠性、跨平臺
它廣泛應用在全世界範圍內的手機電腦應用的內建資料庫
官網地址:https://www.sqlite.org/index.html
SQLite因為其採用檔案儲存,且容易移植。在嵌入式中應用非常廣泛,可以說是嵌入式業務開發的必學庫
這次先講一下怎麼獲取原始碼和使用
SQLite3的官網是https://www.sqlite.org/download.html
在官網找到sqlite-autoconf-3410200.tar.gz
檔案下載,如下
wget https://www.sqlite.org/2023/sqlite-autoconf-3410200.tar.gz
tar -zxvf sqlite-autoconf-3410200
下載後解壓,會發現裡面很多其他的檔案。其中,tea目錄是(Tcl Extension Architecture)可以不用管
主要看裡面的c檔案和h檔案,所以我們把原始碼放到另一個目錄
在工程目錄建立一個目錄lib/sqlite3,然後刪除解壓後的原始碼目錄
cp sqlite-autoconf-3410200/*.c lib/sqlite3/
cp sqlite-autoconf-3410200/*.h lib/sqlite3/
rm -r sqlite-autoconf-3410200
將需要的標頭檔案和原始檔拷貝進去
有點強的是sqlite3.c檔案的大小居然有8.3M
完成後目錄樹大概應該是這個樣子的
├── build
├── CMakeLists.txt
├── main.cpp
└── lib
└── sqlite3
├── shell.c
├── sqlite3.c
├── sqlite3ext.h
├── sqlite3.h
└── sqlite3rc.h
其中shell.c是對應的命令列檔案,我們可以不用新增。仔細研讀官網的檔案
https://www.sqlite.org/howtocompile.html
所以CMakeLists.txt我們可以這麼寫
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(useSQLite LANGUAGES C CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread")
add_executable(useSQLite main.cpp)
# lib sqlite3
include_directories(lib/sqlite3)
add_library(sqlite3
STATIC
lib/sqlite3/sqlite3.c)
target_link_libraries(sqlite3 dl)
#
target_link_libraries(useSQLite sqlite3)
# sqlite3-cli
add_executable(SQLite3-cli lib/sqlite3/shell.c)
target_link_libraries(SQLite3-cli sqlite3)
其中關於新增-ldl選項的步驟參考回答:https://stackoverflow.com/questions/20131138/cmake-add-ldl-at-end-of-link-stage-of-add-library
新增main.cpp
檔案如下
#include <iostream>
#include <stdio.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
int i;
for (i = 0; i < argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char **argv) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
if (argc != 3) {
fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
return(1);
}
rc = sqlite3_open(argv[1], &db);
if (rc) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return(1);
}
rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
if (rc!=SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_close(db);
return 0;
}
簡單講一下這個原始碼,其實就是開啟一個資料庫檔案執行一條指令
看上面的Usage: %s DATABASE SQL-STATEMENT\n就知道,引數是帶引號的SQL語句
裡面呼叫了三個庫函數sqlite3_open()
、sqlite3_exec()
和sqlite3_close()
分別是開啟SQLite資料庫檔案,執行第二個引數給的命令,關閉SQLite資料庫檔案
因為SQLite是本地檔案儲存和讀寫的,所以使用起來還是比較簡單的,不用考慮太多網路的問題。
mkdir build && cd build
cmake .. && make
編譯出來有兩個可執行檔案,一個是useSQLite一個是SQLite3-cli。
useSQLite就是我們上面說的main.cpp的內容。
SQLite3-cli是官方給的一個命令列執行SQL的程式,可以用它去查一些資料什麼的
但是不是很建議用這個,建議用Ubuntu安裝的SQLite3去做查表操作,回退(刪除)比這個方便些(下面一節介紹怎麼安裝)
本文為作者原創文章,轉載請註明出處:https://www.cnblogs.com/nbtech/p/use_sqlite_library.html
那麼都叫SQLite了,肯定是支援SQL語句的,所以使用SQLite,肯定要懂一些SQL。
這裡需要注意的是SQLite中的SQL和其他資料庫的SQL有所區別,使用的時候需要小心。本文只講SQLite的SQL
不過也並不用太擔心,差異不是很大
那麼這裡簡單講兩個SQL語句
因為只是簡單的介紹文,所以不會說的很詳細
我們為了方便練習,可以直接在Ubuntu下安裝一個SQLite3程式
sudo apt install sqlite3
那麼簡單建立一個資料庫可以這樣
sqlite3 mydatabase.db
mydatabase.db是資料庫的名字,這樣開啟之後,就可以執行後面的SQL語句了
不過也可以拿到shell.c編譯出來的SQLite3-cli(這個比較難用,不過也夠用)
首先,資料庫,資料庫有關係型資料庫(SQL)和非關係型(NoSQL),那麼SQLite是屬於 關係型的資料庫
關係型的資料庫呢,一個特點就是它比較的結構化。結構化儲存,就是裡面存放東西都是相同結構的,那麼相同結構的東西呢就放在一個表裡面。
類似於書架上的書籍,整理的時候會將同一型別的書籍放到一個書架上面
那麼先建立一個書架,咳咳,建表
CREATE TABLE IF NOT EXISTS mytable (id integer primary key,name text);
上面語句可以建表,建表前會判斷表是不是已經存在,也可以不去判斷,如下
CREATE TABLE yourtable (id integer primary key, name text, age integer);
primary key表示將該欄位設定成主鍵
注意:語句結束需要由;(分號)進行結尾,表示這一句SQL結束了,SQLite可以執行了
那麼新建的這張表有兩個欄位,一個是id,一個是name。id欄位的型別是integer整形,就是整數型別,就是0、1、2這些
name欄位是text型別,就是文字型別,例如,"zhangsan"或者 "張三"就是文字型別
除了設定主鍵約束,還可以設定唯一約束。SQLite裡面設定唯一隻需要在欄位後面加上一個unique即可
例如在上面name text 改成name text unique
類比我們往書架上放一本書
資料庫,就是存放資料的一個地方。資料倉儲。
現在庫已經有了(已經建好表了),那麼就可以存入結構化的資料了。
在一張表中插入一行資料的操作很簡單,只需要
INSERT INTO mytable (id,name) values (1000, "zhangsan");
注意上面這個zhangsan要用引號括起來,表示這是一個字串(前面的1000因為是數位所以不用括號)
INSERT INTO表示插入到,mytable指定對應的表,所以上面語句表示向mytable這張表插入一行資料,資料內容就是
id是1000,name是"zhangsan"的一行資料
非常容易理解
重複值:如果我們想嘗試往裡面插入相同的id的行,就是報錯,因為id是主鍵,主鍵是不允許重複的。但是插入相同的非主鍵的值是被允許的。
INSERT INTO mytable (id, name) values (1000, "lisi");
Runtime error: UNIQUE constraint failed: mytable.id (19)
可以類比我們從書架上挑選感興趣的書,例如找兩本比較厚的書
如果我們想查詢一張表裡面的所有資料,如下
SELECT * FROM mytable;
*表示匹配所有項,FROM表示從mytable中,SELECT表示選擇
所以就是:從mytable表中選擇所有項
過濾結果
但是如果我們想從表中獲取特定的項呢,我們可以搭配WHERE,例如,我們想提取id大於1000的資料,可以這麼寫
SELECT * FROM mytable WHERE id > 1000;
例如我們的表資料內容如下
1000|zhangsan
1001|lisi
1002|wangwu
可以獲取這樣的結果
1001|lisi
1002|wangwu
可以類比我們從書架上拿下一本書,這本書不存放到這個書架了
DELETE就是刪除,那麼刪除一條資料就是要指定是哪個表的那條資料,可以這麼寫
DELETE FROM mytable WHERE id=1001;
就可以刪除id為1001的資料了,WHERE就是用來指定條件的,一般我們DELETE都是需要搭配WHERE使用,因為通常是要刪除一條或幾條資料。
那麼如果不加WHERE,就是刪除表上的所有資料(注意,只是刪除表裡面的所有記錄,表還是在的)
DELETE FROM mytable;
多條件
有時候我們需要刪除符合多個條件的資料,我們可以用AND將兩個語句連線起來
例如說,需要刪除age欄位大於35的並且id欄位小於1000的,可以這麼寫
DELETE FROM yourtable WHERE id<1000 AND age>35;
如果我們想修改一項資料,例如想將id為1000的"zhangsan"修改成"zhansang"
我們可以刪除id為1000的資料然後插入id為1000但是name欄位為"zhansang"的資料,但是我們可以不必這麼做,我們可以更新值
這個不好類比書架了,可以類比於拿下一本書換了一本上去吧
更新欄位的值,首先要知道是那個欄位,所以一定有WHERE語句,然後更新是UPDATE,所以更新欄位的語句就是
UPDATE mytable SET name="zhansang" WHERE name="zhangsan";
當然條件的欄位和SET的欄位不一定要同一個欄位,例如條件可以是id<1000,SET後面可以name="zhansang"類似這樣,就可以將所有id小於1000的name都更新成"zhansang"了
SQL資料庫的應用非常廣泛,包括資料採集、資料分析、單純的存取資料。
SQL是非常好用的資料庫查詢語言,並且它不復雜,比較容易懂。而且專業做SQL的人工資也不低(前提是就是靠這個吃飯的哈)。
學好SQL無論是對實用性還是經濟性來說都是非常好的,寫SQL有點像搭積木,想要什麼就搭建成什麼樣。
在前面編譯CMake工程中其實就有編譯出使用SQLite進行C語言程式設計的原始碼
裡面main.cpp裡面主要用到3個Sqlite3裡面的函數,分別是sqlite3_open、sqlite3_exec和sqlite3_close
非常簡單,三個函數分別是
sqlite3_open表示開啟一個資料庫檔案,一般為xxx.db
sqlite3_exec表示執行一條SQL語句
sqlite3_close表示關閉資料庫檔案
首先,先編寫如下程式碼
int opendatabase(sqlite3** db, const char* dbfilename) {
char *zErrMsg = 0;
int rc;
rc = sqlite3_open(dbfilename, db);
if(SQLITE_OK != rc) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(*db));
sqlite3_close(*db);
return -1;
}
return 0;
}
上面開啟一個名為mydatabase.db
的資料庫,db為該資料庫的控制程式碼
然後判斷有沒有錯誤(一般在指定檔案不是一個資料庫檔案會報錯),如果沒有指定的資料庫檔案會自動建立。
關閉一個資料庫檔案可以直接呼叫sqlite3_close(sqlite3* db);也可以自己封裝一個函數,如下
int clsoedb(sqlite3** db) {
if(NULL != *db) {
sqlite3_close(*db);
return 0;
}
return -1;
}
本文為作者原創文章,轉載請註明出處:https://www.cnblogs.com/nbtech/p/use_sqlite_library.html
因為可以執行的語句特別多,而且上面章節也講過有關SQLite語句的一些介紹,所以這裡主要講sqlite3_exec這個C語言介面
先看一下原始碼實現
SQLITE_API int sqlite3_exec(
sqlite3 *db, /* The database on which the SQL executes */
const char *zSql, /* The SQL to be executed */
sqlite3_callback xCallback, /* Invoke this callback routine */
void *pArg, /* First argument to xCallback() */
char **pzErrMsg /* Write error messages here */
){
第一個引數是資料庫的控制程式碼,用於運算元據庫用的。
第二個引數是傳遞給SQLite執行的SQL語句
第三個引數是執行後的回撥函數
第四個引數是回撥函數的第一個參
第五個引數是如果執行語句錯誤,用於返回錯誤資訊的字串指標
那麼第三個引數和第四個引數可以根據需要置為空或者給一個函數指標和一片記憶體(通常第四個引數用來返回執行後的結果)
不需要回撥的範例,參考如下程式碼
//建立表
int create_table(sqlite3 *pdb) {
char *sql = NULL;
char *errmsg = NULL;
int ret;
sql = (char*)"create table if not exists mytable (id integer primary key,name text);";
ret = sqlite3_exec(pdb, sql, NULL, NULL, &errmsg);
if(SQLITE_OK != ret) {
printf("create table error! %s\n", errmsg);
return -1;
} else {
return 0;
}
}
上面的sql的值就是建表的一個操作,可以參考上面SQL語句的相關介紹。因為建表操作我們只需要知道建表成功了還是失敗了,所以我們不需要填充xCallback和pArg引數,自然填NULL的時候不會呼叫到回撥
需要呼叫回撥的範例,參考如下程式碼
// callback
int show_row(void *return_, int column, char* result[], char** column_name) {
for(int i = 0; i < column; i++) {
printf("%s\t", result[i]);
}
printf("\n");
return 0;
}
// 查詢和顯示所有mytable的資料
int query_all_and_show(sqlite3* pdb) {
char sql[24] = {0};
char *errmsg = NULL;
int ret;
strncpy(sql, "select * from mytable;", 22);
// 資料庫,語句字串,回撥函數,使用者輸入的引數,最終傳給回撥函數使用,錯誤資訊
ret = sqlite3_exec(pdb, sql, show_row, NULL, &errmsg);
if(SQLITE_OK != ret) {
printf("select exec error: %s\n", errmsg);
return -1;
}
return 0;
}
上述程式碼查詢了當前mytable表裡面的所有行的資料,上面的會調函數當有多行的時候會呼叫多次。每一次都可以將一行的資料列印出來
如果需要將表返回到主函數,可以在pArg引數那裡填充一個結構體連結串列指標(或者vector),然後每次建立一項就可以返回整張表的內容。
除了sqlite3_exec()函數,SQLite3還提供了一個函數可以執行語句,並且可以在同一個函數中處理返回的資料,像上述的查表的操作其實用這個函數會更好一點
SQLITE_API int sqlite3_get_table(
sqlite3 *db, /* The database on which the SQL executes */
const char *zSql, /* The SQL to be executed */
char ***pazResult, /* Write the result table here */
int *pnRow, /* Write the number of rows in the result here */
int *pnColumn, /* Write the number of columns of result here */
char **pzErrMsg /* Write error messages here */
){
同樣的,我們看下引數,第一個引數是資料庫控制程式碼,第二個引數是需要執行的SQL語句
第三個引數是返回的結果的表
第四個引數是一共有多少行
第五個引數是一共有多少列
第六個引數是產生錯誤時的錯誤資訊返回
這裡不講具體的寫法了,輸出行列值可以參考以下寫法
for(int i = 0; i < Col; i++) {
for(int j = 0; j < Row; j++) {
printf("%s\t", azResult[i*Row+j]);
}
printf("\n");
}
SQLiteC++是一個簡潔易用的C++封裝庫
正常我們用C語言去程式設計,可以像上面章節說的,自己去封裝相關的操作。如果你的專案用上了C++,那麼我推薦用C++封裝的庫去寫,會比較方便一點。
上面C語言程式設計的章節,介紹了相關運算元據庫的流程,這裡不再重複介紹
我們看下使用SQLiteC++最簡單的範例是怎麼樣的,首先SQLiteC++的原始碼在:https://github.com/SRombauts/SQLiteCpp
我們可以建立一個工程,然後獲取SQLiteC++的原始碼
mkdir useSQLiteCpp && cd useSQLiteCpp
git clone https://github.com/SRombauts/SQLiteCpp.git
cd SQLiteCpp
git submodule init
git submodule update
在examples/example2/src路徑下有一個範例的main.cpp,我們可以通過觀察這個檔案學習SQLiteC++庫的相關操作,在useSQLiteCpp目錄建立main.cpp,內容如下
#include <iostream>
#include "SQLiteCpp/SQLiteCpp.h"
// https://www.cnblogs.com/nbtech/p/use_sqlite_library.html
int main() {
try
{
// Open a database file in create/write mode(用寫模式開啟一個資料庫檔案)
SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
// Create a new table with an explicit "id" column aliasing the underlying rowid(建立一個表,id設定為主鍵)
db.exec("DROP TABLE IF EXISTS test");
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
// first row(插入一行,id是NULL就是不指定,不指定會從1開始分配,value是test)
int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")");
std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl;
// second row(插入第二行,id是2,根據上一條記錄加一,value是second)
nb = db.exec("INSERT INTO test VALUES (NULL, \"second\")");
std::cout << "INSERT INTO test VALUES (NULL, \"second\")\", returned " << nb << std::endl;
// update the second row(將id為2的行的value值更新為second-updated)
nb = db.exec("UPDATE test SET value=\"second-updated\" WHERE id='2'");
std::cout << "UPDATE test SET value=\"second-updated\" WHERE id='2', returned " << nb << std::endl;
// Check the results : expect two row of result(讀取結果,應該會有兩行資料。其實就是查表)
SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
while (query.executeStep())
{
std::cout << "row (" << query.getColumn(0) << ", \"" << query.getColumn(1) << "\")\n";
}
db.exec("DROP TABLE test"); // 刪除test這個表
}
catch (std::exception& e)
{ // 例外處理
std::cout << "SQLite exception: " << e.what() << std::endl;
return EXIT_FAILURE; // unexpected error : exit the example program
}
// remove("test.db3"); // 刪除檔案
return 0;
}
上面的幾個操作總結就是:
1、宣告一個db檔案,以什麼形式開啟SQLite::Database db("test.db3", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);
2、通過exec方法執行各種SQL語句,db.exec(const char* );
3、定義查詢語句,並獲取結果SQLite::Statement query(db, "SELECT * FROM test");
4、例外處理
根據上面的註釋內容可以知道這個main.cpp做了哪些事情
編寫CMake工程也比較簡單,SQLiteC++是通過CMake管理的,所以新增為子專案即可
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(useSQLiteCpp LANGUAGES CXX)
add_executable(useSQLiteCpp main.cpp)
# SQLiteCpp
include_directories(SQLiteCpp/include)
option(SQLITECPP_RUN_CPPLINT "Not Run cpplint.py tool for Google C++ StyleGuide." OFF)
add_subdirectory(SQLiteCpp lib)
target_link_libraries(useSQLiteCpp SQLiteCpp)
編譯就不描述了
資料庫在業務開發中是非常常見的,而SQLite3被廣泛應用在各個領域。並且由於它的小型無伺服器結構並且依靠檔案儲存,也被廣泛應用在各種嵌入式系統中
所以說,瞭解SQLite3幾乎是從事嵌入式業務開發的必選項
本文從SQLite3的庫的獲取、工程管理、SQL語句介紹、C語言程式設計四個角度闡述了SQLite3資料庫的實際應用。希望對你的資料收集、資料管理有一定的啟蒙作用。
but, not yet
相信這對你只是一個開始,當前時代是資訊的時代,我們需要的資料越來越龐大,巨量資料在生活的各個角落起著越來越重要的作用。
在各種高並行、大流量的場景下,SQLite3還是不夠用。我們不能停下腳步,我們的目標是星辰大海。