sqlite3的API以及命令行

sqlite是目前最流行的嵌入式數據庫。

所謂嵌入式,就是足夠簡單,可以嵌入到我們自己開發的應用程序之中。

在Linux系統中,sqlite的使用只需要使用它的API,連接它的動態連接庫,甚至都不用連接,sqlite的實現只使用一個C語言源程序,直接編譯進自己的應用里面就好。

現在sqlite的最新版本是3,所以我們后面會以這個版本為例。

API

sqlite的API簡單到什么程序呢?

當我們開發應用的時候,通常的情況下,只需要使用sqlite3_open、sqlite3_exec與sqlite3_close這三個函數就夠了。

它們的原型為:

int sqlite3_open(const char *filename, sqlite3 **ppDb);int sqlite3_close(sqlite3*);int sqlite3_exec(  sqlite3*,                                  /* An open database */  const char *sql,                           /* SQL to be evaluated */  int (*callback)(void*,int,char**,char**),  /* Callback function */  void *,                                    /* 1st argument to callback */  char **errmsg                              /* Error msg written here */  
);

這三個函數都使用了一個sqlite3的指針的指針,指向了我們要操作的數據庫實體的指針,不用解釋。

sqlite3_open的第一個參數,表示要創建的數據庫的路徑。

值得一提的是sqlite3_exec。它有一個回調函數以及參數,當有多個返回值的時候,比如select語句被執行之后,將通過callback函數把每一條記錄返回。另外還有一個錯誤字符串,如果出錯將被賦值,需要應用層釋放

如:


static int  
get_id_callback (void *para, int n_column, char **column_value,  char **column_name)  
{  int *id = (int *)para;  if (column_value[0] != NULL)  {  *id = strtoull (column_value[0], NULL, 10);  }  return 0;  
}int main (char *argc, char *argv[])  
{  gchar *dirname;  sqlite3 *db;char *errMsg;int rc;if (sqlite3_open ("/tmp/test.sqlite", &db) != 0)  {  printf ("Open sqlite db failed: %s.", file, strerror (errno));  return 1;  }  rc = sqlite3_exec (db, "select * from media", get_id_callback, NULL, &errMsg);  if (rc != SQLITE_OK)  {  printf ("SQL error: %s in [%s]", errMsg, text);  sqlite3_free (errMsg);  return 2;}sqlite3_close (db);  return 0;  
}  

轉義

sqlite3_exec執行的sql語句里面,如果有值字符串中含有’,需要進行轉義。

轉義的格式是使用兩個’,即’'。

如:

const char *lan = "It's a secret !";

這里的lan需要轉義為:

const char *lan = "It''s a secret !";

可以寫一個簡單的轉化函數實現這個功能:

int convert_str(const char *str, char *output, size_t out_size)
{*p;  int trans_len;for (trans_len = 0, p = str; *p && trans_len + 2 < out_size; trans_len++, p++)  {  output[trans_len] = *p;  if (*p == '\'')  {  output[trans_len + 1] = *p;  trans_len++;  }  }if (trans_len + 1 >= out_size) {printf("str is too long\n");return -1;}output[trans_len] = '\0';return trans_len;
}

除了這樣轉義,還有沒有更好的方法呢?

有的。

方法就是使用預編譯執行。

預編譯執行

所謂預編譯執行,就是生成一個sql語句的結構,再賦值進去,之后執行。

這樣做有多個好處。

首先是字符串不用轉義了。

其次是,生成的sql語句結構可以復用,每次重置,再賦值,可以極大地提升性能。

預編譯執行的方法也很簡單,就是三步:

  1. 使用sqlite3_prepare生成一個sqlite3_stmt結構。
  2. 使用sqlite3_bind綁定值。
  3. 使用sqlite3_step執行。

sqlite3_prepare_v2

sqlite3_prepare_v2是建議的新版本,其實還有一個為了兼容性的sqlite3_prepare以及更新的sqlite3_prepare_v3。基于“性價比”考量,我們使用sqlite3_prepare_v2就行了。

sqlite3_prepare_v2的原型為:

