使用C語言調用mysql數據庫編程實戰以及技巧


今天編寫使用C語言調用mysql數據庫編程實戰以及技巧。為其它IT同行作為參考,當然有錯誤能夠留言,共同學習。

一、mysql數據庫的C語言經常使用接口API
1.首先當然是鏈接數據庫mysql_real_connect,原型例如以下:
MYSQL * STDCALL mysql_real_connect(
MYSQL *mysql, const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned long clientflag);
第一個參數mysql是C語言api中一個很重要的變量。里面內存很豐富。有port,dbname,charset等連接基本參數。它也包括了一個叫 st_mysql_methods的結構體變量,該變量里面保存着許多函數指針。這些函數指針將會在數據庫連接成功以后的各種數據操作中被調用。

mysql_real_connect函數中各參數,基本都是顧名思意。

2.連接數據庫成功之后就能夠使用mysql_query運行sql語句。原型例如以下:
int STDCALL mysql_query(MYSQL *mysql, const char *q);
第一個參數上面已經介紹過。第二個參數為要運行的sql語句,主要就是運行SQL語句的增、刪、改、查等功能。


這個函數整體就兩步:
(1)發送sql語句,事實上就一個socket發送sql 語句,加上mysql固定的協議頭。
(2)然后就是接受結果,這里將會調用MYSQL變量中的st_mysql_methods中的read_query_result函數指針

a 假設包括二進制數據的查詢,要使用mysql_real_query.

b 檢查受查詢影響的行數:
my_ulonglong mysql_affected_rows(MYSQL *connection);
my_ulonglong是無符號長整形,為%lu格式
這個函數返回受之前運行update,insert或delete查詢影響的行數。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "/usr/local/mysql/include/mysql.h"

static MYSQL mysql, *sock;
char sql[1024];
MYSQL_RES *res=NULL;
MYSQL_ROW row ;
int num_fields;
int num_rows = 0;

int main(){
    memset(sql, 0x00, sizeof(sql));
    mysql_init(&mysql);   

    if(!(sock = mysql_real_connect(&mysql, \
        (char *)"localhost", (char *)"ebipcs", \
        (char *)"Dcep2vUnAX", (char *)"ebipcs",\
         0, NULL, 0))){
        printf( "Couldn't connect to DB!\n\n%s\n\n", mysql_error(&mysql));
        return 0 ;
    }

    if(sock){
        printf( "SUCCESS\n" );
    }else{
        printf( "FAIL\n" );
        return 0;
    }
    sprintf(sql,"update cisaddressinfo set cisaddressinfo.addressseqno = '2' where cisaddressinfo.customid='199999900000000000015';");
    if(mysql_query(sock, sql)) {
         printf("mysql_query[%d] [%s]!\n", mysql_errno(sock), mysql_error(sock));
         return -1;
     }
    if( !(mysql_affected_rows( sock )) ){
        printf("update OK\n");
    }else{
        printf("update Fail\n");
    }
    if(mysql_errno(sock)){
        printf("mysql_affected_rows[%d] [%s]!\n", mysql_errno(sock), mysql_error(sock));
    }
    return 0;
}

3.存儲運行結果
SQL最常見的用法是提取數據而不是插入或更新數據。

數據是用select語句提取的
C應用程序提取數據一般須要4個步驟:
1、運行查詢
2、提取數據
3、處理數據
4、必要的清理工作
就像之前的insert和update一樣,使用mysql_query來發送SQL語句,然后使用mysql_store_result或mysql_use_result來提取數據,詳細使用哪個語句取決於你想怎樣提取數據。接着,將使用一系列mysql_fetch_row來處理數據。最后。使用mysql_free_result釋放查詢占用的內存資源。

a 一次提取全部數據:mysql_store_result
假設mysql_query返回成功,那么我們就通過mysql_store_result函數來讀取結果。原型例如以下:
MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql);
該函數會調用MYSQL變量中的st_mysql_methods中的 read_rows函數指針來獲取查詢的結果。同一時候該函數會返回MYSQL_RES 這樣一個變量,該變量主要用於保存查詢的結果。同一時候該函數malloc了一片內存空間來存儲查詢過來的數據,所以我們一定要記的 free(result),不然是肯定會造成內存泄漏的。

