Linux(程序設計):18---C語言訪問MySQL(mysql.h)

1、C語言訪問MySQL的前提

①安裝了MySQL服務端

②安裝MySQL庫

  • 使用C語言訪問MySQL以前須要安裝MySQL庫,輸入如下命令安裝:
  1.  
    #環境:Ubuntu 14.04
  2.  
     
  3.  
    sudo apt- get install libmysqlclient-dev

③gcc編譯MySQL程序

gcc -I/usr/include/mysql demo.c -L/usr/lib/mysql -lmysqlclient -o demo
 
  • 使用-I添加include路徑
  • 使用-L添加庫文件路徑
  • -lmysqlclient指定連接的庫模塊
  • 備註:在某些系統上,你可能還須要使用-lz選項來連接壓縮庫

④mysql頭文件

  • 系統中的mysql頭文件在/usr/include/mysql/目錄下

2、鏈接句柄初始化(mysql_init)

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    MYSQL *mysql_init(MYSQL *mysql);
  • 功能:用來初始化鏈接句柄
  • 參數:
    • 若是爲NULL,則經過返回值返回一個指向新分配的鏈接句柄結構的指針
    • 若是傳遞一個 已有的結構,它將被從新初始化
  • 返回值:出錯返回NULL

3、MySQL的鏈接、斷開(mysql_real_connect、mysql_close)

 MySQL的鏈接

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    MYSQL * mysql_real_connect(MYSQL *mysql,
  4.  
    const char *host,
  5.  
    const char *user,
  6.  
    const char *passwd,
  7.  
    const char *db,
  8.  
    unsigned int port,
  9.  
    const char *unix_socket,
  10.  
    unsigned long clientflag);
  • 功能:用來鏈接數據庫
  • 參數:
    •  mysql:必須指向已經被mysql_init初始化過的結構
    • host:既能夠是主機名,也能夠是IP地址(若是隻是鏈接到本地機器,你能夠經過 指定localhost來優化鏈接類型)
    • user:登陸用戶(若是登陸名爲NULL,則假設登陸 名爲當前Linux用戶的登陸ID)
    • passwd:用戶的密碼(若是密碼爲NULL,你將只能訪問服務器上無需密碼就可訪問的數據),密碼會在經過網絡傳輸前進行加密
    • db:要訪問的數據庫
    • port:應該填爲0,他們會自動默認使用合適的值(除非你改變了MySQL安裝的默認設置)
    • unix_socket:應該填爲NULL,他們會自動默認使用合適的值(除非你改變了MySQL安裝的默認設置)
    • clientflag:用來對一些定義的位模式進行OR操做,使得改變使用協議的某些特性。通常填0
  • 返回值:
    • 若是出錯,返回NULL,mysql_error函數能夠提供有幫助的信息

MySQL的斷開

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    void mysql_close(MYSQL *sock);
  • 功能:關閉鏈接
  • 若是鏈接是由mysql_init創建的,MySQL結構會被釋放。指針將會失效並沒有法再次使用
  • 保留一個不須要的鏈接是對資源的浪費,可是從新打開鏈接也會帶來額外的開銷,你可使用下面的mysql_options選項函數來權衡選擇適合的選項

4、MySQL選項設置(mysql_options)

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    int mysql_options(MYSQL *mysql,enum mysql_option option,const void *arg);
  • 功能:用來設置MySQL鏈接的一些選項
  • 注意:
    • ①僅能在mysql_init和mysql_real_connect之間調用
    • ②由於mysql_options一次只能設置一個選項,因此每設置一個選項就得調用它一次
  • 參數:
    • mysql:MySQL鏈接句柄
    • option:設置的選項
    • arg:相對於option選項要設置的值,在使用時須要強制轉換爲(const char*)
  • option參數以下:
option選項 實際參數類型 說明
MySQL_OPT_CONNECT_TIMEOUT const unsigned int * 鏈接超時以前的等待秒數
MySQL_OPT_COMPRESS None,使用NULL 網絡鏈接中使用壓縮機制
MySQL_INIT_COMMAND const char * 每次鏈接創建後發送的命令

mysql.h中定義的選項選項:html

  • 返回值:
    • 成功:返回0
    • 失敗:返回非0

5、MySQL錯誤處理(mysql_errno、mysql_error)

  • 由於每次調用庫都會更新錯誤碼,因此你只能獲得最後一個執行命令的錯誤碼。 可是這兩個函數是例外,它們不會致使錯誤碼的更新

返回錯誤碼(mysql_errno)

  1.  
    #include <mysql.h>
  2.  
    #include <mysqld_error.h>
  3.  
    #include <errmsg.h>
  4.  
     
  5.  
    unsigned int mysql_errno(MYSQL *mysql);
  • 功能:傳遞MySQL句柄,經過返回值返回錯誤碼,錯誤碼一般是非0值
  • 返回值:
    • 若是有錯誤:錯誤碼經過返回值返回,錯誤碼一般是非0值
    • 若是沒有錯誤:返回值爲0
  • 錯誤碼定義頭文件文件:
    • /usr/include/mysql/mysqld_error.h:主要定義服務端的錯誤編碼
    • /usr/include/mysql/errmsg.h:主要定義客戶端錯誤

返回錯誤字符串 (mysql_error)

  1.  
    #include <mysql.h>
  2.  
    #include <mysqld_error.h>
  3.  
    #include <errmsg.h>
  4.  
     
  5.  
    const char * mysql_error(MYSQL *mysql);
  •  功能:傳遞MySQL句柄,返回錯誤的文本形式

