[TOC]
# 介紹
Yii提供了一個[yii\\db\\Query](http://www.yiichina.com/doc/api/2.0/yii-db-query)讓我們方便地進行數據查詢
這不是模型,而是一個查詢器,基本兼容各種主流關系型數據庫
示例:
~~~php
$user = (new \yii\db\Query())->select(['id', 'name'])->from('user')->one();
print_r($user);
~~~
和db->createCommand('select id,name from user limit 1')的區別就是:createCommand是直接寫一個SQL語句來創建命令的,但Query是根據參數和數據庫類型生成不同了最終SQL語句,所以用Query會提升項目的數據庫可遷移性,而且代碼看起來也比較好閱讀
下面是一些常用的查詢方式
# all 查詢指定表的所有記錄
~~~php
$query = new \yii\db\Query();
// SELECT * FROM user
$users = $query->from('user')->all();
print_r($users);
~~~
就是實例化一個`\yii\db\Query`對象來執行查詢,`from`方法用于指定表
* * *
# where 指定條件
~~~php
$query = new \yii\db\Query();
// SELECT * FROM user WHERE status = 1
$users = $query->from('user')->where(['status' => 1])->all();
print_r($users);
~~~
其中這個where方法的條件寫法將在后面陸續擴展增強演示,各種條件都能表達,除非我寫漏
# one 查詢單條記錄
根據ID查一條記錄
~~~php
$query = new \yii\db\Query();
// SELECT * FROM user WHERE id = 1
$data = $query->from('test')->where(['id' => 1])->one();
print_r($data);
~~~
**注意one方法不會limit 1,而只是在編程語言級別獲取第1條,所以比較適合條件本身就只有唯一的情況**
提示:后面我的演示代碼都不再new了,直接以$query這個變量作為查詢構造器開始演示,以上代碼只是方便你直接復制調試
# 判斷記錄是否存在
~~~php
$query = new \yii\db\Query();
//SELECT EXISTS(SELECT * FROM user WHERE type = 2)
$exists = $query->from('user')->where(['default_patient_id' => 2])->exists();
if($exists == true){
//...
}
~~~
# select 指定查詢字段
~~~php
// SELECT id,name FROM user
$query->select(['id', 'name'])->from('user')->all();
~~~
# 定義字段別名
查找年齡為50的用戶的id,名字和郵箱
~~~php
// SELECT id,email AS mail FROM user
$query->select(['id', 'mail' => 'email'])->from('user')->all();
~~~
其中注意第2個字段`'mail' => 'email'`,意思是查詢email字段,但返回值時以mail作為key
# orderBy排序,limit限制篩選記錄數
查找最新注冊的一個用戶
~~~php
//SELECT * FROM user ORDER BY add_time DESC limit 1
$query->from('user')->orderBy(['add_time' => SORT_DESC])->limit(1)->one();
~~~
`orderBy`設定排序字段,以`數組`作參數,`key是要排序的字段`,排序方式是PHP自帶常量`SORT_ASC`或`SORT_DESC`
`limit`用于限制輸出幾條記錄
需要增加更多排序字段則增加更多`KEY => VALUE`的數組單元,比如`orderBy(['id' => SORT_ASC, 'age' => SORT_DESC])`
**SORT\_ASC**和**SORT\_DESC**是PHP自帶的常量
# 獲取SQL語句
~~~php
$query = new \yii\db\Query();
$query->from('user')->where(['id' => 999]); //注意不要加all或one
echo $query->createCommand()->sql; //獲取 參數化的 SQL原型
echo $query->createCommand()->rawSql; //最終會生成的SQL語句
~~~
在還沒有執行all或one之前不會進行查詢操作,此時再通過createCommand方法創建一個`yii\db\Command`對象,再訪問這個對象的rawSql就能得到SQL語句
`\yii\db\Query`只是一個查詢命令的**構造器**,底層最終其實還是由createCommand創建一個`yii\db\Command`來執行的)
# 獲取執行過的語句
在獲取執行過的SQL方面,我并沒有在Yii里看到簡單快速的方式來獲取,大家平時調試比較需要,所以我繼承yii\\db\\Connection封裝了一個方法來獲取,[參考代碼](https://github.com/kk8686/xoa/blob/master/server/common/ext/db/Connection.php)
使用方法是:下載這個類的代碼放到你的項目中,按照你的需求修改命名空間,調用示例
`echo Yii::$app->db->getLastSqls();`獲取上次執行的一條SQL語句
`echo Yii::$app->db->getLastSqls(3);`獲取上次執行的三條SQL語句
`echo Yii::$app->db->getLastSqls(2, 'from`user`');`獲取上兩條包含'from`user`'關鍵字的SQL語句
# 多個and條件
查找年齡為60的女性用戶
~~~php
// SELECT * FROM user WHERE age = 50 AND sex = 2
$query->from('user')->where([
'age' => 50,
'sex' => 2, //假設是女性的類型標識
])->all();
~~~
和大多數框架一樣,`where`的參數是數組,每一對`KEY => VALUE`就是一個`AND條件`單元了
好了后面我的代碼不執行one或all了,為了讓大家方便地直接復制代碼看到生成的SQL我都用**createCommand()->rawSql**了
# in條件
查找ID為1,2,3并且年齡為30的用戶
~~~php
// SELECT * FROM user WHERE age = 30 AND id in(1, 2, 3)
echo $query->from('user')->where([
'age' => 30,
'id' => [1, 2, 3],
])->createCommand()->rawSql;
~~~
要實現in條件語句則為這個字段的值傳入數組即可
# count 統計
統計(年齡為50)的(男性 和 人妖)的用戶數量`假設sex的1=男,2=女,3=人妖`
~~~php
// SELECT count(*) FROM user WHERE type = 1
$query->from('user')->where(['type' => 1])->count();
~~~
# 大于小于條件比較+offset分頁
以10個用戶顯示一頁的話,查找出第2頁未成年用戶
~~~php
// SELECT * FROM user WHERE age < 18 OFFSET 10 LIMIT 10
echo $query->from('user')->where(['<', 'age', 18])->offset(10)->limit(10)->createCommand()->rawSql;
~~~
這時候你發現where的條件的每一個元素都沒有key了,第一個參數表示比較邏輯符,第二個是比較字段,第三個是比較的值
# 大于等于,小于等于條件比較
查詢所有成年人
~~~php
// SELECT * FROM user WHERE age >= 18
echo $query->from('user')->where(['>=', 'age', 18])->createCommand()->rawSql;
~~~
# like查詢
~~~php
// SELECT * FROM user WHERE name LIKE '%abc%'
echo $query->from('user')->where(['like', 'name', 'abc'])->createCommand()->rawSql;
// SELECT * FROM user WHERE name LIKE '%abc%' AND name LIKE '%xyz%'
echo $query->from('user')->where(['like', 'name', ['abc', 'xyz']])->createCommand()->rawSql;
// SELECT * FROM user WHERE name LIKE '%abc'
echo $query->from('user')->where(['like', 'name', '%abc'])->createCommand()->rawSql;
// SELECT * FROM user WHERE name LIKE 'abc%'
echo $query->from('user')->where(['like', 'name', 'abc%'])->createCommand()->rawSql;
// SELECT * FROM user WHERE name NOE LIKE '%abc%'
echo $query->from('user')->where(['not like', 'name', 'abc'])->createCommand()->rawSql;
~~~
# between篩選和group by分組結果
查詢各性別的未成年用戶
~~~php
// SELECT * FROM user WHERE age BETWEEN 1 AND 18 GROUP BY sex
echo $query->from('user')->where(['between', 'age', 1, 18])->groupBy(['sex']) ->createCommand()->rawSql;
~~~
要groupBy多個字段的話就是`['sex', 'country']`這樣,繼續增加數組元素就好了
# having二級篩選
查詢某分類的文章發表數量超過100的用戶
~~~php
// SELECT count(user_id) as arc_count FROM article WHERE category = 33 HAVING arc_count > 100
$query->select(['arc_count' => 'count(user_id)'])->from('article')->where(['category' => 33])->having('arc_count > 100')->createCommand()->rawSql;
~~~
# or邏輯條件構造
查詢名字為lily或者性別為男的用戶
~~~php
// SELECT * FROM user WHERE name = 'abc' OR sex = 1
$name = 'lily';
echo $query->from('user')->where([
'or',
['name' => 'abc'],
['sex' => 1],
])->createCommand()->rawSql;
~~~
之前where方法傳數組是以鍵值對來表達`key1=val1 and key2 = val2`這樣的
但你要表達復雜邏輯關系時,數組的`第一個元素`必須先聲明你是`什么邏輯關系`,比如`or`
再以`第二個元素`表達or`左邊的條件`是什么,然后`第三個元素`表達or`右邊的條件`是什么
這里要注意第二和第三個參數,第一個是**字符串**,表達了邏輯關系,第二和第三個為什么是數組?很簡單的,因為邏輯關系的左邊和右邊可能會有很多條件,比如
~~~sql
(a=1 and b=2) or (右邊那一塊...)
~~~
所以左邊和右邊都可能是多個條件,意味著如果左右邊都有多條件的話就應該這樣寫了:
~~~php
where([
'or',
[
//or左邊的條件塊
'name' => 'abc',
['<', 'age', 18], //當不是 字段 = 值 的條件而是大于小于之類的比較時,請注意這里要用數組了,前面介紹過大于小于,between那些都可以這樣用
'xxx' => 6,
],
[
//or右邊的條件塊
'sex' => 1,
'email' => 'abc@dd.com',
],
])
~~~
# and、or條件嵌套
查詢用戶表**(年齡為$age)并且(性別為女 或者 2014年注冊)**的用戶
~~~php
// SELECT * FROM user WHERE age = 33 AND (sex = 2 OR add_time > '2014-01-01 00:00:00')
echo $query->from('user')->where([
'and',
['age' => 33],
[
'or',
['sex' => 2],
['>', 'add_time', '2014-01-01 00:00:00'],
],
])->createCommand()->rawSql;
~~~
回到數組的條件傳達方式,之前說過,where條件的數組第一維度,第一元素是邏輯關鍵字,第二元素是邏輯的左邊條件,第三元素是邏輯的右邊條件
而如果右邊條件又是一個多條件表達式,那么則用數組表達,這個數組還是第一元素是邏輯關鍵字,第二元素是左邊邏輯,第三元素是你懂的邏輯
那么反過來,邏輯左邊條件是復合型條件的話又怎么寫第二元素呢?
# 追加and條件
~~~php
$title = 'xx';
$query->from('user')->where(['status' => 2]);
if($title){
//重點
$query->andWhere(['like', 'title', $title]);
}
//SELECT * FROM user WHERE status AND title LIKE '%xx%'
echo $query->createCommand()->rawSql;
~~~
這樣后面andWhere的會跟前面where的組合成and邏輯,如果要換or那就用orWhere方法也可以
# 追加or條件
和andWhere道理是一樣的
~~~php
$title = 'xx';
$query->from('user')->where(['status' => 2]);
if($title){
//重點
$query->orWhere(['like', 'title', $title]);
}
//SELECT * FROM user WHERE status OR title LIKE '%xx%'
echo $query->createCommand()->rawSql;
~~~
# 自動過濾空值條件
你曾經可能經常寫這樣的代碼:
~~~php
if($location){
$where['location'] = $location;
}
~~~
這樣的判斷邏輯在很多程序里都有,yii提供了這樣的辦法:
~~~php
$category = 0;
$location = '';
// SELECT * FROM user WHERE (category = 0 AND size = 33) AND name LEKE '%xx%'
echo $query->from('user')->filterWhere([
'category_id' => $category,
'location' => $location,
'size' => 33,
])->andWhere(['like', 'name', 'xx'])->createCommand()->rawSql;
~~~
由于location是**空字符串**所以該字段的條件不會生成,只會生成其它非空字符串的條件
不過如果還是很喜歡自己寫SQL的話那請一定要做好參數綁定工作防注入!(參數綁定方法可以自己抽時間另外學習yii官方的教程)
# 表別名、左聯接查詢以及聯接表別名的使用
查詢所有文章的標題和發布人的名稱
~~~php
// SELECT * a.title as title, u.name as username FROM article as a LEFT JOIN user as u ON u.id = a.user_id
echo $query->select([
'title' => 'a.title',
'username' => 'u.name',
])->from(['a' => 'article'])->leftJoin(['u' => 'user'], 'u.id = a.user_id')->createCommand()->rawSql;
~~~
# 更新積分+1或-1什么的
這個要靠[yii\\db\\Expression](http://www.yiichina.com/doc/api/2.0/yii-db-exception)來實現
~~~php
$expression = new \yii\db\Expression('score + 9999');
echo $query->createCommand()
->update('user', ['score' => $expression])
->rawSql; // UPDATE `user` SET `score` = score + 9999;
~~~
# 批量查詢 ->大數據維護處理或統計
(哈哈,說得好高大上~~~反正這數據就是大,數量多!)
有沒有過這樣的經歷,某類數據運營一年半年后產生了上千萬條記錄,且不這么說,就是上百萬條吧
然后這個數據的A字段是一個復合數據,里面保存了一些具體的信息,比如里面包含了10個別的地方的ID集
然后某天的產品需求造成程序要根據這些ID集的數量來進行查詢,那總不能count這個字段啊
因為它對于MYSQL而言就是一個字符串而已,要查詢就要where這個數據的數量是否大于指定值來查詢了
而且還要搞個索引來優化查詢速度,沒辦法,那就要在別的專用查詢表或這個表上加一個xxx\_count這樣的字段
然后將所有現存數據的A字段ID集個數統計一下存入到xxx\_count字段以便查詢。
這是一種需求,別的還有比如說簡單地就是查出所有XXX字段大于多少的用戶,坑爹了有時候就算你分表了
在前面的需求和這個需求的情況下你都能遇到一個問題:PHP內存不足,不能一下子讀出所有的記錄來處理。試過了吧?
然后你的辦法可能就是做一些分頁查詢控制,比如第一次查先查1000條,處理完再查第2000條…做完一次再刷新,做完一次再刷新…...
或者還有別的流程,反正就是要你很麻煩地去搭建代碼
Yii提供了底層的分頁查詢處理,不用你再寫這些大數據轉換的非核心邏輯代碼。下面引用官方文檔的代碼足矣:
~~~php
use yii\db\Query;
$query = (new Query())->from('user') ->orderBy('id');
foreach($query->batch() as $users) {
//這樣會先查出100條記錄放到$users里,在第二次for循環的時候再查第二百條,第三次就查第三百條…但關于這個100條如何控制數量變成1000條等,暫時未在文檔中找到控制參數,然而這個問題不大,畢竟最終會遍歷整個表。除了數據維護,其實前端要為用戶統計一些數據的時候,也避免了先查出所有記錄一齊遍歷統計的麻煩,統計一批數據就丟棄一批,再查下一批
}
foreach ($query->each() as $user) {
//用each時每次for循環都會查詢下一條出來
}
~~~
- 目錄
- 配置
- 簡介
- 別名
- gii
- 配置項
- 模型
- 簡介
- 增刪改查
- AR和model
- 模型事件
- 場景
- query查詢
- 增刪改
- AR查詢器
- 模型關系定義
- AR模型連表查詢
- fields
- where拼接
- 模塊
- 創建模塊
- 控制器
- 表單
- 跳轉
- 響應
- 驗證器
- Action
- 組件
- url
- 分頁
- 驗證碼
- 緩存
- 文件上傳
- 預啟動組件
- 事件
- 自定義組件
- redis
- 日志
- 行為
- cookie和session
- 基礎知識
- 創建一個類
- 配置一個類
- object基類
- component組件類特性
- phpstorm無法更改php等級
- url地址美化
- 過濾器
- 請求處理
- 請求組件
- 響應組件
- header
- 用戶登錄
- 實現IdentityInterface接口
- 登錄
- 自動檢測登錄
- 獲取用戶信息
- 訪問行為追蹤
- phpstorm+postman斷點調試