運行完mysql_store_result以后,事實上數據都已經在MYSQL_RES 變量中了。
相關函數:
// 這是在成功調用mysql_query之后使用此函數,這個函數將立馬保存在客戶端中返回的全部數據。它返回一個指向結果集結構的指針,假設失敗返回NULL
MYSQL_RES *mysql_store_result(MYSQL *connection);
// 這個函數接受由mysql_store_result返回的結果結構集,並返回結構集中的行數
my_ulonglong mysql_num_rows(MYSQL_RES *result);
// 這個函數從使用mysql_store_result得到的結果結構中提取一行,並把它放到一個行結構中。當數據用完或錯誤發生時返回NULL.
MYSQL_ROW mysql_fetch_row(MYSQL_RES *resutl);
// 這個函數用來在結果集中跳轉。設置將會被下一個mysql_fetch_row操作返回的行。

參數offset是一個行號。它必須是在0~結果總行數-1的范圍內。

傳遞
// 0將會導致下一個mysql_fetch_row調用返回結果集中的第一行。


void mysql_data_seek(MYSQL_RES *result, my_ulonglong offset);
//返回一個偏移值,它用來表示結果集中的當前位置。它不是行號,不能把它用於mysql_data_seek
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result);
// 這將在結果集中移動當前的位置。並返回之前的位置
MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset);
// 完畢全部對數據的操作后。必須總是調用這個來善后處理
void mysql_free_result(MYSQL_RES *result);

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mysql.h"
#include "errmsg.h"
#include "mysqld_error.h"

MYSQL conn;
MYSQL_RES *res_ptr;
MYSQL_ROW sqlrow;

void connection(const char* host, const char* user, const char* password, const char* database) {
    mysql_init(&conn); // 注意取地址符&

    if (mysql_real_connect(&conn, host, user, password, database, 0, NULL, 0)) {
        printf("Connection success!\n");
    } else {
        fprintf(stderr, "Connection failed!\n");
        if (mysql_errno(&conn)) {
            fprintf(stderr, "Connection error %d: %s\n", mysql_errno(&conn), mysql_error(&conn));
        }
        exit(EXIT_FAILURE);
    }
}

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

    connection("localhost", "root", "shuang", "shuangde");
    int res = mysql_query(&conn, "SELECT * from student");
    if (res) {
        fprintf(stderr, "SELECT error: %s\n", mysql_error(&conn));
    } else {
        res_ptr = mysql_store_result(&conn);
        if (res_ptr) {
            printf("Retrieved %lu rows\n", (unsigned long)mysql_num_rows(res_ptr)); 
            while ((sqlrow = mysql_fetch_row(res_ptr))) {
                printf("Fetched data...\n") ;
            }
            if (mysql_errno(&conn)) {
                fprintf(stderr, "Retrive error: %s\n", mysql_error(&conn));
            }
            mysql_free_result(res_ptr);
        } 
    }
    mysql_close(&conn);
    exit(EXIT_SUCCESS);
}

b 一次提取一行數據:mysql_use_result
用法和mysql_store_result全然一樣。把上面代碼的mysql_store_result改為mysql_use_result就可以。
mysql_use_result具備資源管理方面的實質性優點,更好地平衡了網絡負載。以及降低了可能很大的數據帶來的存儲開銷,可是不能與mysql_data_seek、mysql_row_seek、mysql_row_tell、mysql_num_rows一起使用。假設數據比較少,用mysql_store_result更好。

處理返回的數據相關函數和定義:
// 返回結果集中的字段(列)數目
unsigned int mysql_field_count(MYSQL *connection);
// 將元數據和數據提取到一個新的結構中
MYSQL_FIELD *mysql_fetch_field(MYSQL *result);
// 這個函數用來覆蓋當前的字段編號。該編號會隨着每次mysql_fetch_field調用而自己主動添加。假設給offset傳遞0,那么將跳回第1列
MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL *result, MYSQL_FIELD_OFFSET offset);
// MYSQL_FIELD定義在sql.h中,是指向字段結構數據的指針,有關於列的信息。