演示案例

  1.  
    //connect2.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    int main(int argc,char *argv[])
  10.  
    {
  11.  
    MYSQL mysql;
  12.  
    mysql_init(&mysql);
  13.  
     
  14.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  15.  
    "I do not know", "demo", 0, NULL, 0)){
  16.  
    printf( "Connect success\n");
  17.  
    mysql_close(&mysql);
  18.  
    } else{
  19.  
    fprintf( stderr, "Connect failed:\n");
  20.  
    if(mysql_errno(&mysql)){
  21.  
    printf( "\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql));
  22.  
    }
  23.  
    }
  24.  
     
  25.  
    return 0;
  26.  
    }
  • 編譯程序:
gcc -I/usr/include/mysql connect2.c -L/usr/lib/mysql -lmysqlclient -o connect2
 
  • 運行程序:由於上面咱們鏈接數據庫的用戶密碼錯誤了,因此打印1045錯誤編碼 ,而且打印了錯誤字符串

6、執行SQL語句(mysql_query、mysql_real_query)

  • 在編寫C語言程序時,SQL語句儘可能不要換行。若是SQL語句過長,不得不換行,能夠在行尾使用\字符以容許SQL 語句繼續到下一行

mysql_query

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    int mysql_query(MYSQL *mysql, const char *q);
  • 功能:用來執行一條SQL語句(增、刪、改、查)
  • 參數:
    • mysql:MySQL句柄
    • q:執行的SQL語句文本字符串,不須要分號
  • 返回值:
    • 執行成功:返回0
    • 執行失敗:返回非0

mysql_real_query

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    int mysql_real_query(MYSQL *mysql, const char *q,unsigned long length);
  • 功能:該函數用來查詢二進制數據
  • 與mysql_query的區別:mysql_query不能用於二進制數據,該函數能夠用於二進制數據查詢
  • 返回值:
    • 查詢成功返回0
    • 查詢成功返回非0

演示案例

  • 由於涉及到博主的數據庫的密碼,下面咱們的mysql_real_connect函數的數據庫密碼使用「xxx」表示(出於隱私)
  1.  
    //insert1.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    int main(int argc,char *argv[])
  10.  
    {
  11.  
    MYSQL mysql;
  12.  
    mysql_init(&mysql);
  13.  
     
  14.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  15.  
    "xxx", "demo", 0, NULL, 0)){
  16.  
    printf( "Connect success\n");
  17.  
     
  18.  
    //insert
  19.  
    if(mysql_query(&mysql, "insert into children(fname,age) values('Ann',18)")!= 0){
  20.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  21.  
    mysql_errno(&mysql),mysql_error(&mysql));
  22.  
    } else{
  23.  
    printf( "Insert success,affect row are %lu\n",
  24.  
    mysql_affected_rows(&mysql));
  25.  
    }
  26.  
     
  27.  
    mysql_close(&mysql);
  28.  
    } else{
  29.  
    fprintf( stderr, "Connect failed:\n");
  30.  
    if(mysql_errno(&mysql)){
  31.  
    printf( "\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql));
  32.  
    }
  33.  
    }
  34.  
     
  35.  
    return 0;
  36.  
    }
  • 進入數據庫,在demo數據中建立一張表(編號字段自動增長)

  • 編譯程序
gcc -I/usr/include/mysql insert1.c -L/usr/lib/mysql -lmysqlclient -o insert1
 
  • 運行程序

  • 查看數據庫的信息

7、檢查受影響的行數(mysql_affected_rows)

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    my_ulonglong mysql_affected_rows(MYSQL *mysql);
  • 功能:用於檢查受查詢影響的行數,這個函數返回受以前執行的UPDATE、INSERT或DELETE語句影響的行數
  • 返回值:
    • 返回0:沒有行受影響
    • 正數:受影響的行數
  • 返回值類型my_ulonglong:無符號類型,在prinf打印時,強制轉換爲unsigned long類型,而後使用%lu佔位符打印
  • where條件致使的影響行數:在執行一條SQL語句時,若是SQL語句帶有where條件,那麼雖然有與where匹配的行,當時若是原先的數據與要更改的數據相同,那麼這一行就不會被認爲更改過了。可是其餘數據庫語言僅僅由於記錄匹 配WHERE子句就把它視爲已經更新過(詳情見下面的演示案例①)

mysql_real_connect函數的CLIENT_FOUND_ROWS標誌

  • 在執行mysql_affected_rows函數時,MySQL會默認省略掉與where匹配可是數據同樣的行,從而不會再mysql_affected_rows函數的返回值中記錄這一行數。若是在使用mysql_real_connect函數時,將函數的最後一個參數置位CLIENT_FOUND_ROWS,那麼即便匹配的那一行數據不用更新也會被記錄(詳情見下面的演示案例①)

刪除數據時的一種特殊狀況

  • 在從數據庫中刪除數據的時候。若是使用WHERE子句刪除數據,那麼mysql_affected_rows將返回你指望的刪除的行數。但若是在DELETE語句中沒有WHERE子句(重點:必定要是沒有where的刪除語句),那麼表中的全部行都會被刪除,可是由程序返回的受影響行數卻爲0。 這是由於MySQL優化了刪除全部行的操做,它並非執行許多個單行刪除操做。這一行爲不會受CLIENT_FOUND_ROWS選項標誌的影響
  • 這種狀況是我在看書時書上說的,可是我在Ubuntu中作了實驗,這樣特殊狀況並不存在,因此究竟是怎麼一回事,仍是與實際操做有關

