執(zhí)行查詢(xún)?
基礎(chǔ)查詢(xún)?
執(zhí)行常規(guī)查詢(xún)?
使用 query 方法提交一個(gè)查詢(xún):
$db->query('YOUR QUERY HERE');
執(zhí)行 “讀取” 類(lèi)型查詢(xún)時(shí),query() 方法返回一個(gè)數(shù)據(jù)庫(kù)查詢(xún)結(jié)果 對(duì)象 , 如何使用參考 顯示查詢(xún)結(jié)果 ;執(zhí)行 “寫(xiě)入” 類(lèi)查詢(xún)時(shí), 只返回 TRUE 或 FALSE ,表示執(zhí)行成功或失敗。檢索數(shù)據(jù)時(shí),你通常需要自行 編寫(xiě)查詢(xún)語(yǔ)句,例如:
$query = $db->query('YOUR QUERY HERE');
執(zhí)行簡(jiǎn)單查詢(xún)?
simpleQuery 方法是 $db->query() 的簡(jiǎn)化版本。它不會(huì)返回查詢(xún)結(jié)果, 不記錄查詢(xún)耗時(shí),不會(huì)綁定變量,也不保存查詢(xún)語(yǔ)句(用于調(diào)試)。它只是簡(jiǎn)單的 讓你執(zhí)行一個(gè)查詢(xún)語(yǔ)句,可能大多數(shù)用戶(hù)鮮有使用。
它只返回 “execute” 方法執(zhí)行的返回值,無(wú)關(guān)數(shù)據(jù)庫(kù)類(lèi)型。 典型的返回值是 TRUE/FALSE ,當(dāng)執(zhí)行類(lèi)型是寫(xiě)入型時(shí),它表示寫(xiě)操作的成功或失敗( 插入、刪除或修改,實(shí)際就如此使用);執(zhí)行讀取類(lèi)型時(shí),表示能否成功獲取查詢(xún)結(jié)果資源/對(duì)象。
if ($db->simpleQuery('YOUR QUERY'))
{
echo "Success!";
}
else
{
echo "Query failed!";
}
注解
PostgreSQL的 pg_exec()
方法 (舉例) 執(zhí)行成功時(shí)
總是返回一個(gè)資源值,即使執(zhí)行寫(xiě)入型查詢(xún)也是如此。
所以當(dāng)你判斷布爾值時(shí)要切記此點(diǎn)。
手動(dòng)指定數(shù)據(jù)表前綴?
當(dāng)你配置了數(shù)據(jù)庫(kù)的表前綴,執(zhí)行原生SQL查詢(xún)等類(lèi)似操作,應(yīng)當(dāng)給數(shù)據(jù)表增加前綴, 你可以使用如下操作:
$db->prefixTable('tablename'); // 輸出 prefix_tablename
出于某些原因,你想以編程的方式修改表前綴,且不想創(chuàng)建新數(shù)據(jù)庫(kù)連接時(shí),可以這樣做:
$db->setPrefix('newprefix');
$db->prefixTable('tablename'); // 輸出 newprefix_tablename
你可以用此方法隨時(shí)隨地獲取當(dāng)前的表前綴:
$DBPrefix = $db->getPrefix();
保護(hù)標(biāo)識(shí)符?
許多數(shù)據(jù)庫(kù)建議保護(hù)表名和字段名 - 比如 MySQL 使用反引號(hào)。 查詢(xún)構(gòu)造器會(huì)自動(dòng)保護(hù)它們, 但如果你需要手動(dòng)保護(hù)標(biāo)識(shí)符時(shí),可以這么做:
$db->protectIdentifiers('table_name');
重要
盡管查詢(xún)構(gòu)造器會(huì)盡可能且適當(dāng)?shù)囊媚闼璧淖侄蚊捅砻?但請(qǐng)注意它不適用于惡意用戶(hù)輸入,勿將其用于未處理的用戶(hù)數(shù)據(jù)。
當(dāng)你在數(shù)據(jù)庫(kù)配置文件里配有表前綴,這個(gè)方法還能給表加上前綴, 開(kāi)啟這個(gè)功能請(qǐng)?jiān)诘诙€(gè)參數(shù)填寫(xiě) TRUE(布爾值):
$db->protectIdentifiers('table_name', TRUE);
查詢(xún)轉(zhuǎn)義?
執(zhí)行數(shù)據(jù)庫(kù)查詢(xún)前做數(shù)據(jù)轉(zhuǎn)義是又好又安全的實(shí)踐,CodeIgniter 有三種方法幫到你:
$db->escape() 這個(gè)方法會(huì)判斷數(shù)據(jù)類(lèi)型,對(duì)字符串?dāng)?shù)據(jù)做轉(zhuǎn)義, 它也會(huì)自動(dòng)給數(shù)據(jù)加單引號(hào),你無(wú)需額外處理:
$sql = "INSERT INTO table (title) VALUES(".$db->escape($title).")";
$db->escapeString() 這個(gè)方法對(duì)傳入數(shù)據(jù)做強(qiáng)制轉(zhuǎn)義,且無(wú)關(guān)類(lèi)型, 多數(shù)時(shí)候你會(huì)用上面的方法而非這個(gè)。此方法使用舉例:
$sql = "INSERT INTO table (title) VALUES('".$db->escapeString($title)."')";
- $db->escapeLikeString() 這個(gè)方法用于 LIKE 條件字符串轉(zhuǎn)義,
以確保 LIKE 的通配符 (‘%’, ‘_’) 也能正確的轉(zhuǎn)義。
$search = '20% raise';
$sql = "SELECT id FROM table WHERE column LIKE '%" .
$db->escapeLikeString($search)."%' ESCAPE '!'";
重要
escapeLikeString()
方法使用 ‘!’ (感嘆號(hào))
轉(zhuǎn)義 LIKE 條件中的特殊字符,因?yàn)檫@個(gè)方法只轉(zhuǎn)義引號(hào)里的字符串,
它不能自動(dòng)添加 ESCAPE '!'
條件,因此你必須手動(dòng)添加。
查詢(xún)綁定?
綁定可以讓你用簡(jiǎn)單的查詢(xún)語(yǔ)法,讓系統(tǒng)將查詢(xún)語(yǔ)句合在一起,考慮下這個(gè)例子:
$sql = "SELECT * FROM some_table WHERE id = ? AND status = ? AND author = ?";
$db->query($sql, [3, 'live', 'Rick']);
查詢(xún)語(yǔ)句的問(wèn)號(hào)會(huì)被方法第二個(gè)參數(shù)的數(shù)組順次替換。
使用IN條件時(shí),綁定用多維數(shù)組搞定集合:
$sql = "SELECT * FROM some_table WHERE id IN ? AND status = ? AND author = ?";
$db->query($sql, [[3, 6], 'live', 'Rick']);
轉(zhuǎn)化后的語(yǔ)句是:
SELECT * FROM some_table WHERE id IN (3,6) AND status = 'live' AND author = 'Rick'
使用綁定的第二個(gè)好處是,它會(huì)自動(dòng)轉(zhuǎn)義輸入值,生成安全的查詢(xún)語(yǔ)句。 你無(wú)需記住要手動(dòng)轉(zhuǎn)義數(shù)據(jù)這件事 - 引擎會(huì)自動(dòng)幫你完成。
錯(cuò)誤處理?
$db->error();
如果你需要獲取最近一次發(fā)生的數(shù)據(jù)庫(kù)報(bào)錯(cuò),error() 方法會(huì)返回一個(gè)數(shù)組, 包含錯(cuò)誤號(hào)和錯(cuò)誤信息,來(lái)看下用例:
if ( ! $db->simpleQuery('SELECT `example_field` FROM `example_table`'))
{
$error = $db->error(); // Has keys 'code' and 'message'
}
預(yù)編譯查詢(xún)?
大部分?jǐn)?shù)據(jù)庫(kù)引擎支持某種形式的預(yù)編譯語(yǔ)句,使你僅做一次預(yù)編譯,然后在新數(shù)據(jù)集上多次查詢(xún)。它消除了 SQL 注入的可能性,因?yàn)閿?shù)據(jù)是以另一種形式傳給數(shù)據(jù)庫(kù)而非查詢(xún)語(yǔ)句。 當(dāng)你需要多次執(zhí)行相同查詢(xún)時(shí),它也相當(dāng)快速。然而,若你想應(yīng)用于所有查詢(xún),這會(huì)極大影響性能,因?yàn)樗ǔRL問(wèn)數(shù)據(jù)庫(kù)兩次。 由于查詢(xún)構(gòu)造器和數(shù)據(jù)庫(kù)連接已經(jīng)處理了轉(zhuǎn)義數(shù)據(jù),所以,安全方面已經(jīng)為你解決了,但有時(shí)候,你也需要通過(guò)預(yù)編譯語(yǔ)句或預(yù)編譯查詢(xún)來(lái)優(yōu)化查詢(xún)。
編譯查詢(xún)語(yǔ)句?
使用 prepare()
方法可輕松完成編譯,它有一個(gè)參數(shù),是函數(shù)閉包,返回一個(gè)查詢(xún)對(duì)象。
查詢(xún)對(duì)象由任一 “最終” 類(lèi)型的查詢(xún)自動(dòng)生成,包括 insert , update , delete , replace 和 get 。使用查詢(xún)構(gòu)造器執(zhí)行查詢(xún)可以最輕松地處理此問(wèn)題。
查詢(xún)實(shí)際沒(méi)有執(zhí)行,傳入的值不重要也不會(huì)被處理,僅做占位使用。
這樣會(huì)返回一個(gè)預(yù)編譯查詢(xún)對(duì)象:
$pQuery = $db->prepare(function($db)
{
return $db->table('user')
->insert([
'name' => 'x',
'email' => 'y',
'country' => 'US'
]);
});
如果你不想使用查詢(xún)構(gòu)造器,你可以手動(dòng)創(chuàng)建查詢(xún)對(duì)象,用問(wèn)號(hào)做占位符:
use CodeIgniter\Database\Query;
$pQuery = $db->prepare(function($db)
{
$sql = "INSERT INTO user (name, email, country) VALUES (?, ?, ?)";
return (new Query($db))->setQuery($sql);
});
如果數(shù)據(jù)庫(kù)要求在預(yù)編譯階段提供選項(xiàng)數(shù)組,可以將數(shù)組放到第二個(gè)參數(shù):
use CodeIgniter\Database\Query;
$pQuery = $db->prepare(function($db)
{
$sql = "INSERT INTO user (name, email, country) VALUES (?, ?, ?)";
return (new Query($db))->setQuery($sql);
}, $options);
執(zhí)行預(yù)編譯查詢(xún)?
一旦你有了一個(gè)預(yù)編譯查詢(xún),你可以使用 execute()
方法真正的執(zhí)行查詢(xún)。
你可以傳遞多個(gè)你需要的查詢(xún)參數(shù),參數(shù)的個(gè)數(shù)必須與占位符個(gè)數(shù)相同,參數(shù)的順序也要與原始占位符保持一致:
// 編譯查詢(xún)語(yǔ)句
$pQuery = $db->prepare(function($db)
{
return $db->table('user')
->insert([
'name' => 'x',
'email' => 'y',
'country' => 'US'
]);
});
// 準(zhǔn)備數(shù)據(jù)
$name = 'John Doe';
$email = 'j.doe@example.com';
$country = 'US';
// 執(zhí)行查詢(xún)
$results = $pQuery->execute($name, $email, $country);
這會(huì)返回標(biāo)準(zhǔn)的 結(jié)果集.
其他方法?
除了上述兩個(gè)主要方法,預(yù)編譯查詢(xún)還有以下方法可用:
close()
雖然 PHP 在(自動(dòng))關(guān)閉所有打開(kāi)的查詢(xún)資源時(shí)做的非常好,但手動(dòng)關(guān)閉執(zhí)行完的預(yù)編譯查詢(xún)同樣也是好的主意:
$pQuery->close();
getQueryString()
返回預(yù)編譯查詢(xún)的字符串。
hasError()
返回布爾值 true/false ,表示調(diào)用最近一次是否有執(zhí)行錯(cuò)誤。
getErrorCode() getErrorMessage()
如果有報(bào)錯(cuò),可以用這兩個(gè)方法獲取錯(cuò)誤號(hào)和錯(cuò)誤信息。
使用查詢(xún)對(duì)象?
在內(nèi)部,所有查詢(xún)的處理和存儲(chǔ)都在 CodeIgniterDatabaseQuery 的實(shí)例中進(jìn)行。 這個(gè)類(lèi)負(fù)責(zé)綁定參數(shù)、也做預(yù)編譯查詢(xún)、還能保存查詢(xún)時(shí)的性能數(shù)據(jù)。
getLastQuery()
當(dāng)你需要獲取最近一次的查詢(xún)對(duì)象,請(qǐng)使用 getLastQuery() 方法:
$query = $db->getLastQuery();
echo (string)$query;
查詢(xún)類(lèi)?
每個(gè)查詢(xún)對(duì)象都保存了此次查詢(xún)的一些信息,它有部分被時(shí)間線功能使用, 但你也可以使用(譯者注:此處時(shí)間線指數(shù)據(jù)庫(kù)執(zhí)行SQL過(guò)程,記錄它們方便調(diào)試和優(yōu)化性能)。
getQuery()
返回各種編譯構(gòu)造之后的最終查詢(xún)語(yǔ)句,也就是發(fā)送到數(shù)據(jù)庫(kù)執(zhí)行的語(yǔ)句:
$sql = $query->getQuery();
將查詢(xún)對(duì)象做字符串轉(zhuǎn)換也能獲得相同的值:
$sql = (string)$query;
getOriginalQuery()
返回初始傳入對(duì)象里的 SQL 語(yǔ)句,沒(méi)有任何綁定或前綴修飾等等:
$sql = $query->getOriginalQuery();
hasError()
如果執(zhí)行時(shí)有任何錯(cuò)誤,這個(gè)方法將返回 true:
if ($query->hasError())
{
echo 'Code: '. $query->getErrorCode();
echo 'Error: '. $query->getErrorMessage();
}
isWriteType()
如果當(dāng)前查詢(xún)是寫(xiě)入型 (例如 INSERT, UPDATE, DELETE, 等),此方法返回 true:
if ($query->isWriteType())
{
... do something
}
swapPrefix()
替換最終執(zhí)行的 SQL 里的表前綴,第一個(gè)參數(shù)是原始你想替換的前綴, 第二個(gè)參數(shù)是替換之后你想要的前綴:
$sql = $query->swapPrefix('ci3_', 'ci4_');
getStartTime()
獲取查詢(xún)執(zhí)行時(shí)間,以秒為單位,精確到毫秒級(jí):
$microtime = $query->getStartTime();
getDuration()
返回執(zhí)行查詢(xún)的時(shí)長(zhǎng)(秒),浮點(diǎn)數(shù),精確到毫秒:
$microtime = $query->getDuration();