【原創】使用 mysql_use_result 仍是使用 mysql_store_result?


本文整理了關於「使用 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;
}
相關文章
相關標籤/搜索