有成員:
char *name; // 列名,為字符串
char *table; // 列所屬表名
char *def; // 假設調用mysql_list_fields。它將包括該列的默認值
enum enum_field_types type; // 列類型
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等
unsigned int decimals; // 小數點后的數字個數。
// 列類型相當廣泛,完整的列表見頭文件mysql_com.h,常見的有:
// FIELD_TYPE_DECIMAL
// FIELD_TYPE_LONG
// FIELD_TYPE_STRING
// FIELD_TYPE_VAR_STRING
//一個特別實用的提前定義宏: IS_NUM,當字段類型為數字時,返回true

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mysql.h"
#include "errmsg.h"
#include "mysqld_error.h"


MYSQL conn;
MYSQL_RES *res_ptr;
MYSQL_ROW sqlrow;

void connection(const char* host, const char* user, const char* password, const char* database) {
    mysql_init(&conn); // 注意取地址符&

    if (mysql_real_connect(&conn, host, user, password, database, 0, NULL, 0)) {
        printf("Connection success!\n");
    } else {
        fprintf(stderr, "Connection failed!\n");
        if (mysql_errno(&conn)) {
            fprintf(stderr, "Connection error %d: %s\n", mysql_errno(&conn), mysql_error(&conn));
        }
        exit(EXIT_FAILURE);
    }
}

void display_row() {
    unsigned int field_count = mysql_field_count(&conn);
    int i = 0;
    while (i < field_count) {
        if (sqlrow[i]) printf("%s ", sqlrow[i]);
        else printf("NULL");
        i++;
    }
    printf("\n");
}

void display_header() {
    MYSQL_FIELD *field_ptr;
    printf("Column details:\n");
    while ((field_ptr = mysql_fetch_field(res_ptr)) != NULL) {
        printf("\t Name: %s\n", field_ptr->name);   
        printf("\t Table: %s\n", field_ptr->table); 
        printf("\t Type: ");
        if (IS_NUM(field_ptr->type)) {
            printf("Numeric field\n");  
        } else {
            switch(field_ptr->type) {
                case FIELD_TYPE_VAR_STRING:
                    printf("VARCHAR\n");
                    break;
                case FIELD_TYPE_LONG:
                    printf("LONG");
                    break;
                default:
                    printf("Type is %d, check in msyql_com.h\n", field_ptr->type);
            }   
        }
        printf("\t Max width %ld\n", field_ptr->length);
        if (field_ptr->flags & AUTO_INCREMENT_FLAG)
            printf("\t Auto increments\n");
        printf("\n");
    }
}

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

    connection("localhost", "root", "shuang", "shuangde");
    int res = mysql_query(&conn, "SELECT * from student");
    if (res) {
        fprintf(stderr, "SELECT error: %s\n", mysql_error(&conn));
    } else {
        res_ptr = mysql_use_result(&conn);
        if (res_ptr) {
            int first = 1;
            while ((sqlrow = mysql_fetch_row(res_ptr))) {
                if (first) {
                    display_header();
                    first = 0;  
                }
                display_row();
            }
            if (mysql_errno(&conn)) {
                fprintf(stderr, "Retrive error: %s\n", mysql_error(&conn));
            }
            mysql_free_result(res_ptr);
        } 
    }
    mysql_close(&conn);
    exit(EXIT_SUCCESS);
}

使用技巧:
a 關於MYSQL MYSQL_RES 類型變量定義及free的問題。
使用例如以下方式,會更好一些。
定義: MYSQL_RES *res=NULL;
使用:res = mysql_store_result( m_sock )
釋放:if(res)
{mysql_free_result( res ); res=NULL}
依據以上規則。我們定義一個宏:
*#define MYSQLFREE(a) \
if( a != NULL )\
{ \
mysql_free_result( a );\
a=NULL;\
}
使用的時候:MYSQLFREE(res)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM