From: http://my.oschina.net/moooofly/blog/186456
本文整理了關於“使用 mysql_use_result 還是 mysql_store_result”的相關內容。
下面是網上找到的網友說法:
===============
博文一 :
在使用 mysql_query() 進行一次查詢后,一般要用這兩個函數之一來把結果存到一個 MYSQL_RES * 變量中。
兩者的主要區別是,mysql_use_result() 的結果必須“一次性用完”,也就是說用它得到一個 result 后,必須反復用 mysql_fetch_row() 讀取其結果直至該函數返回 null 為止,否則如果你再次進行 mysql 查詢,會得到 “Commands out of sync; you can't run this command now” 的錯誤。而 mysql_store_result() 得到 result 是存下來的,你無需把全部行結果讀完,就可以進行另外的查詢。比如你進行一個查詢,得到一系列記錄,再根據這些結果,用一個循環再進行數據庫查詢,就只能用 mysql_store_result() 。
博文二 :
對於成功檢索了數據的每個查詢(SELECT、SHOW、DESCRIBE、EXPLAIN、CHECK TABLE 等),必須調用 mysql_store_result() 或 mysql_use_result() 。對於其他查詢,不需要調用 mysql_store_result() 或 mysql_use_result() ,但是如果在任何情況下均調用了 mysql_store_result() ,它也不會導致任何傷害或性能降低。通過檢查 mysql_store_result() 是否返回 0(NULL) ,可檢測查詢是否沒有結果集(以后會更多)。
如果希望了解查詢是否應返回結果集,可使用 mysql_field_count() 進行檢查。mysql_store_result() 將查詢的全部結果讀取到客戶端,分配 1 個 MYSQL_RES 結構,並將結果置於該結構中。
如果查詢未返回結果集,mysql_store_result() 將返回 NULL 指針(例如,如果查詢是 INSERT 語句)。
如果讀取結果集失敗,mysql_store_result() 也會返回 NULL 指針。通過檢查 mysql_error() 是否返回非空字符串,mysql_errno() 是否返回非 0 值,或 mysql_field_count() 是否返回 0 ,可以檢查是否出現了錯誤。如果未返回行,將返回空的結果集。(空結果集設置不同於作為返回值的空指針)。一旦調用了 mysql_store_result() 並獲得了不是 NULL 指針的結果,可調用 mysql_num_rows() 來找出結果集中的行數。
可以調用 mysql_fetch_row() 來獲取結果集中的行,或調用 mysql_row_seek() 和 mysql_row_tell() 來獲取或設置結果集中的當前行位置。一旦完成了對結果集的操作,必須調用 mysql_free_result() 。
返回值
具有多個結果的 MYSQL_RES 結果集合。如果出現錯誤,返回 NULL 。
錯誤
如果成功,mysql_store_result() 將復位 mysql_error() 和 mysql_errno() 。
· CR_COMMANDS_OUT_OF_SYNC
以不恰當的順序執行了命令。
· CR_OUT_OF_MEMORY
內存溢出。
· CR_SERVER_GONE_ERROR
MySQL服務器不可用。
· CR_SERVER_LOST
在查詢過程中,與服務器的連接丟失。
· CR_UNKNOWN_ERROR
出現未知錯誤。
博文三 :
當調用時,mysql_store_result() 立即檢索所有的行,而 mysql_use_result() 啟動查詢,但實際上並未獲取任何行,mysql_store_result() 假設隨后會調用 mysql_fetch_row() 檢索記錄。這些行檢索的不同方法引起兩者在其他方面的不同。本節加以比較,以便了解如何選擇最適合應用程序的方法。
當 mysql_store_result() 從服務器上檢索結果集時,就提取了行,並為之分配內存,存儲到客戶機中,隨后調用 mysql_fetch_row() 就再也不會返回錯誤,因為它僅僅是把行脫離了已經保留結果集的數據結構。mysql_fetch_row() 返回 NULL 始終表示已經到達結果集的末端。相反,mysql_use_result() 本身不檢索任何行,而只是啟動一個逐行的檢索,就是說必須對每行調用 mysql_fetch_row() 來自己完成。既然如此,雖然正常情況下,mysql_fetch_row() 返回 NULL 仍然表示此時已到達結果集的末端,但也可能表示在與服務器通信時發生錯誤。可通過調用 mysql_errno() 和 mysql_error() 將兩者區分開來。
與 mysql_use_result() 相比,mysql_store_result() 有着較高的內存和處理需求,因為是在客戶機上維護整個結果集,所以內存分配和創建數據結構的耗費是非常巨大的,要冒着溢出內存的危險來檢索大型結果集,如果想一次檢索多個行,可用 mysql_use_result()。mysql_use_result() 有着較低的內存需求,因為只需給每次處理的單行分配足夠的空間。這樣速度就較快,因為不必為結果集建立復雜的數據結構。另一方面,mysql_use_result() 把較大的負載加到了服務器上,它必須保留結果集中的行,直到客戶機看起來適合檢索所有的行。
博文四 :
客戶端處理結果集的方式有兩種。一種方式是,通過調用 mysql_store_result(),一次性地檢索整個結果集。該函數能從服務器獲得查詢返回的所有行,並將它們保存在客戶端。第二種方式是針對客戶端的,通過調用 mysql_use_result(),對“按行”結果集檢索進行初始化處理。該函數能初始化檢索結果,但不能從服務器獲得任何實際行。
在這兩種情況下,均能通過調用 mysql_fetch_row() 訪問行。通過 mysql_store_result(),mysql_fetch_row() 能夠訪問以前從服務器獲得的行。通過mysql_use_result(),mysql_fetch_row() 能夠實際地檢索來自服務器的行。通過調用 mysql_fetch_lengths(),能獲得關於各行中數據大小的信息。
完成結果集操作后,請調用 mysql_free_result() 釋放結果集使用的內存。
這兩種檢索機制是互補的。客戶端程序應選擇最能滿足其要求的方法。實際上,客戶端最常使用的是 mysql_store_result() 。
mysql_store_result() 的 1 個優點在於,由於將行全部提取到了客戶端上,你不僅能連續訪問行,還能使用 mysql_data_seek() 或 mysql_row_seek()在結果集中向前或向后移動,以更改結果集內當前行的位置。通過調用 mysql_num_rows(),還能發現有多少行。另一方面,對於大的結果集,mysql_store_result() 所需的內存可能會很大,你很可能遇到內存溢出狀況。
mysql_use_result() 的 1 個優點在於,客戶端所需的用於結果集的內存較少,原因在於,一次它僅維護一行(由於分配開銷較低,mysql_use_result()能更快)。它的缺點在於,你必須快速處理每一行以避免妨礙服務器,你不能隨機訪問結果集中的行(只能連續訪問行),你不知道結果集中有多少行,直至全部檢索了它們為止。不僅如此,即使在檢索過程中你判定已找到所尋找的信息,也必須檢索所有的行。
下面是官網說法:
===============
【22.8.15. Common Questions and Problems When Using the C API】
【22.8.15.1. Why mysql_store_result() Sometimes Returns NULL After mysql_query() Returns Success】
成功調用 mysql_query() 后,調用 mysql_store_result() 卻返回 NULL 是可能的。當發生該情況時,有可能是因為下面情況的出現:
- 出現 malloc() 失敗的情況(例如,獲得的結果集太大)。
- 調用 mysql_store_result() 時,當前使用的 TCP 連接已意外斷開,導致無法讀取數據。
- 調用 mysql_store_result() 后確實應該無數據返回(例如調用的是 INSERT、UPDATE 或 DELETE)。
無論何時,你都可以通過調用 mysql_field_count() 來確定前一條執行的語句是否產生了非空結果集。如果 mysql_field_count() 返回的是 0 ,那么結果集必然是空的(返回 NULL ),最后調用的查詢必定為不會返回結果集的語句(例如 INSERT 或 DELETE )。如果 mysql_field_count() 返回非 0 值,那么最后一條查詢語句應該返回非空結果集。 (如果此時你獲得了空結果集)你可以通過調用 mysql_error() 或者 mysql_errno() 來查看錯誤信息。
【22.8.15.2. What Results You Can Get from a Query】
在調用查詢命令后除了可以獲得結果集外,還可以通過如下命令獲得更多信息:
- 調用 mysql_affected_rows() 會返回執行最后一條查詢語句(如 INSERT、UPDATE 或 DELETE )后所影響的 row 的數量。
- 快速重建表,可以使用 TRUNCATE TABLE 。
- 調用 mysql_num_rows() 會返回結果集中的 row 的數量。如果是使用 mysql_store_result() 來獲取結果集,那么只要在 mysql_store_result() 返回后,立刻就可以調用 mysql_num_rows() 來獲取 row 的數量。如果是使用 mysql_use_result() 來獲取結果集,那么必須在使用 mysql_fetch_row() 獲取到結果集的全部 row 后,才能調用 mysql_num_rows() 來后去 row 的數量。
- 調用 mysql_insert_id() 會返回向具有 AUTO_INCREMENT 索引的表插入 row 時所產生的最后一個 ID 。
- 有一些查詢(如 LOAD DATA INFILE ..., INSERT INTO ... SELECT ..., UPDATE)會返回額外的信息。其結果可以通過 mysql_info() 來獲取。
【Appendix C. Errors, Error Codes, and Common Problems】
【C.5.2. Common Errors When Using MySQL Programs】
【C.5.2.14. Commands out of sync】
如果你遇到了 “Commands out of sync” 錯誤,你的客戶端程序將不能夠在當下成功執行(有結果集返回的)新的查詢命令,因為你以錯誤的執行序列進行了調用。
例如,當你在調用 mysql_use_result() 后,在未調用 mysql_free_result() 之前,又執行了一個新的查詢操作就會出現上述問題。還有一種情況是,當你連續調用能夠獲得結果集的查詢命令,卻在其連續調用中間未調用 mysql_use_result() 或 mysql_store_result() 來獲取結果集的情況。
=============
有了上面的研究,已經很清楚兩者之前的差別了:
- 分別會消耗客戶端或服務器的內存;
- 執行后續操作的約束不同。
決定選擇的關鍵還是要看,客戶端程序是否關心結果集的內容。就 modb 而言,不太關心具體內容,只要知道成功還是失敗就可以了。所以我只需要確定調用 mysql_query() 是否返回成功就可以了。
=============
公司 dbi 庫中對 mysql_use_result() 的使用:
EOpCode CMySqlHandlerImp::GoOnProcRes(BOOL32 bIsClear/* = false */) { // 只要結果集不為 NULL 就釋放掉 if(NULL != m_pRes) { mysql_free_result(m_pRes); // clear preceding query result m_pRes = NULL; } ... // 在 bIsClear 為 True 的情況下,此處相當於將前次請求的全部結果集清除掉 while(TRUE) { ... // 讀取下一個查詢結果 nRet = mysql_next_result(m_pMySql); ... // 啟動一個逐行的檢索 m_pRes = mysql_use_result(m_pMySql); // 這里使用的 mysql_use_result ... if(!bIsClear) { // 綁定結果集到自定義的保存結果集的容器中 m_pcMySqlRsImp->BindRs(m_pMySql, m_pRes, m_pcCbFunc, m_pcContext); return EOpCode_FindRs; } mysql_free_result(m_pRes); m_pRes = NULL; } } BOOL32 CMySqlRsImp::BindRs(MYSQL *pMySql, MYSQL_RES *pRes, DBCbFunc pcCbFunc, void *pcContext) { m_pMySql = pMySql; m_pRes = pRes; m_pcCbFunc = pcCbFunc; m_pcContext = pcContext; m_wColNum = (u16)mysql_field_count(pMySql); m_pField = mysql_fetch_fields(m_pRes); // 這和 mysql_use_result 一起使用可以么 m_bEnd = FALSE; //產生記錄集,修改結束標志位 return TRUE; } BOOL32 CMySqlHandlerImp::ExecSql(IN LPCSTR szsql, OUT CDBRsImp *pcRecordSet, u16* pwErrId/* = NULL*/) { ... // 清除前次請求的全部結果集 GoOnProcRes(TRUE); // 執行新的查詢操作 s32 nRet = mysql_real_query(m_pMySql, szsql, strlen(szsql)+1); // 將外部指定的 buffer 進行綁定 m_pcMySqlRsImp = (CMySqlRsImp *)pcRecordSet; // 獲取此次請求的結果集 if(GoOnProcRes() == EOpCode_FindRs) { u32 dwEndTime = OspTickGet(); if (NULL != pwErrId) { *pwErrId = DB_EXEC_ERRID_SUCC; } return TRUE; } return FALSE; }