SQLITE_API int sqlite3_prepare_v2(  sqlite3 *db,            /* Database handle */  const char *zSql,       /* SQL statement, UTF-8 encoded */  int nByte,              /* Maximum length of zSql in bytes. */  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */  const char **pzTail     /* OUT: Pointer to unused portion of zSql */  
);

其中,db是我們打開的數據庫實例,zSql是我們要執行的sql語句,語句中的變量使用?進行占位,而ppStmt是我們生成的sqlite3_stmt結構的指針的指針。

sqlite3_bind_*

為了綁定不同的值,有幾個不同的函數可以使用,如:sqlite3_bind_intsqlite3_bind_doublesqlite3_bind_text等。

SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));  
SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,  void(*)(void*));  
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);  
SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);  
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);  
SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);  
SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
// 省略其它

這些函數都是第一個是前面生成的sqlite3_stmt的指針,第二個是綁定的值的位置,第三個以及后面是值信息。

值得注意的是,這些函數的位置參數,即索引,從1開始。

比如,我們使用了

sqlite3_bind_int (stmt, 1, 1024);

就是把生成stmt的sql中的第1個?賦值了,不要使用0。

sqlite3_step

賦值完成后,就使用sqlite3_step執行。

這個函數原型特別簡單,就是:

SQLITE_API int sqlite3_step(sqlite3_stmt*);

但是這個函數的返回值很豐富。不同的返回值,需要做不同的處理。

比如,在不出錯的情況下,如果返回SQLITE_DONE,表示執行成功。

如果返回SQLITE_ROW,表示返回了一行數據,我們可以使用sqlite_column_*族的函數取得列的值。之后需要再次調用sqlite3_step,直到最后返回了SQLITE_DONE

sqlite3_column_*

SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);  
SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);  
SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol);  
SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);  
SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
// 省略其它

可以看到,這些函數基本上跟sqlite3_bind_*是對應的。

唯一需要注意的是,這些函數的索引是從0開始的。

另外,sqlite3_data_count可以取得當前行的列數。

示例

最后來一個示例,總結一下:

int insert_medias(sqlite3_database *db, struct media *medias, size_t count)
{int ret;sqlite3_stmt *stmt;const char *sql = "INSERT INTO media(path, size) VALUES(?,?);";if (sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL) != SQLITE_OK) {printf ("sqlite3_prepare_v2() error: %s\n", sqlite3_errmsg (db));return -1;}for(int i = 0; i < count; i++){sqlite3_bind_text(stmt, 1, medias[i].name, strlen (medias[i].name), NULL);sqlite3_bind_int(pstmt, 2, medias[i].size);if (sqlite3_step(pstmt) != SQLITE_DONE) {printf ("sqlite3_step() error: %s\n", sqlite3_errmsg (db));return i;}sqlite3_reset(pstmt);}sqlite3_finalize(pstmt);return count;
}void query_media (sqlite3_database *db) 
{int ret;sqlite3_stmt *stmt;const char *sql = "SELECT * FROM media;";if (sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL) != SQLITE_OK) {printf ("sqlite3_prepare_v2() error: %s\n", sqlite3_errmsg (db));return -1;}ret = sqlite3_step (stmt);while (ret == SQLITE_ROW) {printf("get media: %s, size: %d\n", sqlite3_coumn_text (stmt, 0), sqlite3_column_int (stmt, 1));ret = sqlite3_step (stmt);}if (ret != SQLITE_DONE) {printf ("sqlite3_step() error: %s\n", sqlite3_errmsg (db)); }
}

命令行

sqlite3有一個命令行工具,就叫sqlite3。

我們使用sqlite3 /tmp/test.sqlite3就可以打開前面創建的數據庫。

在這個命令終端里,以.開頭的是內置的命令。

如:

> .help 顯示幫助
> .tables 顯示數據表
> .schema TableName 顯示創建數據表的語句,類似mysql里的describe
> .open 文件名 關閉當前,打開另一個數據庫
> .quit 退出
> .save 文件名 另存當前數據庫到文件
> .headers on/off 在查詢結果前面,是否顯示列名

