C++之Sqlite3小白入門

一.sqlite3簡介

1.我的淺見

sqlite3(官方文檔)是一種輕量級且應用普遍的關係型數據庫的一種。sqlite3數據庫可運行在類Unix和window系統之上,其中,大部分的類Unix系統(如Linux)都默認安裝了sqlite3數據庫,我的使用的系統爲Ubuntu 16.04,默認狀況下系統已經安裝了sqlite3數據庫,檢測方法爲:在Terminal中執行sqlite3,便可進入sqlite3的命令編輯模式,若失敗請手動編譯安裝sqlite3數據庫。html

首先明白幾個概念:ios

1.DML(Data Manipulattion Language),顧名思義--數據操做語言。所謂數據操做,是指對數據的插入(insert),刪除(delete),更新(update)等。
2.DQL(Data Query Language)--數據查詢語言。DQL的基本結構是由SELECT等查詢語句及其子語句組成。
3.DDL(Data Definition Language)--數據定義語言。DDL是對數據庫進行建立的語言。好比建立數據庫(create database),建立數據表(create table),刪除數據表(drop table)等。

2.sqlite3語法

sqlite有其獨特的語法規則。c++

1.大小寫的敏感性.在sqlite中,是不區分大小寫的,例如建立表語句`CREATE TABLE tablename;`;和`create table tablename;`效果是徹底同樣的,再好比查詢語句'select * from tablename'和'SELECT * FROM tablename'也是徹底同樣的。
2.全部的sqlite語句能夠以任何關鍵字開始,`select`,`insert`,`create`等,以距離最近的 **「;」** 做爲本條語句的結束標誌。

3.sqlite的命令

sqlite數據庫中有一些特殊的命令,這些特殊的命令都以 .(點)開頭,命令結尾不須要「;」 結尾,這些特殊的命令被稱爲sqlite的點命令。有一個特殊的點命令.help,它能夠列出全部sqlite的點命令及其使用說明,如圖所示:sqlietehelp.pngsql

sqlite點命令的詳細介紹數據庫

二.sqlite3之C++接口

sqlite提供原生態的C/C++接口,從其C/C++接口文檔能夠查看全部的接口及使用方法。簡單介紹幾個經常使用的對象和接口:編程

1.`sqliet3`.數據庫對象,相似於數據庫的入口。
2.`sqlite3_stmt`.狀態存儲對象,按照官網解釋 「 An instance of this object represents a single SQL statement that has been compiled into binary form and is ready to be evaluated.」 通俗來講,就是它是一個對象實體,該實體所存儲的東西是已經**預編譯**好的單條SQL語句,這個實體對象能夠被後續的sqlite3語句執行。`sqlite3_stmt`對象相似於C/C++語言中**預編譯**的結果(在C++編程中,預編譯的結果存儲在二進制文件中,咱們通常看不到)。
3.`sqlite3_open(const char *filename,sqlite3 **ppDb)`接口。該函數用於打開或建立(若`filename`不存在)一個數據庫對象,建立的結果保存在`ppDd`所指定的`sqlite3`對象中.
4.`sqlite3_prepare_v2(sqlite3 *db,const char *zSql,int nByte,sqlite3_stmt **ppStmt,const char **pzTail);`接口,該函數主要是對SQL語句進行預編譯,並將結果存在參數4指定的對象中。其中參數2`zSal`是代指一條SQL語句的字符串,參數3代指該`zSql`字符串的最大佔用字節長度,參數4 `雙重指針參數ppStmt`即存儲通過sqlite3編譯後的SQL語句的結果,最後一個參數指代參數`zSql`字符串的沒有使用的地址偏移量,通常置`nullptr`便可。該函數執行成功,返回`int`類型的`SQLITE_OK`,失敗返回錯誤代碼。
5.`sqlite3_step(sqlite3_stmt *);`接口,該函數主要是用於執行`sqlite3_prepare()`函數所編譯好的`sqlite3_stmt`對象。對與DDL和DML而言,該函數執行成功返回`SQLITE_DONE`,對於DQL而言,若執行成功,則返回`SQLITE_ROW`。
6.`sqlite3_finalize(sqlite3_stmt *pStmt);`接口,主要用於釋放`sqlite3_stmt`對象所佔用的資源,執行成功過返回`SQLITE_OK;`。
7.`sqlite3_close(sqlite3 *);`接口,該函數主要用於關閉`sqlite3`數據庫對象,成功返回`SQLITE_OK`。

以上兩個對象(1 和 2)以及5個接口函數(3-7)應該算是SQLite c/c++編程中最重要也是最基礎的,幾乎每一個SQLite C編程都離不開。固然還有一些其餘重要的函數,好比sqlite3_bind()族類,sqlite3_column()族類,以及sqlite3_exec()等,這些函數的存在極大方便了sqlite3編程。函數

三.sqlite3編程示例

在這裏建立了一個sqlite操做的類Data,全部對sqlite數據庫的操做都做爲該類的方法,該程序文件以下:測試