演示案例①

  • 由於涉及到博主的數據庫的密碼,下面咱們的mysql_real_connect函數的數據庫密碼使用「xxx」表示(出於隱私)
  1.  
    //update1.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    int main(int argc,char *argv[])
  10.  
    {
  11.  
    MYSQL mysql;
  12.  
    mysql_init(&mysql);
  13.  
     
  14.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  15.  
    "xxx", "demo", 0, NULL, 0)){
  16.  
    printf( "Connect success\n");
  17.  
     
  18.  
    //insert
  19.  
    if(mysql_query(&mysql, "update children set age=20 where fname='Ann'")!= 0){
  20.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  21.  
    mysql_errno(&mysql),mysql_error(&mysql));
  22.  
    } else{
  23.  
    printf( "Update success,affect row are %lu\n",
  24.  
    mysql_affected_rows(&mysql));
  25.  
    }
  26.  
     
  27.  
    mysql_close(&mysql);
  28.  
    } else{
  29.  
    fprintf( stderr, "Connect failed:\n");
  30.  
    if(mysql_errno(&mysql)){
  31.  
    printf( "\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql));
  32.  
    }
  33.  
    }
  34.  
     
  35.  
    return 0;
  36.  
    }
  • 運行程序前,查看數據庫,名爲Ann的行數,有2行age爲18,有2行age爲20

  • 編譯程序:
gcc -I/usr/include/mysql update1.c -L/usr/lib/mysql -lmysqlclient -o update1
 
  • 運行程序:
    • 能夠看到數據庫中名爲Ann的行有4行,可是更新以後受影響的行數只有2行,由於另外兩行雖然與SQL語句匹配,可是因爲其qge已經爲20了,就沒有更新,所以也就沒有被認爲被影響

  • 咱們複製上面的updae1.c程序,並新建一個update2.c程序,將mysql_real_connect函數的最後一個參數設置爲CLIENT_FOUND_ROWS

  • 更改數據庫,更改age字段爲原先的兩行18,兩行20

  • 編譯程序:
gcc -I/usr/include/mysql update2.c -L/usr/lib/mysql -lmysqlclient -o update2
 
  • 運行程序:
    • 能夠看到mysql_real_connect函數加上了CLIENT_FOUND_ROWS標誌以後,即便有的行數沒有更改數據,可是那2行被where條件匹配了,也就算在了影響的行數中

8、MySQL的last_insert_id函數

  • 注意:last_inser_id是MySQL數據庫中的一個函數,而不是C語言提供的API

last_inser_id函數應用的場景

  • 插入數據有一個微小但相當重要的方面。MySQL表中的數據類型爲AUTO_INCREMENT的字段,它由MySQL自動分配ID。這一特性很是有用,特別是當你有許多用戶的時候
  • 例如在上面咱們定義的children時,將childno字段類型設置爲AUTO_INCREMENT

  • 正如你看到的那樣,childno列被設爲AUTO_INCREMENT類型。這樣固然很好,可是一旦你插入一 行,你如何知道剛插入的孩子被分配了什麼數字呢?
  • 你能夠執行一條SELECT語句來搜索孩子的名字,但這樣效率會很低,而且若是有兩個相同名字的 孩子,這將不能保證惟一性。或者,若是同時有多個用戶快速地插入數據,那麼可能在更新操做和 SELECT語句之間會有其餘行被插入。由於發現一個AUTO_INCREMENT列的值是你們都面臨的一個共同 問題,因此MySQL以函數LAST_INSERT_ID()的形式提供了一個專門的解決方案
  • 不管什麼時候MySQL向AUTO_INCREMENT列中插入數據,MySQL都會基於每一個用戶對最後分配的值進 行跟蹤。用戶程序能夠經過SELECT專用函數LAST_INSERT_ID()來發現該值,這個函數的做用有點像 是表中的虛擬列

MySQL操做演示案例

  • 先查看錶中沒有任何數據

  • 此時插入一條語句,並查看last_insert_id函數的返回值

  • 此時再插入一條語句,並查看last_insert_id函數的返回值

  • 查看數據庫信息,能夠看到插入的childno的值與咱們的last_insert_id函數返回的值同樣

  • 實驗解析:每次插入一行,MySQL就分配一個新的id值而且跟蹤它,使得你能夠用LAST_INSERT_ID()來提取它

若是想經過實驗查看返回的數字在本次會話中確實是惟一的,那麼你能夠打開另外一個會話並插入 另外一行數據。而後在最初的會話中從新執行SELECT LAST_INSERT_ID();語句。你將看到數字並無 發生改變,這是由於該語句返回的數字是由當前會話插入的最後一個數字。可是,若是執行SELECT * FROM children,你將看到其餘會話確實已插入數據了mysql

  • 此時,咱們再新建一個新的會話窗口(與先前的不是同一個會話),而且使用LAST_INSERT_ID()返回插入的ID值,能夠看到爲15

  • 此時,咱們來到第一個會話窗口執行LAST_INSERT_ID()函數,發現其值仍是14。而後查詢表數據,能夠看到另外一個會話插入的數據已經插入了(由此能夠得出LAST_INSERT_ID()函數返回的數字在本次會話中是惟一的)