執行sql語句,就直接輸入,加;回車執行就行了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76227.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76227.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76227.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Allure測試報告按測試終端和測試類型智能分類查看

以下是實現Allure測試報告按測試終端和測試類型智能分類的完整方案: 一、測試框架分層設計 # 項目結構 project/ ├── api_tests/ # API測試 │ └── test_order.py ├── app_tests/ # 移動端測試 │ ├── android/ │ └── ios/ ├── pc_te…

Spine-Leaf 與 傳統三層架構:全面對比與解析

本文將詳細介紹Spine-Leaf架構&#xff0c;深入對比傳統三層架構&#xff08;Core、Aggre、Access&#xff09;&#xff0c;并探討其與Full-mesh網絡和軟件定義網絡&#xff08;SDN&#xff09;的關聯。通過通俗易懂的示例和數據中心網絡分析&#xff0c;我將幫助您理解Spine-L…

圖像預處理-圖像噪點消除

一.基本介紹 噪聲&#xff1a;指圖像中的一些干擾因素&#xff0c;也可以理解為有那么一些點的像素值與周圍的像素值格格不入。常見的噪聲類型包括高斯噪聲和椒鹽噪聲。 濾波器&#xff1a;也可以叫做卷積核 - 低通濾波器是模糊&#xff0c;高通濾波器是銳化 - 低通濾波器就…

安卓手機如何改ip地址教程

對于安卓手機用戶而言&#xff0c;ip修改用在電商、跨境電商、游戲搬磚、社交軟件這些需要開多個賬號的項目。因為多個設備或賬號又不能在同一ip網絡下&#xff0c;所以修改手機的IP地址防檢測成為一個必要的操作。以下是在安卓手機上更改IP地址的多種方法及詳細步驟&#xff0…

對象池模式在uniapp鴻蒙APP中的深度應用

文章目錄 對象池模式在uniapp鴻蒙APP中的深度應用指南一、對象池模式核心概念1.1 什么是對象池模式&#xff1f;1.2 為什么在鴻蒙APP中需要對象池&#xff1f;1.3 性能對比數據 二、uniapp中的對象池完整實現2.1 基礎對象池實現2.1.1 核心代碼結構2.1.2 在Vue組件中的應用 2.2 …

本地部署大模型實現掃描版PDF文件OCR識別!

在使用大模型處理書籍 PDF 時&#xff0c;有時你會遇到掃描版 PDF&#xff0c;也就是說每一頁其實是圖像形式。這時&#xff0c;大模型需要先從圖片中提取文本&#xff0c;而這就需要借助 OCR&#xff08;光學字符識別&#xff09;技術。 像 Gemini 2.5 這樣的強大模型&#x…

《Operating System Concepts》閱讀筆記:p700-p732

《Operating System Concepts》學習第 60 天&#xff0c;p700-p732 總結&#xff0c;總計 33 頁。 一、技術總結 1.Virtual machine manager (VMM) The computer function that manages the virtual machine; also called a hypervisor. VMM 也稱為 hypervisor。 2.types …

軟件項目驗收報告模板

軟件項目驗收報告 一、項目基本信息 項目名稱XX智能倉儲管理系統開發單位XX科技有限公司驗收單位XX物流集團合同簽訂日期2023年3月15日項目啟動日期2023年4月1日驗收日期2024年1月20日 二、驗收范圍 入庫管理模塊&#xff08;包含RFID識別、庫存預警&#xff09;出庫調度模…

深度學習筆記39_Pytorch文本分類入門

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 | 接輔導、項目定制 一、我的環境 1.語言環境&#xff1a;Python 3.8 2.編譯器&#xff1a;Pycharm 3.深度學習環境&#xff1a; torch1.12.1cu113torchvision…

二分查找-LeetCode

題目 給定一個 n 個元素有序的&#xff08;升序&#xff09;整型數組 nums 和一個目標值 target&#xff0c;寫一個函數搜索 nums 中的 target&#xff0c;如果目標值存在返回下標&#xff0c;否則返回 -1。 示例 1: 輸入: nums [-1,0,3,5,9,12], target 9 輸出: 4 解釋: …

從 Ext 到 F2FS,Linux 文件系統與存儲技術全面解析