#include <sqlite3.h>
#include <string.h>
#include <iostream>
#include <string>
using namespace std;
//建立操做數據庫的類

class Data
{
public:
    Data(){
        cout<<"執行構造函數"<<endl;    
    };
  
    ~Data(){
        cout<<"執行析構函數"<<endl;
     }
    //1.建立數據庫和數據表
    void createData() ;
    //2.插入數據
    void insertData();
    //3.更新數據(更新第id條記錄的第n列字段的值)
    void updateData(const int id,const int n );
    //4.顯示開啓事物,批量插入數據
    void insertBatch() ;
    //5.選擇數據(選擇特定行數的數據並打印出來)
    void selectData(const int row,const int offset)  ;
    //6.關閉數據庫
    void close(){
        sqlite3_close(conn);    
        return ;
    }
    //7.清空數據庫表,以便於下次操做測試
    void drop();
private:
    sqlite3 *conn = nullptr;
    int rc;
    int count = 10;
};

void Data::updateData(const int id,const int n){
    const char *field = nullptr;
    const char *update ; 
    char str[64] = {0};
    if(1 == n){
        cout<<"約束主鍵不能修改"<<endl;
       // field = "ID";   
      //  update = "update test set ID = %d where ID = %d";
      //  sprintf(str,update,n,id);
     }else if(2 == n){
        field = "name";    
        update = "update test set name = '%s' where ID = %d";
        sprintf(str,update,"yxg",id);
     }else{
        field = "width";    
        update = "update test set width = %f where ID = %d";
        sprintf(str,update,99.8,id);
     };
    
    sqlite3_stmt *stmt = nullptr;
    if(sqlite3_prepare_v2(conn,str,strlen(str),&stmt,nullptr) != SQLITE_OK){
        sqlite3_finalize(stmt);
        close();
        return ;
    }
    if(sqlite3_step(stmt)!=SQLITE_DONE){
        sqlite3_finalize(stmt);
        close();
        return;
    }

    sqlite3_finalize(stmt); 
}

void Data::selectData(const int row,const int offset) {
    int r = row,o = offset;
    const char * select1 = "select * from test limit %d,%d ";
    char select[64] = {0};
    sprintf(select,select1,row,offset);
    sqlite3_stmt *stmt = nullptr;
    // const char * select = "select * from test limit 1,1";
    if(sqlite3_prepare_v2(conn,select,strlen(select),&stmt,nullptr) != SQLITE_OK){
        cout<<"編譯select語句失敗"<<endl;
        close();
        sqlite3_finalize(stmt);
        return ;
    }
    if(sqlite3_step(stmt) == SQLITE_ROW){
        // cout<<"查詢好了:"<<endl;
        int fieldcount = sqlite3_column_count(stmt);
        cout<<"該表所含字段數量是:"<<fieldcount<<endl;
    
        for(int i = 0;i < fieldcount; ++i){
            int type = sqlite3_column_type(stmt,i);
            if(type == SQLITE_INTEGER){
                int v = sqlite3_column_int(stmt,i);
                cout<<"ID   is:  "<<v<<endl;    
            }else if(type == SQLITE_TEXT){
                const char *v=(const char *)sqlite3_column_text(stmt,i);
                string s =  v;
                cout<<"Name is:  "<<s<<endl;
            }else if(type == SQLITE_FLOAT){
                int v = sqlite3_column_int(stmt,i);
                cout<<"Age  is:  "<<v<<endl;
            }else{
                cout<<"The result is nullptr!!"<<endl;    
            }   
            
        }        
    }
    sqlite3_finalize(stmt);
            
}

void Data::drop(){
    
    const char *drop = "drop table test";
    sqlite3_stmt *stmt  = nullptr;
    if(sqlite3_prepare_v2(conn,drop,strlen(drop),&stmt,nullptr ) != SQLITE_OK){
        close();
        sqlite3_finalize(stmt);
        return ;
    }
    if(sqlite3_step(stmt) == SQLITE_DONE){
        cout<<"成功銷燬數據表"<<endl;
    }
            
    sqlite3_finalize(stmt);
    close();    
        
}