C語言演示案例

  • 由於涉及到博主的數據庫的密碼,下面咱們的mysql_real_connect函數的數據庫密碼使用「xxx」表示(出於隱私)
  1.  
    //insert2.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    int main(int argc,char *argv[])
  10.  
    {
  11.  
    MYSQL mysql;
  12.  
    MYSQL_RES *res_ptr;
  13.  
    MYSQL_ROW sqlrow;
  14.  
     
  15.  
    mysql_init(&mysql);
  16.  
     
  17.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  18.  
    "xxx", "demo", 0, NULL, 0)){
  19.  
    printf( "Connect success\n");
  20.  
     
  21.  
    //insert
  22.  
    if(mysql_query(&mysql, "insert into children(fname,age) values('Robert',7)")!= 0){
  23.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  24.  
    mysql_errno(&mysql),mysql_error(&mysql));
  25.  
    } else{
  26.  
    printf( "Insert %lu rows\n",( unsigned long)mysql_affected_rows(&mysql));
  27.  
    }
  28.  
     
  29.  
    //select last_insert_id()
  30.  
    if(mysql_query(&mysql, "select last_insert_id()")!= 0){
  31.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  32.  
    mysql_errno(&mysql),mysql_error(&mysql));
  33.  
    } else{
  34.  
    res_ptr=mysql_use_result(&mysql)
  35.  
    if(res_ptr){
  36.  
    while((sqlrow=mysql_fetch_row(res_ptr))){
  37.  
    printf( "We inserted childno %s\n",sqlrow[ 0]);
  38.  
    }
  39.  
    mysql_free_result(res_ptr);
  40.  
    }
  41.  
    }
  42.  
     
  43.  
    mysql_close(&mysql);
  44.  
    } else{
  45.  
    fprintf( stderr, "Connect failed:\n");
  46.  
    if(mysql_errno(&mysql)){
  47.  
    printf( "\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql));
  48.  
    }
  49.  
    }
  50.  
     
  51.  
    return 0;
  52.  
    }
  53.  
     
  • 編譯程序:
gcc -I/usr/include/mysql insert2.c -L/usr/lib/mysql -lmysqlclient -o insert2
 
  • 運行程序:在插入一行以後,你用LAST_INSERT_ID()函數來獲取分配的ID,就像常規的SELECT語句同樣。 而後使用mysql_use_result()從執行的SELECT語句中獲取數據並將它打印出來,咱們稍後將解釋此函數

9、MySQL查詢:一次提取全部數據(mysql_store_result)

在C應用程序中提取數據通常須要下面4個步驟:

  • 執行查詢(mysql_query)
  • 提取數據(mysql_store_result或mysql_use_result)
  • 處理數據(mysql_fetch_row)
  • 必要的清理工做(mysql_free_result)

MYSQL_RES、MYSQL_ROW、MYSQL_ROW_OFFSET數據類型

  • MYSQL_RES:表明從SELECT(或其餘返回數據的語句)中提取全部數據,是一個結果集結構
  • MYSQL_ROW:表明MYSQL_RES數據集中的一行,是一個行結構
    • 下標操做,由於MYSQL_ROW表明一行數據,咱們可使用索引的方式取出一行中的對應下標數據,例如MYSQL_ROW row,row[0]、row[1]、row[2]
  • MYSQL_ROW_OFFSET:表明當前程序在MYSQL_RES結果集中所操做的行索引

mysql_store_result:一次提取全部數據

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    MYSQL_RES *mysql_store_result(MYSQL *mysql);
  • 功能:mysql_store_result在一次調用中從SELECT(或其餘返回數據的語句)中提取全部數據
  • 參數:MySQL的鏈接句柄
  • 返回值:
    • 成功:保存查詢到的結果集結構指針
    • 失敗:NULL
  • 返回值的注意事項:提示沒有提取到數據,這個函數仍是返回一個MYSQL_RES指針,而不是NULL,NULL只有在函數出錯時返回。所以在編程時判斷該函數出錯的條件應該是該函數返回NULL
  • mysql_use_result是一次只從結果中提取一行數據,置於二者的區別,咱們在下面介紹mysql_use_result函數時會講解

mysql_num_rows:獲取返回的行數

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    my_ulonglong mysql_num_rows(MYSQL_RES *result);
  • 功能:函數參數爲mysql_store_result返回的結果集結構(MYSQL_RES),返回結果集中的行數
  • 返回值:
    • 正數:爲返回的行數。在prinf打印時,強制轉換爲unsigned long類型,而後使用%lu佔位符打印
    • 0:沒有返回行數
  • 若是mysql_store_ result調用成功,mysql_num_rows將始終都是成功的

mysql_fetch_row:提取一行

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
  • 功能:從mysql_store_result獲得的結果集結構(MYSQL_RES)中提取一行,並把它放到一個行結構中
  • 返回值:
    • 成功:返回一個行結構(MYSQL_ROW)
    • 數據用完或失敗:返回NULL
  • 這個函數每次只能從MYSQL_RES中提取一行,所以想要提取全部行,須要不斷的調用該函數

mysql_data_seek、mysql_row_tell、mysql_row_seek:行數索引操做

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    void mysql_data_seek(MYSQL_RES *result,my_ulonglong offset);
  4.  
     
  5.  
    MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *res);
  6.  
     
  7.  
    MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result,MYSQL_ROW_OFFSET offset);
  • mysql_data_seek:這個函數用來在結果集中進行跳轉,設置了offset以後,下一次mysql_fetch_row讀取的行就是這個函數所設置的行
    • 參數result:結果集
    • 參數offset:行號,必須在0到結果集總行數減1的範圍內。傳遞0會致使下一個mysql_fetch_row調用返回結果集中的第一行
  • mysql_row_tell:這個函數返回一個偏移值,它用來表示結果集中的當前位置。但返回值不是行號,而是一種MYSQL_ROW_OFFSET數據類型
  • mysql_row_seek:該函數用於將當前的位置移動到參數offset所指的位置(不是行號,是MYSQL_ROW_OFFSET類型),函數的返回值是該函數執行以前的位置(重點)

