c語言操作mysql
執行 SQL 語句
現在,我們已經有了一個連線,並且知道如何處理錯誤,是時候討論使用我們的資料庫來作一些實際工作了。執行所有型別的 SQL 的主關鍵字是 mysql_query:
int mysql_query(MYSQL *connection, const char *query)
正如您所見,它非常簡單。它取一個指向連線結構的指標和包含要執行的 SQL 的文字字串;與命令列工具不同,將不使用結束分號。成功之後,返回 0。在需要包含二進位制資料的特殊情況下,可以使用相關的函式,mysql_real_query。雖然出於本章的目的,我們僅需要討論 mysql_query。
不返回資料的 SQL 語句
我們將先討論 UPDATE、DELETE 和 INSERT 語句。因為它們不返回資料,所以更易於使用。
這裡我們將介紹的另一個重要函式是檢查受影響的行數的函式:
my_ulonglong mysql_affected_rows(MYSQL *connection);
可能關於這一函式的最顯而易見的事就是其非同尋常的返回結果。由於可移植性原因,這是一個特殊的無符號型別。為了在 printf 中使用,建議將其強制轉換成使用 %lu 格式規範的無符號長整數。這個函式返回受以前的 UPDATE、INSERT 或 DELETE 查詢影響的行數,這些查詢是使用 mysql_query 執行的。
通常對於 mysql_ 函式,返回碼 0 表示沒有行受影響;正數表示實際結果,通常是受影響的行數。
如前所述,當使用 mysql_affected_rows 時可能出現未期望的結果。讓我們先討論受 INSERT 語句影響的行數,它將按預期進行操作。將下列程式碼新增到程式 connect2.c 中,並且稱其為 insert1.c:
#include <stdlib.h> #include <stdio.h> #include "mysql.h" int main(int argc, char *argv[]) { MYSQL my_connection; int res; mysql_init(&my_connection); if (mysql_real_connect(&my_connection, "localhost", "rick", "bar", "rick", 0, NULL, 0)) { printf("Connection success\n"); res = mysql_query(&my_connection, "INSERT INTO children(fname,age) VALUES('Ann',3)"); if (!res) { printf("Inserted %lu rows\n", (unsigned long)mysql_affected_rows(&my_connection)); } else { fprintf(stderr, "Insert error %d: s\n",mysql_errno(&my_connection), mysql_error(&my_connection)); } mysql_close(&my_connection); } else { fprintf(stderr, "Connection failed\n"); if (mysql_errno(&my_connection)) { fprintf(stderr, "Connection error %d: %s\n", mysql_errno(&my_connection),mysql_error(&my_connection)); } } return EXIT_SUCCESS; }
正如預期,插入的行數為 1。
現在,我們更改程式碼,所以 'insert' 部分被替換成:
mysql_errno(&my_connection), mysql_error(&my_connection)); } } res = mysql_query(&my_connection, "UPDATE children SET AGE = 4 WHERE fname = 'Ann'"); if (!res) { printf("Updated %lu rows\n", (unsigned long)mysql_affected_rows(&my_connection)); } else { fprintf(stderr, "Update error %d: %s\n", mysql_errno(&my_connection), mysql_error(&my_connection)); }
現在假設子表中有的資料,如下:
childno | fname | age |
---|---|---|
1
2 3 4 5 6 7 8 9 10 11 |
Jenny
Andrew Gavin Duncan Emma Alex Adrian Ann Ann Ann Ann |
14
10 4 2 0 11 5 3 4 3 4 |
如果我們執行 update1,希望報告的受影響行數為 4,但是實際上程式報告 2,因為它僅必須更改 2 行,雖然 WHERE 子句標識了 4 行。如果想讓 mysql_affected_rows 報告的結果為 4(這可能是熟悉其它資料庫的人所期望的),則需要記住將 CLIENT_FOUND_ROWS 標誌傳遞到 mysql_real_connect,在 update2.c 中的程式如下:
if (mysql_real_connect(&my_connection, "localhost", "rick", "bar", "rick", 0, NULL, CLIENT_FOUND_ROWS)) {
如果我們在資料庫中復位資料,然後執行帶有這種修改的程式,則它報告的行數為 4。
函式 mysql_affected_rows 還有最後一個奇怪之處,它發生在從資料庫中刪除資料時。如果使用 WHERE 子句,則 mysql_affected_rows 將按預期返回刪除行數。但是,如果沒有 WHERE 子句,則刪除所有行,報告受影響的行數卻為 0。這是因為由於效率原因優化刪除整個表。這種行為不受 CLIENT_FOUND_ROWS 選項標誌的影響。
返回資料的語句
現在是時候討論 SQL 的最普遍用法了,從資料庫檢索資料的 SELECT 語句。
MySQL 還支援返回結果的 SHOW、DESCRIBE 和 EXPLAIN SQL 語句,但是這裡不考慮它們。按慣例,手冊中包含這些語句的說明。
您將會從 PostgreSQL 章記起,可以從 PQexec 中的 SQL SELECT 語句檢索資料,這裡馬上獲取所有資料,或者使用遊標從資料庫中逐行檢索資料,以便搞定大資料。
由於完全相同的原因,MySQL 的檢索方法幾乎完全相同,雖然它實際上不用遊標的形式描述逐行檢索。但是,它提供了縮小這兩種方法間差異的 API,如果需要,它通常使兩種方法的互換更加容易。
通常,從 MySQL 資料庫中檢索資料有 4 個階段:
- 發出查詢
- 檢索資料
- 處理資料
- 執行所需的任何整理
象以前一樣,我們使用 mysql_query 發出查詢。資料檢索是使用 mysql_store_result 或 mysql_use_result 完成的,這取決於想如何檢索資料,隨後使用 mysql_fetch_row 呼叫序列來處理資料。最後,必須呼叫 mysql_free_result 以允許 MySQL 執行任何所需的整理。
全部立即資料檢索的函式
可以從 SELECT 語句(或其他返回資料的語句)中檢索完所有資料,在單一呼叫中,使用 mysql_store_result:
MYSQL_RES *mysql_store_result(MYSQL *connection);
必須在 mysql_query 檢索資料後才能呼叫這個函式,以在結果集中儲存該資料。這個函式從伺服器中檢索所有資料並立即將它儲存在客戶機中。它返回一個指向以前我們從未遇到過的結構(結果集結構)的指標。如果語句失敗,則返回 NULL。
使用等價的 PostgreSQL 時,應該知道返回 NULL 意味著已經發生了錯誤,並且這與未檢索到資料的情況不同。即使,返回值不是 NULL,也不意味著當前有資料要處理。
如果未返回 NULL,則可以呼叫 mysql_num_rows 並且檢索實際返回的行數,它當然可能是 0。
my_ulonglong mysql_num_rows(MYSQL_RES *result);
它從 mysql_store_result 取得返回的結果結構,並且在該結果集中返回行數,行數可能為 0。如果 mysql_store_result 成功,則 mysql_num_rows 也總是成功的。
這種 mysql_store_result 和 mysql_num_rows 的組合是檢索資料的一種簡便並且直接的方法。一旦 mysql_store_result 成功返回,則所有查詢資料都已經儲存在客戶機上並且我們知道可以從結果結構中檢索它,而不用擔心會發生資料庫或網路錯誤,因為對於程式所有資料都是本地的。還可以立即發現返回的行數,它可以使編碼更簡便。如前所述,它將所有結果立即地傳送回客戶機。對於大結果集,它可能耗費大量的伺服器、網路和客戶機資源。由於這些原因,使用更大的資料集時,最好僅檢索需要的資料。不久,我們將討論如何使用 mysql_use_result 函式來完成該操作。
一旦檢索了資料,則可以使用 mysql_fetch_row 來檢索它,並且使用 mysql_data_seek、mysql_row_seek、mysql_row_tell 操作結果集。在開始檢索資料階段之前,讓我們先討論一下這些函式。
mysql_fetch_rowmysql_fetch_row
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
這個函式採用從儲存結果中獲取的結果結構,並且從中檢索單一行,在行結構中返回分配給您的資料。當沒有更多資料或者發生錯誤時,返回 NULL。稍後,我們將回來處理這一行中的資料。
mysql_data_seek
void mysql_data_seek(MYSQL_RES *result, my_ulonglong offset);
這個函式允許您進入結果集,設定將由下一個獲取操作返回的行。offset 是行號,它必須在從 0 到結果集中的行數減 1 的範圍內。傳遞 0 將導致在下一次呼叫 mysql_fetch_row 時返回第一行。
my_sql_row_tell, mysql_row_seek
MYSQL_ROW_OFFEST mysql_row_tell(MYSQL_RES *result);
這個函式返回一個偏移值,它表示結果集中的當前位置。它不是行號,不能將它用於 mysql_data_seek。但是,可將它用於:
MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset);
它移動結果集中的當前位置,並返回以前的位置。
有時,這一對函式對於在結果集中的已知點之間跳轉很有用。請注意,不要將 row tell 和 row seek 使用的偏移值與 data_seek 使用的行號混淆。這些是不可交換的,結果將是您所希望看到的。
mysql_free_result
在迫不及待地使用這些新函式之前,我們需要知道的最後一個函式是 mysql_free_result。
void mysql_free_result(MYSQL_RES *result);
完成結果集時, 必須總是 呼叫這個函式,以允許 MySQL 庫整理分配給它的物件。
檢索資料
現在開始編寫從資料庫中檢索資料的第一個程式。我們將選擇所有年齡大於 5 的行的內容。不幸的是我們還不知道如何處理這個資料,所以我們能做的只有迴圈檢索它。這便是 select1.c:
#include <stdlib.h> #include <stdio.h> #include "mysql.h" MYSQL my_connection; MYSQL_RES *res_ptr; MYSQL_ROW sqlrow; int main(int argc, char *argv[]) { int res; mysql_init(&my_connection); if (mysql_real_connect(&my_connection, "localhost", "rick", "bar", "rick", 0, NULL, 0)) { printf("Connection success\n"); res = mysql_query(&my_connection, "SELECT childno, fname, age FROM children WHERE age > 5"); if (res) { printf("SELECT error: %s\n", mysql_error(&my_connection)); } else { res_ptr = mysql_store_result(&my_connection); if (res_ptr) { printf("Retrieved %luows\n",(unsignedlong)mysql_num_rows(res_ptr)); while ((sqlrow = mysql_fetch_row(res_ptr))) { printf("Fetched data...\n"); } if (mysql_errno(&my_connection)) { fprintf(stderr, "Retrive error: s\n",mysql_error(&my_connection)); } } mysql_free_result(res_ptr); } mysql_close(&my_connection); } else { fprintf(stderr, "Connection failed\n"); if (mysql_errno(&my_connection)) { fprintf(stderr, "Connection error %d: %s\n", mysql_errno(&my_connection),mysql_error(&my_connection)); } } return EXIT_SUCCESS; }
檢索結果集並迴圈通過已檢索的資料的重要部分都已突出顯示。
一次檢索一行資料
要按需要逐行檢索資料,而不是立即獲取全部資料並將它儲存在客戶機中,可以將 mysql_store_result 呼叫替換成 mysql_use_result:
MYSQL_RES *mysql_use_result(MYSQL *connection);
這個函式還取得一個連線物件並返回結果結合指標,或者出錯時返回 NULL 。與 mysql_store_result 相似,它返回指向結果集物件的指標;關鍵的不同點在於返回時,實際上沒有將任何資料檢索到結果集,只是初始化結果集以準備好檢索資料。