void Data::insertBatch(){
    //開啓一個事物
    const char *begin = "begin transaction";
    sqlite3_stmt *stmt = nullptr;
    if(sqlite3_prepare_v2(conn,begin,strlen(begin),&stmt,nullptr)!=SQLITE_OK){
        close();
        cout<<"預編譯事物失敗!!"<<endl;
        return ;    
    }
    if(sqlite3_step(stmt) != SQLITE_DONE){
        close();
        cout<<"執行事物失敗"<<endl;
        return;
    }
    sqlite3_finalize(stmt);

    //基於綁定變量插入數據
    const char *insert = "insert into test values(?,?,?)";
    sqlite3_stmt *stmt2 = nullptr;
    if(sqlite3_prepare_v2(conn,insert,strlen(insert),&stmt2,nullptr) != SQLITE_OK){
        close();
        return ;
    }
   // char *name = "this is name";
    for(int i = 0; i < count; ++i){
        //數據表最左邊的索引爲1
        sqlite3_bind_int(stmt2,1,i);
        string name = "this is name ";
        name += to_string(i);
        sqlite3_bind_text(stmt2,2,name.c_str(),sizeof(name),SQLITE_TRANSIENT);
        sqlite3_bind_double(stmt2,3,19.1*i);
        if(sqlite3_step(stmt2)!= SQLITE_DONE){
            close();
            sqlite3_finalize(stmt2);
            return ;    
        }
        sqlite3_reset(stmt2);
        cout<<"Insert succeed!"<<endl;
    }
    sqlite3_finalize(stmt2);

    //提交事務
    const char * commit= "commit";
    sqlite3_stmt *stmt3 = nullptr;
    if(sqlite3_prepare_v2(conn,commit,strlen(commit),&stmt3,nullptr)!= SQLITE_OK){
        close();
        sqlite3_finalize(stmt3);
        return;    
    }
    if(sqlite3_step(stmt3)!=SQLITE_DONE){
           
        close();
        sqlite3_finalize(stmt3);
        return;    
     }
     sqlite3_finalize(stmt3);
       
        
}

void Data::createData()  {
    //在當前目錄下打開(或建立)test.db數據庫。
    rc = sqlite3_open("test.db",&conn);    
    if(rc != SQLITE_OK){
        close();
        cout<<"建立數據庫失敗!!"<<endl;
        return ;
    }
    //SQL語句
    /*
    const char *createTable = "create table test(" \
        "ID INT PRIMARY KEY NOT NULL,"\
        "name TEXT NOT NULL," \
        "age INT NOT NULL," \
        " );";*/
    const char * createTable = "create table test(ID INT PRIMARY KEY NOT NULL,name TEXT,width REAL)";
    sqlite3_stmt *stmt = nullptr;
    //預編譯SQL語句
    if(sqlite3_prepare_v2(conn,createTable,strlen(createTable),&stmt,nullptr) != SQLITE_OK){
        cout<<"預編譯失敗"<<endl;
        sqlite3_finalize(stmt);
        close();
        return;
    }
    //執行SQL語句
    if(sqlite3_step(stmt) != SQLITE_DONE){
        sqlite3_finalize(stmt);
        cout<<"執行失敗"<<endl;
        close();
        return ;
    }
    sqlite3_finalize(stmt);
    cout<<"建立數據庫和數據表成功!!"<<endl;
         
}

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

    Data *base = new Data;
    base->createData();
    base->insertBatch();
    //打印從第一條記錄開始的一條記錄(記錄索引從0開始)
    base->selectData(1,1);
    base->updateData(1,2);

    base->drop();
    delete base;
    base = nullptr;
    return 0;    
}

從以上簡單示例能夠看出,sqlite3c++編程基本都要通過三個步驟(在不使用sqlite3_exec()):優化

1.聲明`SQL`語句和`sqlite3_stmt` 對象;
2.調用`sqlite3_prepare()`函數 「預編譯」;
3.調用`sqlite3_step()`函數執行`SQL`操做.

在上面幾個類方法中,惟一有點特別的是void insertBatch()函數,與其餘功能函數的主要區別就是:在該函數中,顯示的開啓了一個事務,全部的插入操做均在該事務內完成,直到該事務被顯示的提交
所謂事務,其實就是一塊執行單元,一塊代碼段,在該執行單元內的sqlite操做,都不會自動的提交到數據庫中,直到顯示的執行 SQL commit。在sqlite中,全部的操做本質都是「事務性」的,只不過被sqlite隱式的建立並提交,默認狀況下,每個sqlite3函數的調用都伴隨着事務的發生。
通常狀況下,數據插入相似下面的代碼:this

void insert(){
    const char *insertSQL= " ..... ";
    sqlite3_stmt 對象;
    for(int i=0;i < count;++i){
        執行預編譯 sqlite3_prepare();
        執行插入 sqlite3_step();
        ....
        ....
    }
}

在方法void insertBatch()中,如要插入大批量數據,只須要執行一次 「預編譯」 便可,而後基於數據綁定的方式在事務內部執行SQL插入語句,極大節省了sqlite3_prepare_v2()函數的調用次數(一般 函數sqlite3_prepare_v2()比函數sqlite3_step()執行時佔用時間更多)。

能夠看出,在執行大批量操做時,顯示的建立並提交事務,可以優化編碼,並提升代碼執行效率,而且各個事務之間是隔離狀態--不一樣事務之間相互獨立和透明的。

注意:在結束數據庫操做的時候,必定記得執行sqlite3_close()函數關閉該庫,在執行關閉操做以前,也要確保每個sqlite3_stmt對象的資源被釋放(調用sqlite3_finalize()),不然容易形成內存泄露。

相關文章
相關標籤/搜索