備註:sql

  • 不要把mysql_data_seek函數和mysql_row_tell、mysql_row_seek函數所操做的行數混淆,前者是整型,後二者是MYSQL_ROW_OFFSET數據類型

mysql_free_result:清除操做/善後處理

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    void mysql_free_result(MYSQL_RES *result);
  • 功能:完成了對結果集的操做後,你必須老是調用此函數來讓MySQL庫清理它分配的對象
  • 完成了對數據的全部操做後,你必須明確地調用mysql_free_result來讓MySQL庫完成善後處理

演示案例

  • 由於涉及到博主的數據庫的密碼,下面咱們的mysql_real_connect函數的數據庫密碼使用「xxx」表示(出於隱私)
  • 下面的程序只是簡單的獲取返回的行數有多少行,而沒有對返回的數據進行操做,對數據進行操做將在下面的演示案例中講解
  1.  
    //select1.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    int main(int argc,char *argv[])
  10.  
    {
  11.  
    MYSQL mysql;
  12.  
    MYSQL_RES *res;
  13.  
    MYSQL_ROW sqlrow;
  14.  
     
  15.  
    mysql_init(&mysql);
  16.  
     
  17.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  18.  
    "xxx", "demo", 0, NULL, 0)){
  19.  
    printf( "Connect success\n");
  20.  
     
  21.  
    //select
  22.  
    if(mysql_query(&mysql, "select childno,fname,age from children where age>5")!= 0){
  23.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  24.  
    mysql_errno(&mysql),mysql_error(&mysql));
  25.  
    } else{
  26.  
    //get result
  27.  
    res=mysql_store_result(&mysql);
  28.  
    if(res!= NULL){
  29.  
    //show rows
  30.  
    printf( "Retrived %lu rows:\n",( unsigned long)mysql_num_rows(res));
  31.  
    //Get one row at a time
  32.  
    while(sqlrow=mysql_fetch_row(res)){
  33.  
    printf( "\tFetch row...\n");
  34.  
    }
  35.  
    } else{
  36.  
    if(mysql_errno(&mysql)){
  37.  
    printf( "\terror code is %d\n\treason:%s\n",
  38.  
    mysql_errno(&mysql),mysql_error(&mysql));
  39.  
    }
  40.  
    }
  41.  
    }
  42.  
     
  43.  
    mysql_close(&mysql);
  44.  
    } else{
  45.  
    fprintf( stderr, "Connect failed:\n");
  46.  
    if(mysql_errno(&mysql)){
  47.  
    printf( "\terror code is %d\n\treason:%s\n",
  48.  
    mysql_errno(&mysql),mysql_error(&mysql));
  49.  
    }
  50.  
    }
  51.  
     
  52.  
    return 0;
  53.  
    }
  • 查看數據庫信息(能夠看到age>5的有3條記錄):

  • 編譯程序:
gcc -I/usr/include/mysql select1.c -L/usr/lib/mysql -lmysqlclient -o select1
 
  • 運行程序:

10、MySQL查詢:一次提取一行數據(mysql_use_result)

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    MYSQL_RES *mysql_use_result(MYSQL *mysql);
  • 功能:逐行提取數據
  • 返回值:
    • 正確:與mysql_store_result同樣,保存查詢到的結果集結構指針
    • 失敗:返回NULL

工做原理

  • 這個函數在執行以後,其返回值MYSQL_RES是空的,也就是說其未將提取的數據放到它初始化的結果集中
  • 接着,會在mysql_use_result函數以後調用mysql_fetch_row函數,並將mysql_use_result函數的返回值做爲mysql_fetch_row函數的參數,而後使用while循環執行mysql_fetch_row函數,mysql_fetch_row函數會每執行一次才從網絡中獲取一行數據
  • 所以,咱們所說的一次提取一行數據並非發生在mysql_use_result函數提取一行,而是在後面調用mysql_fetch_row函數時才正真的一次只提取一行(重點)
  • 因此,想要得到多少行數據,就調用多少次mysql_fetch_row函數,其調用次數與獲取的行數是1:1的關係。想要獲取全部數據,就是while循環不斷執行mysql_fetch_row函數

mysql_num_rows函數的注意事項

  • 在沒有調用mysql_fetch_row以前,mysql_num_rows老是返回0,所以此時程序尚未正確的獲取數據
  • 每當調用一次mysql_fetch_row,mysql_num_rows函數所獲取的行數就會加1,所以調用mysql_fetch_row以後,此時程序中就被認爲獲取了一行數據

與mysql_store_result到底有什麼區別:

  • 對於mysql_store_resul來講,其一次性直接獲取全部的查詢數據,而且查詢的數據時保存在本地的,使用這個函數能夠不用擔憂網絡或遠程數據庫錯誤帶來數據丟失的影響
  • 對於mysql_use_result來講,其備資源管理方面的實質性好處(更好地平衡了網絡負載,以及 減小了可能很是大的數據集帶來的存儲開銷),可是它不能與mysql_data_seek、mysql_row_seek或mysql_row_tell一塊兒使用,而且因爲直到全部數據都被提取後才能實際生效,mysql_num_rows的使用也受到限制(見下面演示案例)
  • 若是你碰巧使用的是一個特別龐大的數據集,那麼最好使用mysql_use_result函數提取小一些、更容易管理的信息塊,由於這將更快地將控制權返回給應用程序,而且不會佔用大量的網絡資源

