CodeIgniter 的 Query Builder ($this->db->where()) 提供了流暢、安全的 SQL 條件構建介面。本文系統整理其 6 大核心變體、參數綁定機制、原生 SQL 注入防護、子查詢整合與實戰模式,助你寫出既安全又高效的查詢邏輯。
| 方法 | SQL 片段 | 連接邏輯 | 典型用途 |
|---|---|---|---|
where($key, $val, $escape=TRUE) | WHERE key = 'val' | AND | 等值比對 |
or_where($key, $val) | OR key = 'val' | OR | 替代條件 |
where_in($key, $array) | WHERE key IN ('a','b') | AND | 列舉匹配 |
or_where_in($key, $array) | OR key IN ('a','b') | OR | 列舉替代 |
where_not_in($key, $array) | WHERE key NOT IN ('a','b') | AND | 排除列舉 |
or_where_not_in($key, $array) | OR key NOT IN ('a','b') | OR | 排除替代 |
where()// 簡單鍵值
$this->db->where('status', 1);
// WHERE `status` = 1
// 陣列批次 (全部 AND)
$this->db->where(['status' => 1, 'type' => 'vip']);
// WHERE `status` = 1 AND `type` = 'vip'
// 自訂運算子 (需第三參數 FALSE 關閉自動跳脫)
$this->db->where('age >', 18, FALSE);
// WHERE `age` > 18
// 原生 SQL 片段 (第三參數 FALSE,自行負責安全)
$this->db->where('MATCH(title) AGAINST("keyword")', NULL, FALSE);
// WHERE MATCH(title) AGAINST("keyword")
安全提醒:預設$escape=TRUE會自動對值進行$this->db->escape()(加引號、跳脫特殊字元)。傳入FALSE時,**必須確保值來自可信來源**,否則有 SQL 注入風險。
or_where()$this->db->where('role', 'admin');
$this->db->or_where('role', 'supervisor');
// WHERE `role` = 'admin' OR `role` = 'supervisor'
// 結合運算子
$this->db->where('created_at >', '2024-01-01');
$this->db->or_where('status', 'pending');
// WHERE `created_at` > '2024-01-01' OR `status` = 'pending'
$ids = [10, 20, 30];
// WHERE `id` IN (10,20,30)
$this->db->where_in('id', $ids);
// OR WHERE `id` IN (10,20,30)
$this->db->or_where_in('id', $ids);
// WHERE `id` NOT IN (10,20,30)
$this->db->where_not_in('id', $ids);
// OR WHERE `id` NOT IN (10,20,30)
$this->db->or_where_not_in('id', $ids);
// 空陣列保護:CI 3.1+ 空陣列產生 `WHERE 0=1` (永假) 或 `WHERE 1=1` (永真),避免 SQL 語法錯誤
$this->db->where_in('id', []); // WHERE 0=1
CI 不直接支援 where() 參數為 Query Builder 實例,需手動編譯子查詢 SQL:
// 子查詢:取得有訂單的用戶 ID
$subQuery = $this->db->select('user_id')->from('orders')->where('status', 'paid')->get_compiled_select();
// $subQuery = "SELECT `user_id` FROM `orders` WHERE `status` = 'paid'"
$this->db->where("id IN ($subQuery)", NULL, FALSE);
// WHERE `id` IN (SELECT `user_id` FROM `orders` WHERE `status` = 'paid')
// 或使用 EXISTS
$this->db->where("EXISTS (SELECT 1 FROM orders WHERE user_id = users.id AND status = 'paid')", NULL, FALSE);
function build_user_query(array $filters) {
$qb = $this->db->select('u.*, p.name as profile_name')
->from('users u')
->join('profiles p', 'p.user_id = u.id', 'left');
// 關鍵字搜尋 (多欄位 OR)
if (!empty($filters['keyword'])) {
$kw = $filters['keyword'];
$qb->group_start()
->like('u.username', $kw)
->or_like('u.email', $kw)
->or_like('p.name', $kw)
->group_end();
}
// 狀態過濾
if (isset($filters['status'])) {
$qb->where('u.status', $filters['status']);
}
// 角色多選 (IN)
if (!empty($filters['roles'])) {
$qb->where_in('u.role', $filters['roles']);
}
// 註冊時間區間
if (!empty($filters['date_from'])) {
$qb->where('u.created_at >=', $filters['date_from']);
}
if (!empty($filters['date_to'])) {
$qb->where('u.created_at <=', $filters['date_to']);
}
// 排序與分頁
$qb->order_by('u.created_at', 'DESC');
if (!empty($filters['limit'])) {
$qb->limit($filters['limit'], $filters['offset'] ?? 0);
}
return $qb->get()->result_array();
}
// 內部簡化邏輯 (system/database/DB_query_builder.php)
public function where($key, $value = NULL, $escape = TRUE) {
if (is_array($key)) {
foreach ($key as $k => $v) $this->where($k, $v, $escape);
return $this;
}
if ($escape === TRUE) {
$value = $this->escape($value); // 單引號包裹 + 內部單引號跳脫
}
$this->ar_where[] = [
'condition' => '=',
'key' => $this->protect_identifiers($key), // 反引號包裹欄位名
'value' => $value,
'type' => 'AND'
];
return $this;
}
CI 3 Query Builder 不直接支援具名參數綁定,但可透過 $this->db->query() 實現:
$sql = "SELECT * FROM users WHERE email = ? AND status = ?"; $results = $this->db->query($sql, [$email, 'active'])->result_array(); // 內部使用 PDO/MySQLi 預處理語句,完全免疫注入
| 錯誤寫法 | 問題 | 正確寫法 |
|---|---|---|
where('id', $id, FALSE) 且 $id 來自 $_GET | SQL 注入風險 | where('id', $id) (預設 escape) 或 query('... WHERE id = ?', [$id]) |
where("status = '$status'") | 字串拼接注入、語法錯誤 | where('status', $status) |
where_in('id', $ids) 未檢查空陣列 | CI 3.0 產生 IN () 語法錯誤 | CI 3.1+ 自動處理;舊版先 if ($ids) where_in(...) |
多個 where() 想要 OR 關係 | 預設全 AND | 使用 or_where() 或 group_start()/group_end() |
where('JSON_EXTRACT(data, "$.key")', 'val', FALSE) | MySQL JSON 函數需關閉 escape | 確保 key 為常數,val 用 where('JSON_EXTRACT(data, "$.key") =', $val, FALSE) |
where('indexed_col', $val)->where('non_indexed', $val2)where('DATE(created_at)', $date) → 改用 where('created_at >=', $date . ' 00:00:00')->where('created_at <', $date . ' 23:59:59')IN 列表過大影響優化器。$this->db->cache_on(); (僅限 SELECT,需配置快取路徑)。掌握 where() 六大變體、$escape 參數意義、group_start()/group_end() 巢狀分組、子查詢編譯整合、動態條件建構模式,即可應對 95% 的查詢條件場景。核心原則:**永遠預設啟用 escape,只有在確定安全且必要時才關閉;複雜邏輯用 Query Builder 組合,極簡場景用 query() 參數綁定。**