摘要:構造條件如果單單是執行這樣的語句,使用原生擴展就好了,使用查詢構造器就是殺雞用牛刀。這一篇,我們來講講如何使用查詢構造器進行條件查詢。
構造 where 條件
如果單單是執行 SELECT * FROM test_table; 這樣的語句,使用原生擴展就好了,使用查詢構造器就是殺雞用牛刀。當然,在實際的業務需求中,大部分的 SQL 都沒這么簡單,有各種條件查詢、分組、排序、連表等操作,尤其是條件查詢,占到了查詢業務的大多數。
這一篇,我們來講講如何使用查詢構造器進行條件查詢。
條件查詢的核心:參數綁定首先,我們回顧一下用 PDO 來寫條件查詢該怎么做:
1、構造語句、預編譯:
PDO 可以通過占位符綁定參數,占位符可以使用 :name 的形式或者 ? 的形式。
$pdoSt = $pdo->prepare("SELECT * FROM test_table WHERE username = :username AND age = :age;");
2、進行參數綁定,執行語句:
PDOStatement::bindParam() 和 PDOStatement::bindValue() 方法可以綁定一個 PHP 變量到指定的占位符。
$username = "test"; $age = 18; $pdoSt->bindValue(":username", $username, PDO::PARAM_STR); $pdoSt->bindValue(":age", $age, PDO::PARAM_INT); $pdoSt->execute();
由此我們得知,只要搞定了參數綁定,就可以構造一個簡單的 where 子句了。
占位符和綁定方法的選擇占位符選擇:
? 占位符必須按照順序去綁定,而 :name 占位符只要占位符和數據的映射關系確定,綁定的數據就不會出錯。所以我們選擇 :name 占位符。
綁定方法的選擇:
PDOStatement::bindValue() 方法把一個值綁定到一個參數。
PDOStatement::bindParam() 不同于 PDOStatement::bindValue(),綁定變量作為引用被傳入,并只在 PDOStatement::execute() 被調用的時候才取值。
這里我們選擇 PDOStatement::bindValue() 方法,因為參數綁定過程和 execute 執行過程可能被封裝到不同的方法中,我們需要簡單的傳值傳遞而不是引用傳遞。
為基類編寫參數綁定方法先回顧下基類現在執行 sql 的過程:在 get()、row() 這些取結果的方法中,先執行構造 sql 的方法,再執行 _execute() 方法執行 sql。那么也就是說,我們只要在這兩個方法中間進行參數的綁定即可。
當然,并非只有 where 子句需要參數綁定,having 子句、where in 子句等也涉及到參數的綁定。為了程序結構的靈活和清晰,我們在基類新加一個 _bind_params 屬性,鍵值數組類型,用來存儲占位符和其綁定數據的映射。這樣,我們只需:
構造 where (having 等) 子句字符串時生成占位符,并將占位符和其綁定數據存入 _bind_params 數組中。
等待構造 sql 完畢后執行參數綁定方法。
最后執行 _execute() 方法執行 sql。
talk is cheap, just show code:
基類添加 _bind_params 屬性,用于存儲占位符和其綁定數據的映射:
protected $_bind_params = [];
添加參數綁定方法:
protected function _bindParams() { if(is_array($this->_bind_params)) { // 將占位符綁定數據數組迭代綁定 foreach ($this->_bind_params as $plh => $param) { // 默認為字符串類型 $data_type = PDO::PARAM_STR; // 如果綁定數據為數字 if(is_numeric($param)) { $data_type = PDO::PARAM_INT; } // 如果綁定數據為 null if(is_null($param)) { $data_type = PDO::PARAM_NULL; } // 如果綁定數據為 Boolean if(is_bool($param)) { $data_type = PDO::PARAM_BOOL; } // 執行綁定 $this->_pdoSt->bindValue($plh, $param, $data_type); } } }
修改 _execute() 方法:
protected function _execute() { try { $this->_pdoSt = $this->_pdo->prepare($this->_prepare_sql); // 進行參數綁定 $this->_bindParams(); $this->_pdoSt->execute(); $this->_reset(); } catch (PDOException $e) { if($this->_isTimeout($e)) { $this->_closeConnection(); $this->_connect(); try { $this->_pdoSt = $this->_pdo->prepare($this->_prepare_sql); // 進行參數綁定 $this->_bindParams(); $this->_pdoSt->execute(); $this->_reset(); } catch (PDOException $e) { throw $e; } } else { throw $e; } } }where 方法
現在我們開始開發條件查詢的主要對外方法 where()
where 方法應該包含如下的功能:
構造 where 子句字符串,支持鏈式訪問
生成占位符,保存占位符和綁定數據的映射
支持幾種常用的條件模式 (單條件、多條件、是否為 NULL、比較運算符的判斷)
1、構造 where 子句字符串
我們希望 where() 方法支持多種條件模式,如 where("name", "jack")、where("age", "<", "30")、where(["name" => "jack", "age" => 18])??梢杂^察得到,方法的參數是變動的,那么我們可以使用可變參數,用 func_num_args() 函數得到傳入參數的數量進行模式判斷,用 func_get_args() 函數得到傳入的參數,這樣就可以實現對多個模式的支持。(當然使用可變參數也是有缺點的,使用可變參數,接口 ConnectorInterface 中就無法限制該方法參數的個數和類型了,這里要根據個人需求取舍)
基類增加 where() 方法:
public function where() { // 多個條件的默認連接符為 AND,即與的關系 $operator = "AND"; // 在一次查詢構造過程中,是否是第一次調用此方法? // 在鏈式訪問中有效 if($this->_where_str == "") { // 第一次調用,where 子句需要 WHERE 關鍵字 $this->_where_str = " WHERE "; } else { // 非初次訪問,用連接符拼接上一個條件 $this->_where_str .= " ".$operator." "; } // 獲取參數數量和參數數組 $args_num = func_num_args(); $params = func_get_args(); // argurment mode switch ($args_num) { case 1: // 只有一個參數:傳入數組,多條件模式,如 a = b AND c = d ... 默認 AND 連接 ... break; case 2: // 兩個參數:單條件模式 ... break; case 3: // 三個參數:比較運算符判斷模式 ... break; } // 實現鏈式操作,返回當前實例 return $this; }
2、生成占位符,保存占位符和綁定數據的映射
對于 :name 形式的占位符,只要保證占位符唯一即可。但是如何保證其唯一性呢?占位符不光是在 where 子句中出現,還在 where in 、where between 這些需要參數綁定的子句中出現。那么按照功能和綁定數據拼接字符串來生成嗎?但是問題又來了,對于 where,有 where 和 or where 子句,where in 有 where in、where not in、or where in、or where not in 等組合,多個要綁定的參數也可能擁有相同的值,用功能加綁定數據拼接字符串來生成占位符很復雜,而且因為和方法、參數本身的依賴度高,沒法獨立出來,程序的可維護性也不行。
當然使用 ? 占位符不用考慮那么多 (知名框架 laravel 的查詢構造器就是這么做的,然而源碼太多,原諒我沒時間看完),但是使用 ? 占位符對參數綁定的順序有很大的要求。對于目前我的程序結構來說,_bindParams() 方法只是一股腦的迭代綁定參數,并不能分清楚各個參數的順序,容易導致綁錯數據的狀況。
那么,就說說我最后決定的做法:使用唯一 ID 生成。
首先將生成占位符的過程獨立出來,作為一個獨立方法,這樣即使以后有了更好的方案,也不用更改其他程序。
使用 PHP 的 uniqid() 函數生成一個唯一字符串,加前綴和熵值 (提高唯一性),用 MD5 簽一下名 (生成 :name 占位符可接受的字符)。
代碼如下:
// 生成占位符的方法 // 考慮此方法和類實例本身無關,所以寫為 static 方法提高效率 protected static function _getPlh() // get placeholder { return ":".md5(uniqid(mt_rand(), TRUE)); }
性能相關的思考:
Q:uniqid()、mt_rand()、md5() 這些函數的性能如何?會不會拖慢查詢構造器的速度?
A:這幾個函數性能不怎么樣,但是就像前言那一篇所說的,這些函數的使用的影響是否超過了系統性能的平衡點?對于此查詢構造器程序來講,并不是頻繁使用這些函數做密集運算,系統的瓶頸還是在和數據庫交互的網絡 IO 上,所以,這些函數是可以使用的。
注:我在一些測試機上測試過拼接字符串做占位符和隨機 ID 做占位符的壓測 AB 對比,并沒有什么性能差距。當然可能在一個處理速度超快的服務器、數據庫組合上能看到差距,如果各位有更好的方法歡迎提出,對我的方法的不足也歡迎指正。
Q:能保證生成的占位符是唯一的嗎?
A:如果在多線程的環境下,存在數據競爭,PHP 又沒有好用的線程庫進行數據加鎖,會出現重復的狀況。但是在其他環境下不會。傳統 web 環境下每次執行隨著一次 HTTP 請求結束而結束,解析 PHP 程序的 PHP-FPM、MOD_PHP 是多進程模型,處理請求時每個進程中的數據獨立,互不影響。而在 workerman 這個常駐內存的框架里,多任務也是一個任務開啟一個進程,數據相互獨立,每個進程中使用 epoll (linux)、select (windows) 來處理并發,并不會出現并行和數據競爭的狀況。所以說只要沒有多線程的需求,則占位符不會重復。
3、支持幾種方便的條件模式
OK,占位符的生成方式搞定,那么我們開始在 where() 方法中使用吧。
public function where() { // 多個條件的默認連接符為 AND,即與的關系 $operator = "AND"; // 在一次查詢構造過程中,是否是第一次調用此方法? // 在鏈式訪問中有效 if($this->_where_str == "") { // 第一次調用,where 子句需要 WHERE 關鍵字 $this->_where_str = " WHERE "; } else { // 非初次訪問,用連接符拼接上一個條件 $this->_where_str .= " ".$operator." "; } // 獲取參數數量和參數數組 $args_num = func_num_args(); $params = func_get_args(); // 判斷傳入的參數數量是否合法 if( ! $args_num || $args_num > 3) { throw new InvalidArgumentException("Error number of parameters"); } // argurment mode switch ($args_num) { // 只有一個參數:傳入數組,多條件模式,如 a = b AND c = d ... 默認 AND 連接 case 1: if( ! is_array($params[0])) { // 傳入非法參數,拋出異常提醒 throw new InvalidArgumentException($params[0]." should be Array"); } // 遍歷構造多條件 where 子句 $this->_where_str .= "("; foreach ($params[0] as $field => $value) { $plh = self::_getPlh(); // 生成占位符 $this->_where_str .= " ".$field." = ".$plh." AND"; // 將占位符添加到子句字符串中 $this->_bind_params[$plh] = $value; // 保存占位符和待綁定數據 } // 清除最后一個 AND 連接符 $this->_where_str = substr($this->_where_str, 0, strrpos($this->_where_str, "AND")); $this->_where_str .= ")"; break; // 兩個參數:單條件模式 case 2: if(is_null($params[1])) { // 如果數據為 null,則使用 IS NULL 語法 $this->_where_str .= " ".$params[0]." IS NULL "; } else { $plh = self::_getPlh(); // 生成占位符 $this->_where_str .= " ".$params[0]." = ".$plh." "; // 將占位符添加到子句字符串中 $this->_bind_params[$plh] = $params[1]; // 保存占位符和待綁定數據 } break; // 三個參數:比較運算符判斷模式 case 3: // 判斷使用的比較運算符是否合法 (各數據庫的運算符支持并不相同) if( ! in_array(strtolower($params[1]), $this->_operators)) { throw new InvalidArgumentException("Confusing Symbol ".$params[1]); } $plh = self::_getPlh(); // 生成占位符 $this->_where_str .= " ".$params[0]." ".$params[1]." ".$plh." "; // 將占位符添加到子句字符串中 $this->_bind_params[$plh] = $params[2]; // 保存占位符和待綁定數據 break; } // where 子句構造完畢 return $this; }
關于上述代碼這里有幾點要提一下:
在多條件模式下,需要判斷一下傳入的是不是一個數組 (過濾非法參數,方便開發),多個條件之間的連接符默認是 AND (大部分多個相等判斷條件之間都是以 AND 的形式連接的,如果有 OR 的需求請用鏈式訪問的多個 orWhere() 方法)。
單條件模式下需要判斷綁定數據是否為 null,如果為 null,則使用 IS NULL 的語法 (SQL 中不能用 < > = 判斷 null )。
比較運算符判斷模式下,首先要判斷一下傳入的比較運算符是否合法,這里各個數據庫提供的比較運算符是有差異的,所以我們要多帶帶設置一個屬性 _operators 保存這些運算符,Mysql、PostgreSql、Sqlite 這些驅動類中進行重寫。
Mysql 驅動類中:
// Mysql 提供的比較運算符 protected $_operators = [ "=", "<", ">", "<=", ">=", "<>", "!=", "<=>", "like", "not like", "like binary", "rlike", "regexp", "not regexp", "&", "|", "^", "<<", ">>", ];
PostgreSql 驅動類中:
// PostgreSql 提供的比較運算符 protected $_operators = [ "=", "<", ">", "<=", ">=", "<>", "!=", "like", "not like", "ilike", "similar to", "not similar to", "&", "|", "#", "<<", ">>", ];
Sqlite 驅動類中:
// Sqlite 提供的比較運算符 protected $_operators = [ "=", "<", ">", "<=", ">=", "<>", "!=", "like", "not like", "ilike", "&", "|", "<<", ">>", ];測試
打開 test/test.php,修改代碼:
require_once dirname(dirname(__FILE__)) . "/vendor/autoload.php"; use DriversMysql; $config = [ "host" => "localhost", "port" => "3306", "user" => "username", "password" => "password", "dbname" => "database", "charset" => "utf8", "timezone" => "+8:00", "collection" => "utf8_general_ci", "strict" => false, ]; $driver = new Mysql($config); // 單條件模式測試 $results = $driver->table("test_table") ->select("*") ->where("username", "jack") ->get(); var_dump($results); // 鏈式訪問 + 比較運算符判斷模式測試 $results = $driver->table("test_table") ->select("*") ->where("username", "jack") ->where("age", "<", 30) ->get(); var_dump($results); // 多條件模式測試 $results = $driver->table("test_table") ->select("*") ->where([ "username" => "jack", "age" => 18, ]) ->get(); var_dump($results);
執行看看,數據是不是如你所想。
優化雖然完成了 where() 方法的編寫,但是我們發現 where() 方法中的代碼很臃腫,而且后續編寫 orWhere()、having() 這些都需要用到條件查詢的方法時,很多的代碼都是重復的。既然如此,那么就把這部分代碼提出來。
基類添加 _condition_constructor 方法:
// $args_num 為 where() 傳入參數的數量 // $params 為 where() 傳入的參數數組 // $construct_str 為要構造的子句的字符串,在 where() 方法中調用會傳入 $this->_where_str // 因為要改變該子句字符串,所以這里使用引用傳遞 protected function _condition_constructor($args_num, $params, &$construct_str) { if( ! $args_num || $args_num > 3) { throw new InvalidArgumentException("Error number of parameters"); } switch ($args_num) { case 1: if( ! is_array($params[0])) { throw new InvalidArgumentException($params[0]." should be Array"); } $construct_str .= "("; foreach ($params[0] as $field => $value) { $plh = self::_getPlh(); $construct_str .= " ".$field." = ".$plh." AND"; $this->_bind_params[$plh] = $value; } $construct_str = substr($construct_str, 0, strrpos($construct_str, "AND")); $construct_str .= ")"; break; case 2: if(is_null($params[1])) { $construct_str .= " ".$params[0]." IS NULL "; } else { $plh = self::_getPlh(); $construct_str .= " ".$params[0]." = ".$plh." "; $this->_bind_params[$plh] = $params[1]; } break; case 3: if( ! in_array(strtolower($params[1]), $this->_operators)) { throw new InvalidArgumentException("Confusing Symbol ".$params[1]); } $plh = self::_getPlh(); $construct_str .= " ".$params[0]." ".$params[1]." ".$plh." "; $this->_bind_params[$plh] = $params[2]; break; } }
修改后的 where() 方法:
public function where() { // 多個條件的默認連接符為 AND,即與的關系 $operator = "AND"; // 在一次查詢構造過程中,是否是第一次調用此方法? // 在鏈式訪問中有效 if($this->_where_str == "") { // 第一次調用,where 子句需要 WHERE 關鍵字 $this->_where_str = " WHERE "; } else { // 非初次訪問,用連接符拼接上一個條件 $this->_where_str .= " ".$operator." "; } // 進行占位符生成、參數綁定、生成子句字符串操作 $this->_condition_constructor(func_num_args(), func_get_args(), $this->_where_str); return $this; }
這樣我們就把可以通用的邏輯提出來了,趁熱打鐵,我們把 orWhere() 方法也添加到基類中。
對于 orWhere() 方法,和 where() 方法的區別只有鏈式操作進行多條件查詢時的連接符不同:
public function orWhere() { $operator = "OR"; if($this->_where_str == "") { $this->_where_str = " WHERE "; } else { $this->_where_str .= " ".$operator." "; } $this->_condition_constructor(func_num_args(), func_get_args(), $this->_where_str); return $this; }
構造語句 SELECT * FROM test_table WHERE username = "jack" OR username = "mike";:
$results = $driver->table("test_table") ->select("*") ->where("username", "jack") ->orWhere("username", "mike") ->get();關鍵字沖突
熟悉數據庫的朋友們應該知道,每種數據庫都有一些關鍵字,一部分是 SQL 語句的關鍵字,另一部分是數據庫自己的關鍵字。既然有關鍵字,那么就避免不了用戶鍵入的數據和關鍵字重名的問題,比如表名和關鍵字重名、字段名 (別名) 和關鍵字重名等。
那么如何解決關鍵字沖突呢?
當然,建表的時候盡量注意命名,不要和關鍵字沖突是一種方法,但是如果這個表的建立、修改權限不在你手中,你又要訪問這個表去拿數據的時候就沒招了,所以我們常常要對歷史遺留問題進行兼容處理。
各數據庫使用了類似轉義的做法。Mysql 使用反引號 ` 來包裹字符串避免數據庫將這個字符解析為關鍵字,PostgreSql 和 Sqlite 則是用雙引號 " 來做相應的工作。
而在使用查詢構造器的過程中,總不能每次由用戶手動來寫這個符號 (如 where("`count`", 12) ),這樣更換數據庫驅動的時候會影響到上層的代碼,可維護性差 (如 mysql 切到 pgsql,需要把所有 ` 改為 " )。所以,為可能出現關鍵字沖突的地方添加引號應該交給查詢構造器底層去做。
既然各個數據庫有差異,想必現在大家已經知道該怎么做了,基類添加屬性 _quote_symbol,Mysql 類中進行重寫。
Mysql 驅動類中添加:
// 因為次屬性不會改變,使用 static 關鍵字 protected static $_quote_symbol = "`";
PostgreSql 和 Sqlite 同理,這里不多帶帶演示了。
下面我們給基類添加 _quote() 方法,用于給字符串添加引號:
// static 方法 protected static function _quote($word) { return static::$_quote_symbol.$word.static::$_quote_symbol; }
有了這個方法,我們可以簡單的防止一個字符串關鍵字沖突了。但是在實際應用中還遠不夠。
首先,在書寫 SQL 時字段的表述有很多模式
別名:username as name (這里不對省略 as 的情況做處理,請不要省略 as 關鍵字)
點號:table_name.field
SQL 函數做為列:COUNT(field)
我們必須的對這些常用情形做處理,而不只是直接對這些字符串的兩邊加引號。
對字符串的匹配處理,那么我們首先想到的是正則表達式。至于正則的性能,還是參考前言所說的性能平衡點,這里每次請求用到正則的次數很少,并沒有突破數據庫連接和執行的網絡 IO 瓶頸,所以可以使用。
在基類添加 _wrapRow 方法,用來處理 SQL 字段的字符串:
// static 方法 protected static function _wrapRow($str) { // 匹配模式 $alias_pattern = "/([a-zA-Z0-9_.]+)s+(AS|as|As)s+([a-zA-Z0-9_]+)/"; $alias_replace = self::_quote("$1")." $2 ".self::_quote("$3"); $prefix_pattern = "/([a-zA-Z0-9_]+s*)(.)(s*[a-zA-Z0-9_]+)/"; $prefix_replace = self::_quote("$1")."$2".self::_quote("$3"); $func_pattern = "/[a-zA-Z0-9_]+([a-zA-Z0-9_,s`""*]*)/"; // alias mode 別名模式 if(preg_match($alias_pattern, $str, $alias_match)) { // 如果列是 aa.bb as cc 的模式 if(preg_match($prefix_pattern, $alias_match[1])) { $pre_rst = preg_replace($prefix_pattern, $prefix_replace, $alias_match[1]); $alias_replace = $pre_rst." $2 ".self::_quote("$3"); } // 如果列是 aa as bb 的模式 return preg_replace($alias_pattern, $alias_replace, $str); } // prefix mode 表.字段 模式 if(preg_match($prefix_pattern, $str)) { return preg_replace($prefix_pattern, $prefix_replace, $str); } // func mode 函數模式,什么都不做,交給用戶去處理 if(preg_match($func_pattern, $str)) { return $str; } // field mode 簡單的字段模式,直接加引號返回 return self::_quote($str); }
上訴代碼有幾點要說明:
別名模式是最復雜的,需要判斷是 aa as bb 模式還是 aa.bb as cc 模式,匹配替換后的結果是 `aa` as `bb` 、`aa`.`bb` as `cc` (這里以 mysql 為例)。
函數模式如 count(aa.cc)、max(count) 這種,函數的參數數量不定,模式多變不好匹配,交給用戶手動輸入原生字符串去處理,而且諸如此類的聚合函數的話,后面的篇幅會增加聚合函數的相關方法去獲得結果。
有了 _wrapRow() 方法,我們可以使關鍵字沖突的處理對上層應用完全透明。
修改 table() 方法:
public function table($table) { // 添加引號 $this->_table = self::_wrapRow($table); return $this; }
修改 select() 方法:
public function select() { $cols = func_get_args(); if( ! func_num_args() || in_array("*", $cols)) { $this->_cols_str = " * "; } else { $this->_cols_str = ""; foreach ($cols as $col) { // 添加引號 $this->_cols_str .= " ".self::_wrapRow($col).","; } $this->_cols_str = rtrim($this->_cols_str, ","); } return $this; }
修該用于條件構造的 _condition_constructor() 方法:
protected function _condition_constructor($args_num, $params, &$construct_str) { if( ! $args_num || $args_num > 3) { throw new InvalidArgumentException("Error number of parameters"); } switch ($args_num) { case 1: if( ! is_array($params[0])) { throw new InvalidArgumentException($params[0]." should be Array"); } $construct_str .= "("; foreach ($params[0] as $field => $value) { $plh = self::_getPlh(); // 添加引號 $construct_str .= " ".self::_wrapRow($field)." = ".$plh." AND"; $this->_bind_params[$plh] = $value; } $construct_str = substr($construct_str, 0, strrpos($construct_str, "AND")); $construct_str .= ")"; break; case 2: if(is_null($params[1])) { // 添加引號 $construct_str .= " ".self::_wrapRow($params[0])." IS NULL "; } else { $plh = self::_getPlh(); // 添加引號 $construct_str .= " ".self::_wrapRow($params[0])." = ".$plh." "; $this->_bind_params[$plh] = $params[1]; } break; case 3: if( ! in_array(strtolower($params[1]), $this->_operators)) { throw new InvalidArgumentException("Confusing Symbol ".$params[1]); } $plh = self::_getPlh(); // 添加引號 $construct_str .= " ".self::_wrapRow($params[0])." ".$params[1]." ".$plh." "; $this->_bind_params[$plh] = $params[2]; break; } }
現在我們給要查的數據表中添加一個名為 group 的字段,構造一下 SELECT * FROM test_table where group = "test"; 這個語句,看是否會報錯呢?
$results = $driver->table("test_table") ->select("*") ->where("group", "test") ->get();
Just do it!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30828.html
摘要:而在項目開發中,我們想要的是一個更好用的可維護的工具,此時,對代碼的封裝模塊化就顯得尤為重要,于是出現了兩種方案查詢構造器,對象關系映射。典型環境下按照一般的查詢構造器處理就行。 文章目錄 寫一個特殊的查詢構造器 - (前言) 寫一個特殊的查詢構造器 - (一、程序結構,基礎封裝) 寫一個特殊的查詢構造器 - (二、第一條語句) 寫一個特殊的查詢構造器 - (三、條件查詢) 寫一個特殊...
摘要:復雜的條件在的條件查詢中,不只有這些基本的子句,還有等復雜一些的子句。這篇我們就來講一下查詢構造器如何構造這些復雜的查詢語句。 復雜的條件 在 SQL 的條件查詢中,不只有 where、or where 這些基本的子句,還有 where in、where exists、where between 等復雜一些的子句。而且即使是 where 這種基礎的子句,也有多個條件的多種邏輯組合。這篇...
摘要:雖然現在這樣的情況已經很少,但是對于查詢構造器而言,還是要提供一個方便的方法來對表前綴進行設置,特別是當你沒有權限修改表名的時候。所以我們將表前綴作為一個配置參數傳入查詢構造器,在查詢構造器的底層進行自動前綴添加。 關聯查詢是關系型數據庫典型的查詢語句,根據兩個或多個表中的列之間的關系,從這些表中查詢數據。在 SQL 標準中使用 JOIN 和 ON 關鍵字來實現關聯查詢。 Join 子...
摘要:聚合函數在中,有一些用來統計匯總的函數,被稱作聚合函數,如等。方法其它方法如之類的編寫就不一一展示了,代碼請看聚合函數。如何獲取總數當然是使用上面講到的聚合函數來處理。 where 相關的子句構造完成后,我們繼續構造其它子句。這一篇我們進行聚合函數、分組、排序等子句的構造。 聚合函數 在 SQL 中,有一些用來統計、匯總的函數,被稱作聚合函數,如 SUM、COUNT、AVG 等。 使用...
摘要:注在常駐內存單例模式下,這種多次用一個類進行查詢的情形很常見。斷線重連對于典型環境而言,一次的查詢已經隨著的請求而結束,的垃圾回收功能會回收一次請求周期內的數據。但在常駐內存的環境下,尤其是單例模式下,數據庫驅動類可能一直在內存中不被銷毀。 構造、執行第一條語句 上一篇完成了代碼結構的搭建和 PDO 的基礎封裝,這一篇我們來講如何構造一個最基本的 SQL 語句,并執行得到結果。 que...
閱讀 2778·2021-11-19 11:30
閱讀 3064·2021-11-15 11:39
閱讀 1785·2021-08-03 14:03
閱讀 1993·2019-08-30 14:18
閱讀 2049·2019-08-30 11:16
閱讀 2160·2019-08-29 17:23
閱讀 2604·2019-08-28 18:06
閱讀 2539·2019-08-26 12:22