演示案例

  • 由於涉及到博主的數據庫的密碼,下面咱們的mysql_real_connect函數的數據庫密碼使用「xxx」表示(出於隱私)
  1.  
    //select2.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    int main(int argc,char *argv[])
  10.  
    {
  11.  
    MYSQL mysql;
  12.  
    MYSQL_RES *res;
  13.  
    MYSQL_ROW sqlrow;
  14.  
     
  15.  
    mysql_init(&mysql);
  16.  
     
  17.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  18.  
    "xxx", "demo", 0, NULL, 0)){
  19.  
    printf( "Connect success\n");
  20.  
     
  21.  
    //select
  22.  
    if(mysql_query(&mysql, "select childno,fname,age from children where age>5")!= 0){
  23.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  24.  
    mysql_errno(&mysql),mysql_error(&mysql));
  25.  
    } else{
  26.  
    //get result
  27.  
    res=mysql_use_result(&mysql);
  28.  
    if(res!= NULL){
  29.  
    //Get one row at a time
  30.  
    while(sqlrow=mysql_fetch_row(res)){
  31.  
    printf( "\tFetch row...\n");
  32.  
    }
  33.  
    } else{
  34.  
    if(mysql_errno(&mysql)){
  35.  
    printf( "\terror code is %d\n\treason:%s\n",
  36.  
    mysql_errno(&mysql),mysql_error(&mysql));
  37.  
    }
  38.  
    }
  39.  
    }
  40.  
     
  41.  
    mysql_close(&mysql);
  42.  
    } else{
  43.  
    fprintf( stderr, "Connect failed:\n");
  44.  
    if(mysql_errno(&mysql)){
  45.  
    printf( "\terror code is %d\n\treason:%s\n",
  46.  
    mysql_errno(&mysql),mysql_error(&mysql));
  47.  
    }
  48.  
    }
  49.  
     
  50.  
    return 0;
  51.  
    }
  • 查看數據庫:

  • 編譯程序:
gcc -I/usr/include/mysql select2.c -L/usr/lib/mysql -lmysqlclient -o select2
 
  • 運行結果:由於mysql_num_rows放置到了mysql_fetch_row函數的前面,此時程序尚未正真的從數據庫中獲取數據,所以mysql_num_rows函數顯示爲0

  • 如今咱們更改程序:複製select2.c,新建select3.c程序將程序中的mysql_num_rows函數放置到mysql_fetch_row循環中

  • 運行select3.c結果:能夠看到每次調用mysql_fetch_row獲取一行數據以後,mysql_num_rows函數再去執行就能夠獲取到行數了

11、獲得返回的列(字段)數(mysql_field_count)

  • 在上面咱們介紹瞭如何提取行,下面學習如何處理返回的數據了。如同大多數SQL數據庫同樣,MySQL返回兩種類型的數據:
    • 列數據:從表中提取的信息
    • 元數據:關於數據的數據,例如列名和類型
  1.  
    #include <mysql.h>
  2.  
     
  3.  
    unsigned int mysql_field_count(MYSQL *mysql);
  • 功能:提供了一些關於查詢結果的基本信息,它接受鏈接對象,並返回結果集中的字段(列)數目
  • 參數:鏈接的mysql對象
  • 返回值:字段(列)數目

使用mysql_field_count判斷mysql_store_result的調用失敗緣由

  • mysql_field_count提供一個功能:判斷爲什麼mysql_ store_result的調用會失敗
  • 例如,若是mysql_store_result返回NULL,可是mysql_field_ count 返回一個正數,你能夠推測這是一個提取錯誤。可是,若是mysql_field_count返回0,則表示沒有 列能夠提取,這能夠解釋爲什麼存儲結果會失敗

演示案例

  • 由於涉及到博主的數據庫的密碼,下面咱們的mysql_real_connect函數的數據庫密碼使用「xxx」表示(出於隱私)
  1.  
    //insert4.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow);
  10.  
     
  11.  
    int main(int argc,char *argv[])
  12.  
    {
  13.  
    MYSQL mysql;
  14.  
    MYSQL_RES *res;
  15.  
    MYSQL_ROW sqlrow;
  16.  
     
  17.  
    mysql_init(&mysql);
  18.  
     
  19.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  20.  
    "xxx", "demo", 0, NULL, 0)){
  21.  
    printf( "Connect success\n");
  22.  
     
  23.  
    //select
  24.  
    if(mysql_query(&mysql, "select childno,fname,age from children where age>5")!= 0){
  25.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  26.  
    mysql_errno(&mysql),mysql_error(&mysql));
  27.  
    } else{
  28.  
    //get result
  29.  
    res=mysql_use_result(&mysql);
  30.  
    if(res!= NULL){
  31.  
    //Get one row at a time
  32.  
    while(sqlrow=mysql_fetch_row(res)){
  33.  
    printf( "Fetch row:\n");
  34.  
    display_show(&mysql,&sqlrow);
  35.  
    }
  36.  
    } else{
  37.  
    if(mysql_errno(&mysql)){
  38.  
    printf( "\terror code is %d\n\treason:%s\n",
  39.  
    mysql_errno(&mysql),mysql_error(&mysql));
  40.  
    }
  41.  
    }
  42.  
    }
  43.  
     
  44.  
    mysql_close(&mysql);
  45.  
    } else{
  46.  
    fprintf( stderr, "Connect failed:\n");
  47.  
    if(mysql_errno(&mysql)){
  48.  
    printf( "\terror code is %d\n\treason:%s\n",
  49.  
    mysql_errno(&mysql),mysql_error(&mysql));
  50.  
    }
  51.  
    }
  52.  
     
  53.  
    return 0;
  54.  
    }
  55.  
     
  56.  
    void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow)
  57.  
    {
  58.  
    unsigned int count= 0;
  59.  
    while(count<mysql_field_count(mysql)){
  60.  
    printf( "\t%s",(*sqlrow)[count]);
  61.  
    count++;
  62.  
    }
  63.  
    printf( "\n");
  64.  
    }
  • 查看數據庫:

  • 編譯程序:
gcc -I/usr/include/mysql select4.c -L/usr/lib/mysql -lmysqlclient -o select4
 
  • 運行結果:由於查詢的結果集中有3個字段,所以mysql_field_count函數返回3,在display_show函數中咱們用while循環遍歷,由於sqlrow表明一行數據,接着咱們用索引一次取出一行中的0、一、2下標的數據,也就是childno、fname、age

11、得到字段屬性(mysql_fetch_field)

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);
  • 上面咱們使用mysql_field_count函數雖然也能夠打印數據,可是輸出不美觀,而且每回都返回一行數據,並且查詢的結果中能夠能會出現NULL。若是想要打印出更整潔的格式化(或許是表格化)的數據,你須要同時獲得MySQL返回的數據和元數據。你可使用mysql_fetch_field函數來同時將元數據和數據提取到一個新的結構中
  • 參數:查詢返回的結果集
  • 返回值:
    • 成功:返回一個MYSQL_FIELD的結構
    • 數據讀取完或失敗:返回NULL

MYSQL_FIELD的結構

  • 一個字段就用一個MYSQL_FIELD結構體標識,該結構體能夠表示字段的數據類型、字段名、所屬表等信息
  1.  
    typedef struct st_mysql_field {
  2.  
    char *name; /* Name of column */
  3.  
    char *org_name; /* Original column name, if an alias */
  4.  
    char *table; /* Table of column if column was a field */
  5.  
    char *org_table; /* Org table name, if table was an alias */
  6.  
    char *db; /* Database for table */
  7.  
    char *catalog; /* Catalog for table */
  8.  
    char *def; /* Default value (set by mysql_list_fields) */
  9.  
    unsigned long length; /* Width of column (create length) */
  10.  
    unsigned long max_length; /* Max width for selected set */
  11.  
    unsigned int name_length;
  12.  
    unsigned int org_name_length;
  13.  
    unsigned int table_length;
  14.  
    unsigned int org_table_length;
  15.  
    unsigned int db_length;
  16.  
    unsigned int catalog_length;
  17.  
    unsigned int def_length;
  18.  
    unsigned int flags; /* Div flags */
  19.  
    unsigned int decimals; /* Number of decimals in field */
  20.  
    unsigned int charsetnr; /* Character set */
  21.  
    enum enum_field_types type; /* Type of field. See mysql_com.h for types */
  22.  
    void *extension;
  23.  
    } MYSQL_FIELD;

比較重要的成員數據庫

  • char *name:列名,爲字符串
  • char *table:列所屬的表名。當一個查詢要使用到多個表時,這將特別有用。注意: 對於結果中可計算的值如MAX,它所對應的表名將爲空字符串
  • char *def:若是調用mysql_list_fields(咱們未在這裏介紹它),它將包含該列 的默認值
  • enum enum_field_types type:列類型,至關普遍,列類型在頭文件mysql_con.h頭文件中,常見的幾種以下
    • FIELD_TYPE_DECIMAL
    • FIELD_TYPE_LONG
    • FIELD_TYPE_STRING
    • FIELD_TYPE_VAR_STRING
  • unsigned int length:列寬,在定義表時指定
  • unsigned int max_length:若是使用mysql_store_result,它將包含以字節爲單位的提取的最長列 值的長度。若是使用mysql_use_result,它將不會被設置
  • unsigned int flags:關於列定義的標誌,與獲得的數據無關。常見標誌的含義:NOT_NULL_FLAG、PRI_KEY_FLAG、UNSIGNED_FLAG、AUTO_INCREMENT_FLAG和BINARY_FLAG。完整列表可參見MySQL文檔。能夠在編程時利用flags成員與這些值進行「&」而後某一字段是否屬於某一標誌
  • unsigned int decimals:小數點後的數字個數。僅對數字字段有效

IS_NUM宏

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    #define IS_NUM(t) (((t) <= MYSQL_TYPE_INT24 && (t) != MYSQL_TYPE_TIMESTAMP) || (t) == MYSQL_TYPE_YEAR || (t) == MYSQL_TYPE_NEWDECIMAL)
  •  功能:當字段類型爲數字時,它返回true
  1.  
    //演示案例
  2.  
    if(IS_NUM(mysql_filed_prt->type))
  3.  
    printf( "Number type field\n");

mysql_field_seek函數

  1.  
    #include <mysql.h>
  2.  
     
  3.  
    MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,MYSQL_FIELD_OFFSET offset);
  • 設置當前的字段編號
  • 該編號會隨每次mysql_fetch_field調用而自動增長
  • 若是給參數offset傳遞值0,你將跳回第一列

