使用C語言調用mysql資料庫編程實戰以及技巧,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)
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。