参见《Thinkphp5.1完全开发手册》学习 Mirror王宇阳
想要查看SQL的执行语句细节可以配置:config/app.php -> app_trace => true; 开启后就可以在HTML页面右下角打开SQL语句的具体执行过程 注意:json输出的数据无法使用这一功能哦!
查询单个数据使用find
方法:
// table方法必须指定完整的数据表名
Db::table('think_user')->where('id',1)->find();
最终生成的SQL语句可能是:
SELECT * FROM `think_user` WHERE `id` = 1 LIMIT 1
find 方法查询结果不存在,返回 null,否则返回结果数组
V5.1.23+
版本开始,支持findOrEmpty
方法,当查询不存在的时候返回空数组而不是Null。
// table方法必须指定完整的数据表名
Db::table('think_user')->where('id',1)->findOrEmpty();
如果没有查找到数据,则会抛出一个think\db\exception\DataNotFoundException
异常。
try{ //捕获异常
// table方法必须指定完整的数据表名
$data = Db::table('think_user')->where('id',1)->findOrEmpty();
} catch (DataNotFoundException $err) {
return '数据查询发送异常!'
}
查询多个数据(数据集)使用select
方法:
Db::table('think_user')->where('status',1)->select();
最终生成的SQL语句可能是:
SELECT * FROM `think_user` WHERE `status` = 1
select 方法查询结果是一个二维数组,如果结果不存在,返回空数组
如果希望在没有查找到数据后抛出异常可以使用
try{ //捕获异常
$data = Db::table('think_user')->where('status',1)->selectOrFail();
} catch (DataNotFoundException $err) {
return '数据查询发送异常!'
}
默认情况下,
find
和select
方法返回的都是数组,区别在于后者是二维数组。
系统提供了一个db
助手函数,可以更方便的查询:
db('user')->where('id',1)->find();
db('user')->where('status',1)->select();
db
方法的第一个参数的作用和name
方法一样,如果需要使用不同的数据库连接,可以使用:
db('user','db_config1')->where('id', 1)->find();
查询某个字段的值可以用 value(‘字段名’)
// 返回某个字段的值
Db::table('think_user')->where('id',1)->value('name');
value 方法查询结果不存在,返回 null
查询某一列的值可以用 colum('字段名/列名')
// 返回数组
Db::table('think_user')->where('status',1)->column('name');
// 指定id字段的值作为索引
Db::table('think_user')->where('status',1)->column('name','id');
如果要返回完整数据,并且添加一个索引值的话,可以使用
// 指定id字段的值作为索引 返回所有数据
Db::table('think_user')->where('status',1)->column('*','id');
column 方法查询结果不存在,返回空数组
数据分批处理可以使用 chunk
我们可以全部用户表数据进行分批处理,每次处理 100 个用户记录:
Db::table('think_user')->chunk(100, function($users) {
foreach ($users as $user) {
//
}
});
// 或者交给回调方法myUserIterator处理
Db::table('think_user')->chunk(100, 'myUserIterator');
你可以通过从闭包函数中返回false
来中止对后续数据集的处理:
Db::table('think_user')->chunk(100, function($users) {
foreach ($users as $user) {
// 处理结果集...
if($user->status==0){
return false;
}
}
});
也支持在chunk
方法之前调用其它的查询方法,例如:
Db::table('think_user')
->where('score','>',80)
->chunk(100, function($users) {
foreach ($users as $user) {
//
}
});
chunk
方法的处理默认是根据主键查询,支持指定字段,例如:
Db::table('think_user')->chunk(100, function($users) {
// 处理结果集...
return false;
},'create_time');
并且支持指定处理数据的顺序。
Db::table('think_user')->chunk(100, function($users) {
// 处理结果集...
return false;
},'create_time', 'desc');
chunk
方法一般用于命令行操作批处理数据库的数据,不适合WEB访问处理大量数据,很容易导致超时。
大批量数据处理
如果你需要处理大量的数据,可以使用新版提供的游标查询功能,该查询方式利用了PHP的生成器特性,可以大幅减少大量数据查询的内存占用问题。
$cursor = Db::table('user')->where('status', 1)->cursor();
foreach($cursor as $user){
echo $user['name'];
}
cursor
方法返回的是一个生成器对象,user
变量是数据表的一条数据(数组)。
JSON类型数据查询(mysql
)
// 查询JSON类型字段 (info字段为json类型)
Db::table('think_user')
->where('info->email','thinkphp@qq.com')
->find();
->
多次连续的调用方法
Db::table('think_user') ->where('status',1) ->order('create_time') ->limit(10) ->select();
removerOption()
方法可以清理上一个保留的值
$data3 = $book->removeOption('where')->column('title'); return json($data3); // 结果返回 没有where条件限制了
ThinkPHP5.1使用insert()
、insertGetId()
方法向数据表添加一条数据
使用 Db
类的 insert
方法向数据库提交数据
$data = ['foo' => 'bar', 'bar' => 'foo'];
// 笔者在insert()的时候,发现中文内容添加后会在数据库中显示空白
Db::name('user')->insert($data);
insert
方法添加数据成功返回添加成功的条数,通常情况返回 1
// 新增数据到数据表中
public function insert()
{
$book = Db::table('tp_book');
// 匹配添加数据
$data = [ // 可为空Null的、自动增补的可以忽略不添加
'user_id' => '66',
'title' => 'Mirror'
];
$book->insert($data);
$select = $book->select();
return json($select);
}
或者使用data
方法配合insert
使用。
$data = ['foo' => 'bar', 'bar' => 'foo'];
Db::name('user')
->data($data)
->insert();
如果你的数据表里面没有
foo
或者bar
字段,那么就会抛出异常。
如果不希望抛出异常,可以使用下面的方法:
$data = ['foo' => 'bar', 'bar' => 'foo'];
Db::name('user')->strict(false)->insert($data);
不存在的字段的值将会直接抛弃。
如果是mysql数据库,支持replace
写入,例如:
$data = ['foo' => 'bar', 'bar' => 'foo'];
Db::name('user')->insert($data, true);
添加数据后如果需要返回新增数据的自增主键,可以使用insertGetId
方法新增数据并返回主键值:
$userId = Db::name('user')->insertGetId($data);
insertGetId
方法添加数据成功返回添加数据的自增主键
添加多条数据直接向 Db 类的 insertAll
方法传入需要添加的数据即可
public function insertAll()//批量添加到数据表
{
$dataAll = [
[
'user_id' => '66',
'title' => '《白帽子讲Web安全》'
],
[
'user_id' => '88',
'title' => '《乔布斯传》'
],
[
'user_id' => '99',
'title' => '《白夜行》'
],
];
$kk = Db::table('tp_book')->insertAll($dataAll);
return $kk;
}
insertAll 方法添加数据成功返回添加成功的条数
如果批量插入的数据比较多,可以指定分批插入,使用limit
方法指定每次插入的数量限制。
$data = [
['foo' => 'bar', 'bar' => 'foo'],
['foo' => 'bar1', 'bar' => 'foo1'],
['foo' => 'bar2', 'bar' => 'foo2']
...
];
// 分批写入 每次最多100条数据
Db::name('user')->data($data)->limit(100)->insertAll();
// 修改数据
public function update()
{
$date = [
'title' => '《解忧杂货铺》'
];
$kk = Db::table('tp_book')->where('user_id','99')->update($date);
return $kk;
}
update 方法返回影响数据的条数,没修改任何数据返回 0
如果数据中包含主键,可以直接使用:
Db::name('user')
->update(['name' => 'thinkphp','id'=>1]);
如果要更新的数据需要使用SQL
函数或者其它字段,可以使用下面的方式:
Db::name('user')
->where('id',1)
->inc('read_time') //对字段增值
->dec('score',3) //对字段减值
->exp('name','UPPER(name)') // 在字段中使用mysql函数
->update();
可以使用setInc/setDec
方法自增或自减一个字段的值( 如不加第二个参数,默认步长为1)。
// score 字段加 1
Db::table('think_user')
->where('id', 1)
->setInc('score');
// score 字段加 5
Db::table('think_user')
->where('id', 1)
->setInc('score', 5);
// score 字段减 1
Db::table('think_user')
->where('id', 1)
->setDec('score');
// score 字段减 5
Db::table('think_user')
->where('id', 1)
->setDec('score', 5);
setInc/setDec
支持延时更新,如果需要延时更新则传入第三个参数,下例中延时10秒更新。
Db::name('user')->where('id', 1)->setInc('score', 1, 10);
V5.1.7+
版本以后,支持使用raw
方法进行数据更新,适合在数组更新的情况。
Db::name('user')
->where('id', 1)
->update([
'name' => Db::raw('UPPER(name)'),
'score' => Db::raw('score-3'),
'read_time' => Db::raw('read_time+1')
]);
字段值更新 setField('原字段名','新字段名')
Db::name('user')
->where('id',1)
->setField('name', 'thinkphp');
// 根据主键删除
Db::table('think_user')->delete(1);
Db::table('think_user')->delete([1,2,3]);
// 条件删除
Db::table('think_user')->where('id',1)->delete();
Db::table('think_user')->where('id','<',10)->delete();
// 无条件删除所有数据
Db::name('user')->delete(true);
Db::name('book')->where('id',80)->find(); //简写
Db::name('book')->where('id','=',80)->find(); //完整;且与第一条同意义
5.1
还支持新的查询方法
whereField('表达式','查询条件');
whereOrField('表达式','查询条件');
Field
使用字段的驼峰命名方式。
whereLike/whereNotLike
[NOT] BETWEEN (不在)区间查询 whereBetween/whereNotBetween
[NOT] IN (不在)IN 查询 whereIn/whereNotIn
[NOT] NULL 查询字段是否(不)是NULL whereNull/whereNotNull
[NOT] EXISTS EXISTS查询 whereExists/whereNotExists
[NOT] REGEXP 正则(不)匹配查询 (仅支持Mysql) [NOT] BETWEEM TIME 时间区间比较 whereBetweenTime > TIME 大于某个时间 whereTime
< TIME 小于某个时间 whereTime
>= TIME 大于等于某个时间 whereTime
<= TIME 小于等于某个时间 whereTime
EXP 表达式查询,支持SQL语法 whereExp
whereLike
方法
Db::name('user')->whereLike('name','thinkphp%')->select(); Db::name('user')->whereNotLike('name','thinkphp%')->select();
Null
// null、not null 查询指定字段是否可以为Null/Not Null $data = Db::table('tp_access')->where('details','null')->column('user_id'); $data = Db::table('tp_access')->where('details','notnull')->column('user_id'); $data = Db::table('tp_access')->whereNull('details')->column('user_id'); $data = Db::table('tp_access')->whereNotNull('details')->column('user_id'); return json($data);
支持更复杂的查询情况 例如:
Db::name('user')->where('id','in','1,3,8')->select();
可以改成:
Db::name('user')->where('id','exp',' IN (1,3,8) ')->select();
exp
查询的条件不会被当成字符串,所以后面的查询条件可以使用任何SQL支持的语法,包括使用函数和字段名称。
$data = Db::table('tp_access')->where('id','exp','in (1,2,3,4)')->column('user_id');// 快捷 ↓
$data = Db::table('tp_access')->whereExp('id','in (1,2,3,4)')->column('user_id');
// 等效于 ==> SELECT `user_id` FROM `tp_access` WHERE ( `id` in (1,2,3,4) )
$data = Db::table('tp_book')->whereExp('id','>=20 and user_id =88 ')->column('title');
$data = Db::table('tp_book')->whereExp('','id >=20 and user_id =88 ')->column('title');
// 等效于 ==> SELECT `title` FROM `tp_book` WHERE ( `id` >=20 and user_id =88 )
print_r($data);
where
方法支持时间比较,例如:
// 采用比较符号比较时间 $data = Db::table('tp_one')->where('create_time','< time','2016-10-10 00:00:00')->select(); // ==> SELECT * FROM `tp_one` WHERE `create_time` < '2016-10-10 00:00:00' // 在第二参数明确 time 的时候,第三个参数会自动按照完整的time格式填充(年-月-日 时:分:秒) // 时间区间查询 where('create_time', 'between time', ['2015-1-1', '2016-1-1']);
第三个参数可以传入任何有效的时间表达式,会自动识别你的时间字段类型,支持的时间类型包括timestamps
、datetime
、date
和int
区间查询 whereTime()
/wherebetween()
/wherebetweenTime()
:
// 采用whereTime()区间查询时间 (whereTime()和between time 结果是一样的,推荐后者) $data = Db::table('tp_one') ->where('create_time','between time',['2018-01-01','2019-01-01']) ->select(); $data = Db::table('tp_one') ->whereTime('create_time',['2018-01-01','2019-01-01']) ->select(); // ==> SELECT * FROM `tp_one` WHERE `create_time` BETWEEN '2018-01-01 00:00:00' AND '2019-01-01 00:00:00' // wherebetween() 也支持如上的区间Time查询 $data = Db::table('tp_one') ->whereBetween('create_time',['2018-01-01','2019-01-01']) ->select(); // wherebetweenTime() 也是一个支持时间起始查询函数 $data = Db::table('tp_one') ->whereBetweenTime('create_time','2018-01-01','2019-01-01') ->select(); // 如果第三个参数为空则代表查询当天(一天)
whereTime()
还支持时间便捷固定查询:
关键字 (whereTime()的第三个参数值) 说明 today 今天 yesterday 昨天 week 本周 last week 上周 month 本月 last month 上月 year 本年 last year 去年 同时还支持指定的时间数据查询;例如两小时内就是:-2 hour
whereBetweenTimeField()
多时间(字)段查询
V5.1.17+
版本开始,可以支持对两个时间字段的区间比较
// 查询有效期内的活动 Db::name('event') ->whereBetweenTimeField('start_time','end_time') ->select();
上面的查询相当于
// 查询有效期内的活动 Db::name('event') ->whereTime('start_time', '<=', time()) ->whereTime('end_time', '>=', time()) ->select();
在应用中我们经常会用到一些统计数据,例如当前所有(或者满足某些条件)的用户数、所有用户的最大积分、用户的平均成绩等等,ThinkPHP为这些统计操作提供了一系列的内置方法,包括:
方法 | 说明 |
---|---|
count | 统计数量,参数是要统计的字段名(可选) |
max | 获取最大值,参数是要统计的字段名(必须) |
min | 获取最小值,参数是要统计的字段名(必须) |
avg | 获取平均值,参数是要统计的字段名(必须) |
sum | 获取总分,参数是要统计的字段名(必须) |
聚合方法如果没有数据,默认都是0,聚合查询都可以配合其它查询条件
V5.1.5+
版本开始,聚合查询可以支持JSON
字段类型(MySQL5.7+开始支持JSON)
count()
统计数量: 可以根据表的行数或根据字段的行数
$data = Db::table('tp_book')->count();
// ==> SELECT COUNT(*) AS tp_count FROM `tp_book`
$data = Db::table('tp_book')->count('id');
// ==> SELECT COUNT(`id`) AS tp_count FROM `tp_book`
max()/min()
获取最值:可以根据字段名获取字段列中最值;如果字段中的不是数值,函数会自动强制转换,可以通过定义第二参数为“false”来取消强制转换的行为
// max()/min() 最值函数
$data = Db::table('tp_book')
->max('user_id');
// ==> SELECT MAX(`user_id`) AS tp_max FROM `tp_book`
$data = Db::table('tp_book')
->max('title');
$data = Db::table('tp_book')
->max('title',false); // 和↑条同作用,但是这条将不强制转换,按照编码进行
// ==> SELECT MAX(`title`) AS tp_max FROM `tp_book`
avg()
平均值计算
$data = Db::table('tp_book')->avg('user_id');
// ==> SELECT AVG(`user_id`) AS tp_avg FROM `tp_book`
sum()
求和计算
$data = Db::table('tp_book')->sum('user_id');
// ==> SELECT SUM(`user_id`) AS tp_sum FROM `tp_book`
fetchSql()
方法,不执行SQL语句而是返回SQL语句,默认True
$subQuery = Db::table('think_user') ->field('id,name') ->where('id', '>', 10) ->fetchSql(true) ->select();
生成的subQuery结果为:
SELECT `id`,`name` FROM `think_user` WHERE `id` > 10
buidSql()
方法,返回SQL语句且不需要执行select()同时添加两侧括号
$subQuery = Db::table('think_user') ->field('id,name') ->where('id', '>', 10) ->buildSql();
生成的subQuery结果为:
( SELECT `id`,`name` FROM `think_user` WHERE `id` > 10 )
在SQL语句中我们需要用到子查询的功能,就是利用SQL1语句的查询结果给SQL2语句使用
原生SQL语句:(具备子查询)
SELECT * FROM `tp_one` WHERE id in (SELECT uid FROM tp_two where gender = '男');
构造ThinkPHP的子查询代码:
$sql_1 = Db::table('tp_two')
->field('uid')
// field()方法主要作用是标识要返回或者操作的字段,可以用于查询和写入操作。
->where('gender','男')
->buildSql();
$sql = Db::table('tp_one')
->whereIn('id',$sql_1)
->select();
// ==> SELECT * FROM `tp_one` WHERE `id` = ( SELECT `uid` FROM `tp_two` WHERE `gender` = '男' )
// 这里使用WhereIn出现的问题就是原本应该是 IN 却变成了 = 符号
$sql_exp = Db::table('tp_one')
->whereExp('id','IN'.$sql_1)
->select();
// ==> SELECT * FROM `tp_one` WHERE ( `id` IN( SELECT `uid` FROM `tp_two` WHERE `gender` = '男' ) )
// 由于whereIn无法正确的使用,于是采用whereExp()方法构造IN并使用连接符连接$sql_1
使用闭包构造子查询
IN/NOT IN
和EXISTS/NOT EXISTS
之类的查询可以直接使用闭包作为子查询,例如:
Db::table('think_user')
->where('id', 'IN', function ($query) {
$query->table('think_profile')->where('status', 1)->field('id');
})
->select();
query
方法用于执行SQL
查询操作,如果数据非法或者查询错误则返回false,否则返回查询结果数据集(同select
方法)。
使用示例:
Db::query("select * from think_user where status=1");
execute
用于更新和写入数据的sql操作,如果数据非法或者查询错误则返回false
,否则返回影响的记录数。
使用示例:
Db::execute("update think_user set name='thinkphp' where status=1");
这里的where多样运用需要开发者按照自己的喜好和实际的开发需求决定如何使用;如果无法运用就直接使用原生!
table
方法主要用于指定操作的数据表。
Db::table('think_user')->where('status>1')->select(); // 指定数据表
Db::table('db_name.think_user')->where('status>1')->select(); //指定数据库和表
如果需要对多表进行操作,可以这样使用:
Db::field('user.name,role.title')
->table('think_user user,think_role role')
->limit(10)->select();
为了尽量避免和mysql的关键字冲突,可以建议使用数组方式定义,例如:
Db::field('user.name,role.title')
->table(['think_user'=>'user','think_role'=>'role'])
->limit(10)->select();
使用数组方式定义的优势是可以避免因为表名和关键字冲突而出错的情况。
alias
用于设置当前数据表的别名,便于使用其他的连贯操作;例如join方法等。
示例:
Db::table('think_user')
->alias('a')
->join('think_dept b ','b.user_id= a.id')
->select();
最终生成的SQL语句类似于:
SELECT * FROM think_user a INNER JOIN think_dept b ON b.user_id= a.id
field
方法主要作用是标识要返回或者操作的字段,可以用于查询和写入操作。
strict
方法用于设置是否严格检查字段名,用法如下:(建议true)
// 关闭字段严格检查
Db::name('user')
->strict(false)
->insert($data);
注意,系统默认值是由数据库配置参数fields_strict
决定,因此修改数据库配置参数可以进行全局的严格检查配置,如下:
// 关闭严格检查字段是否存在
'fields_strict' => false,
如果开启字段严格检查的话,在更新和写入数据库的时候,一旦存在非数据表字段的值,则会抛出异常。
limit()
用于限制输入输出的数据条数,也可以指定输出的行数范围
// limit() 可以指定输出的条数、行数范围、输入的条数限制
$data = Db::table('tp_book')->field('title')->limit(3)->select(); //限制条数
// ==> SELECT `title` FROM `tp_book` LIMIT 3
$data = Db::table('tp_book')->field('title')->limit(3,3)->select(); // 实现分页
// ==> SELECT `title` FROM `tp_book` LIMIT 3,3 //limit(起始行,条数)
limit
方法也可以用于写操作,例如更新满足要求的3条数据:
Db::table('think_user')
->where('score',100)
->limit(3)
->update(['level'=>'A']);
page()
方法主要用于分页查询。
// page() 分页查询
$data = Db::table('tp_book')->field('title')->page(1,3)->select();
// => SELECT `title` FROM `tp_book` LIMIT 0,3
$data = Db::table('tp_book')->field('title')->page(2,3)->select();
// => SELECT `title` FROM `tp_book` LIMIT 3,3
page()直接是规定第一页第二页即可,page()方法自动实现LIMIT的分页补充……而当LIMIT和page同时出现的话,page接受的一个参数代表页数,而limit的参数代表输出的每页条数。(具体见手册)
order()
方法用于对操作的结果排序或者优先级限制。(参考手册)
Db::table('think_user')
->where('status', 1)
->order('id', 'desc')
->limit(5)
->select();
SELECT * FROM `think_user` WHERE `status` = 1 ORDER BY `id` desc LIMIT 5
如果没有指定
desc
或者asc
排序规则的话,默认为asc
。
支持使用数组对多个字段的排序,例如:
Db::table('think_user')
->where('status', 1)
->order(['order','id'=>'desc'])
->limit(5)
->select();
最终的查询SQL可能是
SELECT * FROM `think_user` WHERE `status` = 1 ORDER BY `order`,`id` desc LIMIT 5
对于更新数据或者删除数据的时候可以用于优先级限制
Db::table('think_user')
->where('status', 1)
->order('id', 'desc')
->limit(5)
->delete();
生成的SQL
DELETE FROM `think_user` WHERE `status` = 1 ORDER BY `id` desc LIMIT 5
在V5.1.7+
版本开始,如果你需要在order
方法中使用mysql函数的话,必须使用下面的方式:
Db::table('think_user')
->where('status', 1)
->orderRaw("field(name,'thinkphp','onethink','kancloud')")
->limit(5)
->select();
GROUP
方法通常用于结合合计函数,根据一个或多个列对结果集进行分组 。(参考手册)
group
方法只有一个参数,并且只能使用字符串。
例如,我们都查询结果按照用户id进行分组统计:
Db::table('think_user')
->field('user_id,username,max(score)')
->group('user_id')
->select();
生成的SQL语句是:
SELECT user_id,username,max(score) FROM think_score GROUP BY user_id
也支持对多个字段进行分组,例如:
Db::table('think_user')
->field('user_id,test_time,username,max(score)')
->group('user_id,test_time')
->select();
生成的SQL语句是:
SELECT user_id,test_time,username,max(score) FROM think_user GROUP BY user_id,test_time
HAVING
方法用于配合group方法完成从分组的结果中筛选(通常是聚合条件)数据。
having
方法只有一个参数,并且只能使用字符串,例如:
Db::table('think_user')
->field('username,max(score)')
->group('user_id')
->having('count(test_time)>3')
->select();
生成的SQL语句是:
SELECT username,max(score) FROM think_score GROUP BY user_id HAVING count(test_time)>3