演示案例

  • 由於涉及到博主的數據庫的密碼,下面咱們的mysql_real_connect函數的數據庫密碼使用「xxx」表示(出於隱私)
  1.  
    //select5.c
  2.  
     
  3.  
    #include <stdio.h>
  4.  
    #include <stdlib.h>
  5.  
    #include <mysql.h>
  6.  
    #include <mysqld_error.h>
  7.  
    #include <errmsg.h>
  8.  
     
  9.  
    void display_header(MYSQL_RES *result);
  10.  
    void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow);
  11.  
     
  12.  
    int main(int argc,char *argv[])
  13.  
    {
  14.  
    int isHeader;
  15.  
    MYSQL mysql;
  16.  
    MYSQL_RES *res;
  17.  
    MYSQL_ROW sqlrow;
  18.  
    isHeader= 1;
  19.  
     
  20.  
    mysql_init(&mysql);
  21.  
     
  22.  
    if(mysql_real_connect(&mysql, "localhost", "root",
  23.  
    "xxx", "demo", 0, NULL, 0)){
  24.  
    printf( "Connect success\n");
  25.  
     
  26.  
    //select
  27.  
    if(mysql_query(&mysql, "select childno,fname,age from children where age>5")!= 0){
  28.  
    fprintf( stderr, "mysql_query failed:\n\tcode:%d\n\treason:%s\n",
  29.  
    mysql_errno(&mysql),mysql_error(&mysql));
  30.  
    } else{
  31.  
    //get result
  32.  
    res=mysql_use_result(&mysql);
  33.  
    if(res!= NULL){
  34.  
    //Get one row at a time
  35.  
    while(sqlrow=mysql_fetch_row(res)){
  36.  
    if(isHeader){
  37.  
    display_header(res);
  38.  
    isHeader= 0;
  39.  
    printf( "Row Deails:\n");
  40.  
    }
  41.  
    display_show(&mysql,&sqlrow);
  42.  
    }
  43.  
    } else{
  44.  
    if(mysql_errno(&mysql)){
  45.  
    printf( "\terror code is %d\n\treason:%s\n",
  46.  
    mysql_errno(&mysql),mysql_error(&mysql));
  47.  
    }
  48.  
    }
  49.  
    }
  50.  
    mysql_close(&mysql);
  51.  
    } else{
  52.  
    fprintf( stderr, "Connect failed:\n");
  53.  
    if(mysql_errno(&mysql)){
  54.  
    printf( "\terror code is %d\n\treason:%s\n",
  55.  
    mysql_errno(&mysql),mysql_error(&mysql));
  56.  
    }
  57.  
    }
  58.  
     
  59.  
    return 0;
  60.  
    }
  61.  
     
  62.  
     
  63.  
    void display_header(MYSQL_RES *result)
  64.  
    {
  65.  
    printf( "Column Details:\n");
  66.  
    MYSQL_FIELD *field;
  67.  
    //循環,每次返回一個字段
  68.  
    while((field=mysql_fetch_field(result))!= NULL){
  69.  
    //1
  70.  
    printf( "\tcol_name:%s",field->name);
  71.  
    //2
  72.  
    if(IS_NUM(field->type)){
  73.  
    printf( " ,type:%s", "Number");
  74.  
    } else{
  75.  
    switch(field->type){
  76.  
    case FIELD_TYPE_DECIMAL:
  77.  
    printf( " ,type:%s", "Decimal");
  78.  
    break;
  79.  
    case FIELD_TYPE_LONG:
  80.  
    printf( " ,type:%s", "Long");
  81.  
    break;
  82.  
    case FIELD_TYPE_STRING:
  83.  
    printf( " ,type:%s", "String");
  84.  
    break;
  85.  
    case FIELD_TYPE_VAR_STRING:
  86.  
    printf( " ,type:%s", "Var_String");
  87.  
    break;
  88.  
    }
  89.  
    }
  90.  
    //3
  91.  
    printf( " ,column width:%ld",field->length);
  92.  
    //4
  93.  
    if(field->flags & AUTO_INCREMENT_FLAG){
  94.  
    printf(
  95.  
    " ,column flags:%s", "AUTO_INCREMENT_ FLAG");
  96.  
    }
  97.  
    printf( "\n");
  98.  
    }
  99.  
    }
  100.  
     
  101.  
    void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow)
  102.  
    {
  103.  
    unsigned int count= 0;
  104.  
    while(count<mysql_field_count(mysql)){
  105.  
    if((*sqlrow)[count]){
  106.  
    printf( "\t%s",(*sqlrow)[count]);
  107.  
    } else{
  108.  
    printf( "NULL");
  109.  
    }
  110.  
    count++;
  111.  
    }
  112.  
    printf( "\n");
  113.  
    }
  •  查看數據庫:

 

  • 編譯程序:
gcc -I/usr/include/mysql select5.c -L/usr/lib/mysql -lmysqlclient -o select5
 
  • 運行程序: 

12、更多的函數

  • 下表中顯示了其餘一些其餘經常使用的API函數
示例API調用 說 明
char *mysql_get_client_info(void); 返回客戶端使用的庫的版本信息
char *mysql_get_host_info(MySQL *connection); 返回服務器鏈接信息
char *mysql_get_server_info(MySQL *connection); 返回當前鏈接的服務器的信息
char *mysql_info(MySQL*connection); 返回最近執行的查詢的信息,可是僅僅只對一些查詢 類型有效——一般是INSERT和UPDATE語句,不然返回 NULL
int mysql_select_db(MySQL *connection, const char *dbname); 若是用戶擁有合適的權限,則把默認數據庫改成參數 指定的數據庫。成功時返回0
int mysql_shutdown(MySQL *connection,enum mysql_enum_shutdown_level); 若是用戶擁有合適的權限,則關閉鏈接的數據庫服務 器。目前關閉級別必須被設置爲SHUTDOWN_DEFAULT。成 功時返回0
相關文章
相關標籤/搜索