與 Windows 和 macOS 操作系統不同&#xff0c;Linux 是由愛好者社區開發的大型開源項目。它的代碼始終可供那些想要做出貢獻的人使用&#xff0c;任何人都可以根據個人需求自由調整它&#xff0c;或在其基礎上創建自己的發行版本。這就是為什么 Linux 存在如此多的變體&#x…

leetcode:3210. 找出加密后的字符串(python3解法)

難度&#xff1a;簡單 給你一個字符串 s 和一個整數 k。請你使用以下算法加密字符串&#xff1a; 對于字符串 s 中的每個字符 c&#xff0c;用字符串中 c 后面的第 k 個字符替換 c&#xff08;以循環方式&#xff09;。 返回加密后的字符串。 示例 1&#xff1a; 輸入&#xff…

JVM詳解(曼波腦圖版)

(?ω?)&#xff89; 好噠&#xff01;曼波會用最可愛的比喻給小白同學講解JVM&#xff0c;準備好開啟奇妙旅程了嗎&#xff1f;(??????)? &#x1f4cc; 思維導圖 ━━━━━━━━━━━━━━━━━━━ &#x1f34e; JVM是什么&#xff1f;&#xff08;蘋果式比…

ZStack文檔DevOps平臺建設實踐

&#xff08;一&#xff09;前言 對于軟件產品而言&#xff0c;文檔是不可或缺的一環。文檔能幫助用戶快速了解并使用軟件&#xff0c;包括不限于特性概覽、用戶手冊、API手冊、安裝部署以及場景實踐教程等。由于軟件與文檔緊密耦合&#xff0c;面對業務的瞬息萬變以及軟件的飛…

Git創建分支操作指南

1. 創建新分支但不切換&#xff08;僅創建&#xff09; git branch <分支名>示例&#xff1a;創建一個名為 new-feature 的分支git branch new-feature2. 創建分支并立即切換到該分支 git checkout -b <分支名> # 傳統方式 # 或 git switch -c <分支名&g…

package.json 中的那些版本數字前面的符號是什么意思?

1. 語義化版本&#xff08;SemVer&#xff09; 語義化版本的格式是 MAJOR.MINOR.PATCH&#xff0c;其中&#xff1a; MAJOR&#xff1a;主版本號&#xff0c;表示不兼容的 API 修改。MINOR&#xff1a;次版本號&#xff0c;表示新增功能但保持向后兼容。PATCH&#xff1a;修訂號…

如何有效防止服務器被攻擊

首先&#xff0c;我們要明白服務器被攻擊的危害有多大。據不完全統計&#xff0c;每年因服務器遭受攻擊而導致的經濟損失高達數十億。這可不是一個小數目&#xff0c;就好比您辛苦積攢的財富&#xff0c;瞬間被人偷走了一大半。 要有效防止服務器被攻擊&#xff0c;第一步就是…

Chainlit 快速構建Python LLM應用程序

背景 chainlit 是一款簡單易用的Web UI goggle&#xff0c;它支持使用 Python 語言快速構建 LLM 應用程序&#xff0c;提供了豐富的功能&#xff0c;包括文本分析&#xff0c;情感分析等。 這里我們以官網openai提供的例子&#xff0c;快速的開發一個帶有UI的聊天界面&#xf…

華為OD機試真題——硬件產品銷售方案(2025A卷:100分)Java/python/JavaScript/C++/C語言/GO六種最佳實現

2025 A卷 100分 題型 本文涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、測試用例以及綜合分析&#xff1b; 并提供Java、python、JavaScript、C、C語言、GO六種語言的最佳實現方式&#xff01; 2025華為OD真題目錄全流程解析/備考攻略/經驗分享 華為OD機試真題《硬件產品…

【數據結構_6】雙向鏈表的實現

一、實現MyDLinkedList&#xff08;雙向鏈表&#xff09; package LinkedList;public class MyDLinkedList {//首先我們要創建節點&#xff08;因為雙向鏈表和單向鏈表的節點不一樣&#xff01;&#xff01;&#xff09;static class Node{public String val;public Node prev…