- TP内置了抽象数据库访问层,把不同的数据库操作封装起来,只需要使用公共的Db类进行操作,而无需针对不同的数据库写不同的代码和底层实现,Db类会自动调用相应的数据库驱动来处理。采用PDO方式,目前包含了Mysql、SqlServer、PgSQL、Sqlite等数据库的支持。
一、连接数据库
配置文件定义
常用的配置方式是在应用目录或者模块目录下面的database.php中添加下面的配置参数:
return [
// 数据库类型
'type' => 'mysql',
// 数据库连接DSN配置
'dsn' => '',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => 'thinkphp',
// 数据库用户名
'username' => 'root',
// 数据库密码
'password' => '',
// 数据库连接端口
'hostport' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => 'think_',
// 数据库调试模式
'debug' => false,
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
];
type参数支持命名空间完整定义,不带命名空间定义的话,默认采用\think\db\connector作为命名空间,如果使用应用自己扩展的数据库驱动,可以配置为:
// 数据库类型
'type' => '\org\db\Mysql',
表示数据库的连接器采用 \org\db\Mysql类作为数据库连接驱动,而不是默认的\think\db\connector\Mysql。
每个模块可以设置独立的数据库连接参数,并且相同的配置参数可以无需重复设置,例如我们可以在admin模块的database.php配置文件中定义:
return [
// 服务器地址
'hostname' => '192.168.1.100',
// 数据库名
'database' => 'admin',
];
表示admin模块的数据库地址改成 192.168.1.100,数据库名改成admin,其它的连接参数和应用的database.php中的配置一样。
V5.0.6+版本开始,支持Mysql的断线重连机制,默认关闭,需要的话,是数据库配置文件中添加:
// 开启断线重连
'break_reconnect' => true,
连接参数
可以针对不同的连接需要添加数据库的连接参数(具体的连接参数可以参考PHP手册),内置采用的参数包括如下:
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,
在database中设置的params参数中的连接配置将会和内置的设置参数合并,如果需要使用长连接,并且返回数据库的小写列名,可以采用下面的方式定义:
'params' => [
\PDO::ATTR_PERSISTENT => true,
\PDO::ATTR_CASE => \PDO::CASE_LOWER,
],
可以在params里面配置任何PDO支持的连接参数。
方法配置
我们可以在调用Db类的时候动态定义连接信息,例如:
Db::connect([
// 数据库类型
'type' => 'mysql',
// 数据库连接DSN配置
'dsn' => '',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => 'thinkphp',
// 数据库用户名
'username' => 'root',
// 数据库密码
'password' => '',
// 数据库连接端口
'hostport' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => 'think_',
]);
或者使用字符串方式:
Db::connect('mysql://root:[email protected]:3306/thinkphp#utf8');
字符串连接的定义格式为
数据库类型://用户名:密码@数据库地址:数据库端口/数据库名#字符集
注意:字符串方式可能无法定义某些参数,例如前缀和连接参数。
如果我们已经在应用配置文件(注意这里不是数据库配置文件)中配置了额外的数据库连接信息,例如:
//数据库配置1
'db_config1' => [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => 'thinkphp',
// 数据库用户名
'username' => 'root',
// 数据库密码
'password' => '',
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => 'think_',
],
//数据库配置2
'db_config2' => 'mysql://root:1234@localhost:3306/thinkphp#utf8';
可以改成
Db::connect('db_config1');
Db::connect('db_config2');
模型类定义
如果在某个模型类里面定义了connection属性的话,则该模型操作的时候会自动连接给定的数据库连接,而不是配置文件中设置的默认连接信息,通常用于某些数据表位于当前数据库连接之外的其它数据库,例如:
//在模型里单独设置数据库连接信息
namespace app\index\model;
use think\Model;
class User extends Model
{
protected $connection = [
// 数据库类型
'type' => 'mysql',
// 数据库连接DSN配置
'dsn' => '',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => 'thinkphp',
// 数据库用户名
'username' => 'root',
// 数据库密码
'password' => '',
// 数据库连接端口
'hostport' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => 'think_',
];
}
也可以采用DSN字符串方式定义,例如:
//在模型里单独设置数据库连接信息
namespace app\index\model;
use think\Model;
class User extends Model
{
//或者使用字符串定义
protected $connection = 'mysql://root:[email protected]:3306/thinkphp#utf8';
}
并且支持直接使用数据库连接的配置名称,例如:
//在模型里单独设置数据库连接信息
namespace app\index\model;
use think\Model;
class User extends Model
{
// 直接使用配置参数名
protected $connection = 'db_config1';
}
需要注意的是,ThinkPHP的数据库连接是惰性的,所以并不是在实例化的时候就连接数据库,而是在有实际的数据操作的时候才会去连接数据库。
配置参数参考
下面是默认支持的数据库连接信息:
常用数据库连接参数(params)可以参考PHP在线手册中的以PDO::ATTR_开头的常量。
如果是使用pgsql数据库驱动的话,请先导入 thinkphp/library/think/db/connector/pgsql.sql文件到数据库执行。
二、基本使用
配置了数据库连接信息后,就可以直接使用数据库运行原生SQL操作了,支持query(查询操作)和execute(写入操作)方法,并且支持参数绑定。
Db::query('select * from think_user where id=?',[8]);
Db::execute('insert into think_user (id, name) values (?, ?)',[8,'thinkphp']);
也支持命名占位符绑定,例如:
Db::query('select * from think_user where id=:id',['id'=>8]);
Db::execute('insert into think_user (id, name) values (:id, :name)',['id'=>8,'name'=>'thinkphp']);
可以使用多个数据库连接,使用
Db::connect($config)->query('select * from think_user where id=:id',['id'=>8]);
$config是一个单独的数据库配置,支持数组和字符串,也可以是一个数据库连接的配置参数名。
三、查询构造器
-
查询数据
基本查询
查询一个数据使用:
// table方法必须指定完整的数据表名 Db::table('think_user')->where('id',1)->find();
find 方法查询结果不存在,返回 null
查询数据集使用:
Db::table('think_user')->where('status',1)->select();
select 方法查询结果不存在,返回空数组
如果设置了数据表前缀参数的话,可以使用
Db::name('user')->where('id',1)->find(); Db::name('user')->where('status',1)->select();
如果数据表没有使用表前缀功能,那么name和table方法的一样的效果。
在find和select方法之前可以使用所有的链式操作方法。
默认情况下,find和select方法返回的都是数组。
主从查询
如果你使用了分布式数据库,那么默认情况下查询操作都是在从数据库中进行,如果你在一些特殊的情况下需要从主库读取数据,可以使用:
$data = ['foo' => 'bar', 'bar' => 'foo']; Db::table('think_user')->insert($data); Db::name('user')->master()->where('id',1)->find();
一旦某个表写入了数据,那么当前请求的后续查询操作都可以自动从主库读取。
需要在数据库配置文件中增加下面的配置参数:
// 从主库读取数据 'read_master' => true,
如果你只需要对某个表进行操作,可以使用readMaster方法。
$data = ['foo' => 'bar', 'bar' => 'foo']; Db::table('think_user') ->readMaster() ->insert($data); // 后续所有关于该数据表的查询都会走主库
也可以让后续所有数据表的查询都走主库
$data = ['foo' => 'bar', 'bar' => 'foo']; Db::table('think_user') ->readMaster(true) ->insert($data); // 后续所有数据表的查询都会走主库
助手函数
系统提供了一个db助手函数,可以更方便的查询:
db('user')->where('id',1)->find(); db('user')->where('status',1)->select();
使用db助手函数默认每次都会重新连接数据库,而使用Db::name或者Db::table方法的话都是单例的。db函数如果需要采用相同的链接,可以传入第三个参数,例如:
db(‘user’,[],false)->where(‘id’,1)->find();
db(‘user’,[],false)->where(‘status’,1)->select();
上面的方式会使用同一个数据库连接,第二个参数为数据库的连接参数,留空表示采用数据库配置文件的配置。使用Query对象或闭包查询
或者使用查询对象进行查询,例如
$query = new \think\db\Query(); $query->table('think_user')->where('status',1); Db::find($query); Db::select($query);
或者直接使用闭包函数查询,例如:
Db::select(function($query){ $query->table('think_user')->where('status',1); });
值和列查询
查询某个字段的值可以用
// 返回某个字段的值 Db::table('think_user')->where('id',1)->value('name');
value 方法查询结果不存在,返回 null
查询某一列的值可以用
// 返回数组 Db::table('think_user')->where('status',1)->column('name'); // 指定索引 Db::table('think_user')->where('status',1)->column('name','id'); Db::table('think_user')->where('status',1)->column('id,name'); // 同tp3的getField
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) { // 处理结果集... 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');
chunk方法支持指定处理数据的顺序。
Db::table('think_user')->chunk(100, function($users) { // 处理结果集... return false; },'create_time', 'desc');
JSON类型数据查询
// 查询JSON类型字段 (info字段为json类型) Db::table('think_user')->where('info$.email','[email protected]')->find();
-
添加数据
添加一条数据
使用 Db 类的 insert 方法向数据库提交数据
$data = ['foo' => 'bar', 'bar' => 'foo']; Db::table('think_user')->insert($data);
如果在database.php配置文件中配置了数据库前缀(prefix),那么可以直接使用 Db 类的 name 方法提交数据
Db::name('user')->insert($data);
insert 方法添加数据成功返回添加成功的条数,insert 正常情况返回 1
添加数据后如果需要返回新增数据的自增主键,可以使用getLastInsID方法:
Db::name('user')->insert($data); $userId = Db::name('user')->getLastInsID();
或者直接使用insertGetId方法新增数据并返回主键值:
Db::name('user')->insertGetId($data);
insertGetId 方法添加数据成功返回添加数据的自增主键
添加多条数据
添加多条数据直接向 Db 类的 insertAll 方法传入需要添加的数据即可
$data = [ ['foo' => 'bar', 'bar' => 'foo'], ['foo' => 'bar1', 'bar' => 'foo1'], ['foo' => 'bar2', 'bar' => 'foo2'] ]; Db::name('user')->insertAll($data);
insertAll 方法添加数据成功返回添加成功的条数
助手函数
// 添加单条数据 db('user')->insert($data); // 添加多条数据 db('user')->insertAll($list);
**快捷更新**
V5.0.5+以上版本封装的快捷更新方法data,可以配合insert使用。
举个例子说明用法:
Db::table('data') ->data(['name'=>'tp','score'=>1000]) ->insert();
-
更新数据
更新数据表中的数据
Db::table('think_user')->where('id', 1)->update(['name' => 'thinkphp']);
如果数据中包含主键,可以直接使用:
Db::table('think_user')->update(['name' => 'thinkphp','id'=>1]);
update 方法返回影响数据的条数,没修改任何数据返回 0
如果要更新的数据需要使用SQL函数或者其它字段,可以使用下面的方式:
Db::table('think_user') ->where('id', 1) ->update([ 'login_time' => ['exp','now()'], 'login_times' => ['exp','login_times+1'], ]);
V5.0.18+版本开始是数组中使用exp查询和更新的话,必须改成下面的方式:
Db::table('think_user') ->where('id', 1) ->update([ 'login_time' => Db::raw('now()'), 'login_times' => Db::raw('login_times+1'), ]);
更新某个字段的值:
Db::table('think_user')->where('id',1)->setField('name', 'thinkphp');
setField 方法返回影响数据的条数,没修改任何数据字段返回 0
自增或自减一个字段的值
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秒,给score字段增加1Db::table('think_user')->where('id', 1)->setInc('score', 1, 10);
助手函数
// 更新数据表中的数据 db('user')->where('id',1)->update(['name' => 'thinkphp']); // 更新某个字段的值 db('user')->where('id',1)->setField('name','thinkphp'); // 自增 score 字段 db('user')->where('id', 1)->setInc('score'); // 自减 score 字段 db('user')->where('id', 1)->setDec('score');
快捷更新
V5.0.5+以上版本封装的data、inc、dec和exp方法属于链式操作方法,可以配合update使用(官方推荐用法)。
下面举个例子说明用法:
Db::table('data') ->where('id',1) ->inc('read') ->dec('score',3) ->exp('name','UPPER(name)') ->update();
-
删除数据
删除数据表中的数据
// 根据主键删除 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();
delete 方法返回影响数据的条数,没有删除返回 0
助手函数
// 根据主键删除 db('user')->delete(1); // 条件删除 db('user')->where('id',1)->delete()
-
查询方法
条件查询方法
where方法
可以使用where方法进行AND条件查询:
Db::table('think_user') ->where('name','like','%thinkphp') ->where('status',1) ->find();
多字段相同条件的AND查询可以简化为如下方式:
Db::table('think_user') ->where('name&title','like','%thinkphp') ->find();
whereOr方法
使用whereOr方法进行OR查询:
Db::table('think_user') ->where('name','like','%thinkphp') ->whereOr('title','like','%thinkphp') ->find();
多字段相同条件的OR查询可以简化为如下方式:
Db::table('think_user') ->where('name|title','like','%thinkphp') ->find();
混合查询
where方法和whereOr方法在复杂的查询条件中经常需要配合一起混合使用,下面举个例子:
$result = Db::table('think_user')->where(function ($query) { $query->where('id', 1)->whereor('id', 2); })->whereOr(function ($query) { $query->where('name', 'like', 'think')->whereOr('name', 'like', 'thinkphp'); })->select();
生成的sql语句类似于下面:
SELECT * FROM `think_user` WHERE ( `id` = 1 OR `id` = 2 ) OR ( `name` LIKE 'think' OR `name` LIKE 'thinkphp' )
注意闭包查询里面的顺序,而且第一个查询方法用where或者whereOr是没有区别的。
getTableInfo方法
使用getTableInfo可以获取表信息,信息类型 包括 fields,type,bind,pk,以数组的形式展示,可以指定某个信息进行获取
// 获取`think_user`表所有信息 Db::getTableInfo('think_user'); // 获取`think_user`表所有字段 Db::getTableInfo('think_user', 'fields'); // 获取`think_user`表所有字段的类型 Db::getTableInfo('think_user', 'type'); // 获取`think_user`表的主键 Db::getTableInfo('think_user', 'pk');
-
查询语法
查询表达式
查询表达式支持大部分的SQL查询语法,也是ThinkPHP查询语言的精髓,查询表达式的使用格式:
where('字段名','表达式','查询条件'); whereOr('字段名','表达式','查询条件');
表达式不分大小写,支持的查询表达式有下面几种,分别表示的含义是:
表达式查询的用法示例如下:EQ :等于(=)
例如:
where('id','eq',100); where('id','=',100);
和下面的查询等效
where('id',100);
表示的查询条件就是 id = 100
NEQ: 不等于(<>)
例如:
where('id','neq',100); where('id','<>',100);
表示的查询条件就是 id <> 100
GT:大于(>)
例如:
where('id','gt',100); where('id','>',100);
表示的查询条件就是 id > 100
EGT:大于等于(>=)
例如:
where('id','egt',100); where('id','>=',100);
表示的查询条件就是 id >= 100
LT:小于(<)
例如:
where('id','lt',100); where('id','<',100);
表示的查询条件就是 id < 100
ELT: 小于等于(<=)
例如:
where('id','elt',100); where('id','<=',100);
表示的查询条件就是 id <= 100
[NOT] LIKE: 同sql的LIKE
例如:
where('name','like','thinkphp%');
查询条件就变成 name like ‘thinkphp%’
V5.0.5+版本开始,like查询支持使用数组
where('name','like',['%think','php%'],'OR');
[NOT] BETWEEN :同sql的[not] between
查询条件支持字符串或者数组,例如:
where('id','between','1,8');
和下面的等效:
where('id','between',[1,8]);
查询条件就变成 id BETWEEN 1 AND 8
[NOT] IN: 同sql的[not] in
查询条件支持字符串或者数组,例如:
where('id','not in','1,5,8');
和下面的等效:
where('id','not in',[1,5,8]);
查询条件就变成 id NOT IN (1,5, 8)
[NOT] IN查询支持使用闭包方式
[NOT] NULL :
查询字段是否(不)是Null,例如:
where('name', null); where('title','null'); where('name','not null');
如果你需要查询一个字段的值为字符串null或者not null,应该使用:
where('title','=', 'null'); where('name','=', 'not null');
EXP:表达式
支持更复杂的查询情况 例如:
where('id','in','1,3,8');
可以改成:
where('id','exp',' IN (1,3,8) ');
exp查询的条件不会被当成字符串,所以后面的查询条件可以使用任何SQL支持的语法,包括使用函数和字段名称。
-
链式操作
where
where方法的用法是TP查询语言的精髓,也是TP ORM的重要组成部分和亮点所在,可以完成包括普通查询、表达式查询、快捷查询、区间查询、组合查询在内的查询操作。where方法的参数支持字符串和数组,虽然也可以使用对象但并不建议。
表达式查询
新版的表达式查询采用全新的方式,查询表达式的使用格式:
Db::table('think_user') ->where('id','>',1) ->where('name','thinkphp') ->select();
数组条件
可以通过数组方式批量设置查询条件。
普通查询
最简单的数组查询方式如下:
$map['name'] = 'thinkphp'; $map['status'] = 1; // 把查询条件传入查询方法 Db::table('think_user')->where($map)->select(); // 助手函数 db('user')->where($map)->select();
最后生成的SQL语句是
SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
表达式查询
可以在数组条件中使用查询表达式,例如:
$map['id'] = ['>',1]; $map['mail'] = ['like','%[email protected]%']; Db::table('think_user')->where($map)->select();
字符串条件
使用字符串条件直接查询和操作,例如:
Db::table('think_user')->where('type=1 AND status=1')->select();
最后生成的SQL语句是
SELECT * FROM think_user WHERE type=1 AND status=1
使用字符串条件的时候,建议配合预处理机制,确保更加安全,例如:
Db::table('think_user')->where("id=:id and username=:name")->bind(['id'=>[1,\PDO::PARAM_INT],'name'=>'thinkphp'])->select();
table
table方法主要用于指定操作的数据表。
一般情况下,操作模型的时候系统能够自动识别当前对应的数据表,所以,使用table方法的情况通常是为了:
切换操作的数据表; 对多表进行操作;
例如:
Db::table('think_user')->where('status>1')->select();
也可以在table方法中指定数据库,例如:
Db::table('db_name.think_user')->where('status>1')->select();
table方法指定的数据表需要完整的表名,但可以采用下面的方式简化数据表前缀的传入,例如:
Db::table('__USER__')->where('status>1')->select();
会自动获取当前模型对应的数据表前缀来生成 think_user 数据表名称。
需要注意的是table方法不会改变数据库的连接,所以你要确保当前连接的用户有权限操作相应的数据库和数据表。 切换数据表后,系统会自动重新获取切换后的数据表的字段缓存信息。
如果需要对多表进行操作,可以这样使用:
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
alias用于设置当前数据表的别名,便于使用其他的连贯操作例如join方法等。
示例:
Db::table('think_user')->alias('a')->join('__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
v5.0.2+版本开始,可以传入数组批量设置数据表以及别名,例如:
Db::table('think_user')->alias(['think_user'=>'user','think_dept'=>'dept'])->join('think_dept','dept.user_id= user.id')->select();
最终生成的SQL语句类似于:
SELECT * FROM think_user user INNER JOIN think_dept dept ON dept.user_id= user.id
field
field方法属于模型的连贯操作方法之一,主要目的是标识要返回或者操作的字段,可以用于查询和写入操作。
用于查询
指定字段
在查询操作中field方法是使用最频繁的。
Db::table('think_user')->field('id,title,content')->select();
这里使用field方法指定了查询的结果集中包含id,title,content三个字段的值。执行的SQL相当于:
SELECT id,title,content FROM table
可以给某个字段设置别名,例如:
Db::table('think_user')->field('id,nickname as name')->select();
执行的SQL语句相当于:
SELECT id,nickname as name FROM table
使用SQL函数
可以在field方法中直接使用函数,例如:
Db::table('think_user')->field('id,SUM(score)')->select();
执行的SQL相当于:
SELECT id,SUM(score) FROM table
除了select方法之外,所有的查询方法,包括find等都可以使用field方法。
使用数组参数
field方法的参数可以支持数组,例如:
Db::table('think_user')->field(['id','title','content'])->select();
最终执行的SQL和前面用字符串方式是等效的。
数组方式的定义可以为某些字段定义别名,例如:
Db::table('think_user')->field(['id','nickname'=>'name'])->select();
执行的SQL相当于:
SELECT id,nickname as name FROM table
对于一些更复杂的字段要求,数组的优势则更加明显,例如:
Db::table('think_user')->field(['id','concat(name,"-",id)'=>'truename','LEFT(title,7)'=>'sub_title'])->select();
执行的SQL相当于:
SELECT id,concat(name,'-',id) as truename,LEFT(title,7) as sub_title FROM table
获取所有字段
如果有一个表有非常多的字段,需要获取所有的字段(这个也许很简单,因为不调用field方法或者直接使用空的field方法都能做到):
Db::table('think_user')->select(); Db::table('think_user')->field('*')->select();
上面的用法是等效的,都相当于执行SQL:
SELECT * FROM table
但是这并不是我说的获取所有字段,而是显式的调用所有字段(对于对性能要求比较高的系统,这个要求并不过分,起码是一个比较好的习惯),下面的用法可以完成预期的作用:
Db::table('think_user')->field(true)->select();
field(true)的用法会显式的获取数据表的所有字段列表,哪怕你的数据表有100个字段。
字段排除
如果我希望获取排除数据表中的content字段(文本字段的值非常耗内存)之外的所有字段值,我们就可以使用field方法的排除功能,例如下面的方式就可以实现所说的功能:
Db::table('think_user')->field('content',true)->select();
则表示获取除了content之外的所有字段,要排除更多的字段也可以:
Db::table('think_user')->field('user_id,content',true)->select(); //或者用 Db::table('think_user')->field(['user_id','content'],true)->select();
注意的是 字段排除功能不支持跨表和join操作。
用于写入
除了查询操作之外,field方法还有一个非常重要的安全功能–字段合法性检测。field方法结合数据库的写入方法使用就可以完成表单提交的字段合法性检测,如果我们在表单提交的处理方法中使用了:
Db::table('think_user')->field('title,email,content')->insert($data);
即表示表单中的合法字段只有title,email和content字段,无论用户通过什么手段更改或者添加了浏览器的提交字段,都会直接屏蔽。因为,其他是所有字段我们都不希望由用户提交来决定,你可以通过自动完成功能定义额外的字段写入。
在开启数据表字段严格检查的情况下,提交了非法字段会抛出异常,可以在数据库设置文件中设置:
// 关闭严格字段检查 'fields_strict' => false,
V5.0.17+版本开始,系统还提供了fieldRaw方法,用于更安全的指定字段,尤其是使用了SQL函数的情况。
Db::table('think_user')->fieldRaw('title,email,content,max(score) as max_score')->insert($data);
order
order方法属于模型的连贯操作方法之一,用于对操作的结果排序。
用法如下:
Db::table('think_user')->where('status=1')->order('id desc')->limit(5)->select();
注意:连贯操作方法没有顺序,可以在select方法调用之前随便改变调用顺序。
支持对多个字段的排序,例如:
Db::table('think_user')->where('status=1')->order('id desc,status')->limit(5)->select();
如果没有指定desc或者asc排序规则的话,默认为asc。
如果你的字段和mysql关键字有冲突,那么建议采用数组方式调用,例如:
Db::table('think_user')->where('status=1')->order(['order','id'=>'desc'])->limit(5)->select();
V5.0.17+版本开始,当你的order排序中使用了SQL函数的时候,请使用orderRaw方法替代order,例如:
Db::table('think_user')->where('status=1')->orderRaw('rand()')->limit(5)->select();
limit
limit方法也是模型类的连贯操作方法之一,主要用于指定查询和操作的数量,特别在分页查询的时候使用较多。ThinkPHP的limit方法可以兼容所有的数据库驱动类的。
限制结果数量
例如获取满足要求的10个用户,如下调用即可:
Db::table('think_user') ->where('status=1') ->field('id,name') ->limit(10) ->select();
limit方法也可以用于写操作,例如更新满足要求的3条数据:
Db::table('think_user') ->where('score=100') ->limit(3) ->update(['level'=>'A']);
分页查询
用于文章分页查询是limit方法比较常用的场合,例如:
Db::table('think_article')->limit('10,25')->select();
表示查询文章数据,从第10行开始的25条数据(可能还取决于where条件和order排序的影响 这个暂且不提)。
你也可以这样使用,作用是一样的:
Db::table('think_article')->limit(10,25)->select();
对于大数据表,尽量使用limit限制查询结果,否则会导致很大的内存开销和性能问题。
page
page方法也是模型的连贯操作方法之一,是完全为分页查询而诞生的一个人性化操作方法。
page方法是更人性化的进行分页查询的方法,例如还是以文章列表分页为例来说,如果使用limit方法,我们要查询第一页和第二页(假设我们每页输出10条数据)写法如下:
// 查询第一页数据 Db::table('think_article')->limit('0,10')->select(); // 查询第二页数据 Db::table('think_article')->limit('10,10')->select();
虽然利用扩展类库中的分页类Page可以自动计算出每个分页的limit参数,但是如果要自己写就比较费力了,如果用page方法来写则简单多了,例如:
// 查询第一页数据 Db::table('think_article')->page('1,10')->select(); // 查询第二页数据 Db::table('think_article')->page('2,10')->select();
显而易见的是,使用page方法你不需要计算每个分页数据的起始位置,page方法内部会自动计算。
和limit方法一样,page方法也支持2个参数的写法,例如:
Db::table('think_article')->page(1,10)->select(); // 和下面的用法等效 Db::table('think_article')->page('1,10')->select();
page方法还可以和limit方法配合使用,例如:
Db::table('think_article')->limit(25)->page(3)->select();
当page方法只有一个值传入的时候,表示第几页,而limit方法则用于设置每页显示的数量,也就是说上面的写法等同于:
Db::table('think_article')->page('3,25')->select();
group
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_score GROUP BY user_id,test_time
having
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
join
join通常有下面几种类型,不同类型的join操作会影响返回的数据结果。
- INNER JOIN: 等同于 JOIN(默认的JOIN类型),如果表中有至少一个匹配,则返回行
- LEFT JOIN: 即使右表中没有匹配,也从左表返回所有的行
- RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行
- FULL JOIN: 只要其中一个表中存在匹配,就返回行
说明
object join ( mixed join [, mixed $condition = null [, string $type = 'INNER']] )
JOIN方法也是连贯操作方法之一,用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
参数
join要关联的(完整)表名以及别名
支持三种写法:
写法1:[ '完整表名或者子查询'=>'别名' ] 写法2:'完整表名 别名' 写法3:'不带数据表前缀的表名'
condition
关联条件。可以为字符串或数组, 为数组时每一个元素都是一个关联条件。
type
关联类型。可以为:INNER、LEFT、RIGHT、FULL,不区分大小写,默认为INNER。
返回值
模型对象
举例 :
Db::table('think_artist') ->alias('a') ->join('think_work w','a.id = w.artist_id') ->join('think_card c','a.card_id = c.id') ->select();
$join = [ ['think_work w','a.id=w.artist_id'], ['think_card c','a.card_id=c.id'], ]; Db::table('think_user')->alias('a')->join($join)->select();
以上三种写法的效果一样,__WORK__和 __CARD__在最终解析的时候会转换为 think_work和 think_card。注意:'表名’这种方式中间的表名需要用大写
如果不想使用别名,后面的条件就要使用表全名,可以使用下面这种方式
Db::table('think_user')->join('__WORK__','__ARTIST__.id = __WORK__.artist_id')->select();
默认采用INNER JOIN 方式,如果需要用其他的JOIN方式,可以改成
Db::table('think_user')->alias('a')->join('word w','a.id = w.artist_id','RIGHT')->select();
表名也可以是一个子查询
$subsql = Db::table('think_work')->where(['status'=>1])->field('artist_id,count(id) count')->group('artist_id')->buildSql(); Db::table('think_user')->alias('a')->join([$subsql=> 'w'], 'a.artist_id = w.artist_id')->select();
因buildSql返回的语句带有(),所以这里不需要在两端再加上()。
union
UNION操作用于合并两个或多个 SELECT 语句的结果集。
使用示例:
Db::field('name') ->table('think_user_0') ->union('SELECT name FROM think_user_1') ->union('SELECT name FROM think_user_2') ->select();
闭包用法:
Db::field('name') ->table('think_user_0') ->union(function($query){ $query->field('name')->table('think_user_1'); }) ->union(function($query){ $query->field('name')->table('think_user_2'); }) ->select();
或者
Db::field('name') ->table('think_user_0') ->union(['SELECT name FROM think_user_1','SELECT name FROM think_user_2']) ->select();
支持UNION ALL 操作,例如:
Db::field('name') ->table('think_user_0') ->union('SELECT name FROM think_user_1',true) ->union('SELECT name FROM think_user_2',true) ->select();
或者
Db::field('name') ->table('think_user_0') ->union(['SELECT name FROM think_user_1','SELECT name FROM think_user_2'],true) ->select();
每个union方法相当于一个独立的SELECT语句。
注意:UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。
distinct
DISTINCT 方法用于返回唯一不同的值 。
以下代码会返回user_login字段不同的数据
Db::table('think_user')->distinct(true)->field('user_login')->select();
生成的SQL语句是: SELECT DISTINCT user_login FROM think_user
返回以下数组
array(2) { [0] => array(1) { ["user_login"] => string(7) "chunice" } [1] => array(1) { ["user_login"] => string(5) "admin" } }
distinct方法的参数是一个布尔值。
lock
Lock方法是用于数据库的锁机制,如果在查询或者执行操作的时候使用:
Db::name('user')->where('id',1)->lock(true)->find();
就会自动在生成的SQL语句最后加上 FOR UPDATE或者FOR UPDATE NOWAIT(Oracle数据库)。
lock方法支持传入字符串用于一些特殊的锁定要求,例如:
Db::name('user')->where('id',1)->lock('lock in share mode')->find();
cache
cache方法用于查询缓存操作,也是连贯操作方法之一。
cache可以用于select、find、value和column方法,以及其衍生方法,使用cache方法后,在缓存有效期之内不会再次进行数据库查询操作,而是直接获取缓存中的数据,关于数据缓存的类型和设置可以参考缓存部分。
下面举例说明,例如,对find方法使用cache方法如下:
Db::table('think_user')->where('id=5')->cache(true)->find();
第一次查询结果会被缓存,第二次查询相同的数据的时候就会直接返回缓存中的内容,而不需要再次进行数据库查询操作。
默认情况下, 缓存有效期是由默认的缓存配置参数决定的,但cache方法可以单独指定,例如:
Db::table('think_user')->cache(true,60)->find(); // 或者使用下面的方式 是等效的 Db::table('think_user')->cache(60)->find();
表示对查询结果的缓存有效期60秒。
cache方法可以指定缓存标识:
Db::table('think_user')->cache('key',60)->find();
指定查询缓存的标识可以使得查询缓存更有效率。
这样,在外部就可以通过\think\Cache类直接获取查询缓存的数据,例如:
$result = Db::table('think_user')->cache('key',60)->find(); $data = \think\Cache::get('key');
缓存自动更新
这里的缓存自动更新是指一旦数据更新或者删除后会自动清理缓存(下次获取的时候会自动重新缓存)。
当你删除或者更新数据的时候,可以使用cache方法手动更新(清除)缓存,例如:
Db::table('think_user')->cache('user_data')->select([1,3,5]); Db::table('think_user')->cache('user_data')->update(['id'=>1,'name'=>'thinkphp']); Db::table('think_user')->cache('user_data')->select([1,5]);
最后查询的数据不会受第一条查询缓存的影响,确保查询和更新或者删除使用相同的缓存标识才能自动清除缓存。
如果使用find方法并且使用主键查询的情况,不需要指定缓存标识,会自动清理缓存,例如:
Db::table('think_user')->cache(true)->find(1); Db::table('think_user')->update(['id'=>1,'name'=>'thinkphp']); Db::table('think_user')->cache(true)->find(1);
最后查询的数据会是更新后的数据。
comment
COMMENT方法 用于在生成的SQL语句中添加注释内容,例如:
Db::table('think_score')->comment('查询考试前十名分数') ->field('username,score') ->limit(10) ->order('score desc') ->select();
最终生成的SQL语句是:
SELECT username,score FROM think_score ORDER BY score desc LIMIT 10 /* 查询考试前十名分数 */
fetchSql
fetchSql用于直接返回SQL而不是执行查询,适用于任何的CURD操作方法。 例如:
$result = Db::table('think_user')->fetchSql(true)->find(1);
输出result结果为: SELECT * FROM think_user where id = 1
**force **
force 方法用于数据集的强制索引操作,例如:
Db::table('think_user')->force('user')->select();
对查询强制使用user索引,user必须是数据表实际创建的索引名称。
bind
bind方法用于手动参数绑定,大多数情况,无需进行手动绑定,系统会在查询和写入数据的时候自动使用参数绑定。
bind方法用法如下:
// 用于查询 Db::table('think_user') ->where('id',':id') ->where('name',':name') ->bind(['id'=>[10,\PDO::PARAM_INT],'name'=>'thinkphp']) ->select(); // 用于写入 Db::table('think_user') ->bind(['id'=>[10,\PDO::PARAM_INT],'email'=>'[email protected]','name'=>'thinkphp']) ->where('id',':id') ->update(['name'=>':name','email'=>':email']);
partition
partition 方法用于是数据库水平分表
partition($data, $field, $rule); // $data 分表字段的数据 // $field 分表字段的名称 // $rule 分表规则
注意:不要使用任何 SQL 语句中会出现的关键字当表名、字段名,例如 order 等。会导致数据模型拼装 SQL 语句语法错误。
partition 方法用法如下:
// 用于写入 $data = [ 'user_id' => 110, 'user_name' => 'think' ]; $rule = [ 'type' => 'mod', // 分表方式 'num' => 10 // 分表数量 ]; Db::name('log') ->partition(['user_id' => 110], "user_id", $rule) ->insert($data); // 用于查询 Db::name('log') ->partition(['user_id' => 110], "user_id", $rule) ->where(['user_id' => 110]) ->select();
strict
strict方法用于设置是否严格检查字段名,用法如下:
// 关闭字段严格检查 Db::name('user') ->strict(false) ->insert($data);
注意,系统默认值是由数据库配置参数fields_strict决定,因此修改数据库配置参数可以进行全局的严格检查配置,如下:
// 关闭严格检查字段是否存在 'fields_strict' => false,
如果开启字段严格检查的话,在更新和写入数据库的时候,一旦存在非数据表字段的值,则会抛出异常。
failException
failException设置查询数据为空时是否需要抛出异常,如果不传入任何参数,默认为开启,用于select和find方法,例如:
// 数据不存在的话直接抛出异常 Db::name('blog') ->where(['status' => 1]) ->failException() ->select(); // 数据不存在返回空数组 不抛异常 Db::name('blog') ->where(['status' => 1]) ->failException(false) ->select();
或者可以使用更方便的查空报错
// 查询多条 Db::name('blog') ->where(['status' => 1]) ->selectOrFail(); // 查询单条 Db::name('blog') ->where(['status' => 1]) ->findOrFail();
sequence
sequence方法用于pgsql数据库指定自增序列名,其它数据库不必使用,用法为:
Db::name('user')->sequence('id')->insert(['name'=>'thinkphp']);
-
聚合查询
在应用中我们经常会用到一些统计数据,例如当前所有(或者满足某些条件)的用户数、所有用户的最大积分、用户的平均成绩等等,TP为这些统计操作提供了一系列的内置方法,包括:
获取用户数:
Db::table('think_user')->count();
// 助手函数
db('user')->count();
或者根据字段统计:
Db::table('think_user')->count('id');
// 助手函数
db('user')->count('id');
获取用户的最大积分:
Db::table('think_user')->max('score');
// 助手函数
db('user')->max('score');
获取积分大于0的用户的最小积分:
Db::table('think_user')->where('score>0')->min('score');
// 助手函数
db('user')->where('score>0')->min('score');
获取用户的平均积分:
Db::table('think_user')->avg('score');
// 助手函数
db('user')->avg('score');
统计用户的总成绩:
Db::table('think_user')->sum('score');
// 助手函数
db('user')->sum('score');
-
时间查询
时间比较
使用where方法
where方法支持时间比较,例如:
// 大于某个时间 where('create_time','> time','2016-1-1'); // 小于某个时间 where('create_time','<= time','2016-1-1'); // 时间区间查询 where('create_time','between time',['2015-1-1','2016-1-1']);
第三个参数可以传入任何有效的时间表达式,会自动识别你的时间字段类型,支持的时间类型包括timestamps、datetime、date和int。
使用whereTime方法
whereTime方法提供了日期和时间字段的快捷查询,示例如下:
// 大于某个时间 Db::table('think_user')->whereTime('birthday', '>=', '1970-10-1')->select(); // 小于某个时间 Db::table('think_user')->whereTime('birthday', '<', '2000-10-1')->select(); // 时间区间查询 Db::table('think_user')->whereTime('birthday', 'between', ['1970-10-1', '2000-10-1'])->select(); // 不在某个时间区间 Db::table('think_user')->whereTime('birthday', 'not between', ['1970-10-1', '2000-10-1'])->select();
时间表达式
还提供了更方便的时间表达式查询,例如:
// 获取今天的博客 Db::table('think_blog') ->whereTime('create_time', 'today')->select(); // 获取昨天的博客 Db::table('think_blog')->whereTime('create_time', 'yesterday')->select(); // 获取本周的博客 Db::table('think_blog')->whereTime('create_time', 'week')->select(); // 获取上周的博客 Db::table('think_blog')->whereTime('create_time', 'last week')->select(); // 获取本月的博客 Db::table('think_blog')->whereTime('create_time', 'month')->select(); // 获取上月的博客 Db::table('think_blog')->whereTime('create_time', 'last month')->select(); // 获取今年的博客 Db::table('think_blog')->whereTime('create_time', 'year')->select(); // 获取去年的博客 Db::table('think_blog')->whereTime('create_time', 'last year')->select();
如果查询当天、本周、本月和今年的时间,还可以简化为:
// 获取今天的博客 Db::table('think_blog')->whereTime('create_time', 'd')->select(); // 获取本周的博客 Db::table('think_blog')->whereTime('create_time', 'w')->select(); // 获取本月的博客 Db::table('think_blog')->whereTime('create_time', 'm')->select(); // 获取今年的博客 Db::table('think_blog')->whereTime('create_time', 'y') ->select();
V5.0.5+版本开始,还可以使用下面的方式进行时间查询
// 查询两个小时内的博客 Db::table('think_blog')->whereTime('create_time','-2 hours')->select();
-
高级查询
快捷查询
快捷查询方式是一种多字段相同查询条件的简化写法,可以进一步简化查询条件的写法,在多个字段之间用|分割表示OR查询,用&分割表示AND查询,可以实现下面的查询,例如:
Db::table('think_user') ->where('name|title','like','thinkphp%') ->where('create_time&update_time','>',0) ->find();
生成的查询SQL是:
SELECT * FROM `think_user` WHERE ( `name` LIKE 'thinkphp%' OR `title` LIKE 'thinkphp%' ) AND ( `create_time` > 0 AND `update_time` > 0 ) LIMIT 1
快捷查询支持所有的查询表达式。
区间查询
区间查询是一种同一字段多个查询条件的简化写法,例如:
Db::table('think_user') ->where('name',['like','thinkphp%'],['like','%thinkphp']) ->where('id',['>',0],['<>',10],'or') ->find();
生成的SQL语句为:
SELECT * FROM `think_user` WHERE ( `name` LIKE 'thinkphp%' AND `name` LIKE '%thinkphp' ) AND ( `id` > 0 OR `id` <> 10 ) LIMIT 1
区间查询的查询条件必须使用数组定义方式,支持所有的查询表达式。
下面的查询方式是错误的:
Db::table('think_user') ->where('name',['like','thinkphp%'],['like','%thinkphp']) ->where('id',5,['<>',10],'or') ->find();
批量查询
可以进行多个条件的批量条件查询定义,例如:
Db::table('think_user') ->where([ 'name' => ['like','thinkphp%'], 'title' => ['like','%thinkphp'], 'id' => ['>',0], 'status'=> 1 ]) ->select();
生成的SQL语句为:
SELECT * FROM `think_user` WHERE `name` LIKE 'thinkphp%' AND `title` LIKE '%thinkphp' AND `id` > 0 AND `status` = '1'
闭包查询
Db::table('think_user')->select(function($query){ $query->where('name','thinkphp') ->whereOr('id','>',10); });
生成的SQL语句为:
SELECT * FROM `think_user` WHERE `name` = 'thinkphp' OR `id` > 10
使用Query对象查询
也可以事先封装Query对象,并传入select方法,例如:
$query = new \think\db\Query; $query->name('user') ->where('name','like','%think%') ->where('id','>',10) ->limit(10); Db::select($query);
如果使用Query对象的话,select方法之前调用的任何的链式操作都是无效。
混合查询
可以结合前面提到的所有方式进行混合查询,例如:
Db::table('think_user') ->where('name',['like','thinkphp%'],['like','%thinkphp']) ->where(function($query){ $query->where('id',['<',10],['>',100],'or'); }) ->select();
生成的SQL语句是:
SELECT * FROM `think_user` WHERE ( `name` LIKE 'thinkphp%' AND `name` LIKE '%thinkphp' ) AND ( `id` < 10 or `id` > 100 )
字符串条件查询
对于一些实在复杂的查询,也可以直接使用原生SQL语句进行查询,例如:
Db::table('think_user') ->where('id > 0 AND name LIKE "thinkphp%"') ->select();
为了安全起见,我们可以对字符串查询条件使用参数绑定,例如:
Db::table('think_user') ->where('id > :id AND name LIKE :name ',['id'=>0, 'name'=>'thinkphp%']) ->select();
V5.0.4+开始,ThinkPHP支持对同一个字段多次调用查询条件,例如:
Db::table('think_user') ->where('name','like','%think%') ->where('name','like','%php%') ->where('id','in',[1,5,80,50]) ->where('id','>',10) ->find();
快捷方法
-
视图查询
视图查询可以实现不依赖数据库视图的多表查询,并不需要数据库支持视图,例如:
Db::view('User','id,name') ->view('Profile','truename,phone,email','Profile.user_id=User.id') ->view('Score','score','Score.user_id=Profile.id') ->where('score','>',80) ->select();
生成的SQL语句类似于:
SELECT User.id,User.name,Profile.truename,Profile.phone,Profile.email,Score.score FROM think_user User INNER JOIN think_profile Profile ON Profile.user_id=User.id INNER JOIN think_socre Score ON Score.user_id=Profile.id WHERE Score.score > 80
注意,视图查询无需调用table和join方法,并且在调用where和order方法的时候只需要使用字段名而不需要加表名。
默认使用INNER join查询,如果需要更改,可以使用:
Db::view('User','id,name') ->view('Profile','truename,phone,email','Profile.user_id=User.id','LEFT') ->view('Score','score','Score.user_id=Profile.id','RIGHT') ->where('score','>',80) ->select();
生成的SQL语句类似于:
SELECT User.id,User.name,Profile.truename,Profile.phone,Profile.email,Score.score FROM think_user User LEFT JOIN think_profile Profile ON Profile.user_id=User.id RIGHT JOIN think_socre Score ON Score.user_id=Profile.id WHERE Score.score > 80
可以使用别名:
Db::view('User',['id'=>'uid','name'=>'account']) ->view('Profile','truename,phone,email','Profile.user_id=User.id') ->view('Score','score','Score.user_id=Profile.id') ->where('score','>',80) ->select();
生成的SQL语句变成:
SELECT User.id AS uid,User.name AS account,Profile.truename,Profile.phone,Profile.email,Score.score FROM think_user User INNER JOIN think_profile Profile ON Profile.user_id=User.id INNER JOIN think_socre Score ON Score.user_id=Profile.id WHERE Score.score > 80
可以使用数组的方式定义表名以及别名,例如:
Db::view(['think_user'=>'member'],['id'=>'uid','name'=>'account']) ->view('Profile','truename,phone,email','Profile.user_id=member.id') ->view('Score','score','Score.user_id=Profile.id') ->where('score','>',80) ->select();
生成的SQL语句变成:
SELECT member.id AS uid,member.name AS account,Profile.truename,Profile.phone,Profile.email,Score.score FROM think_user member INNER JOIN think_profile Profile ON Profile.user_id=member.id INNER JOIN think_socre Score ON Score.user_id=Profile.id WHERE Score.score > 80
-
子查询
首先构造子查询SQL,可以使用下面三种的方式来构建子查询。
使用select方法
当select方法的参数为false的时候,表示不进行查询只是返回构建SQL,例如:
$subQuery = Db::table('think_user') ->field('id,name') ->where('id','>',10) ->select(false);
生成的subQuery结果为:
SELECT `id`,`name` FROM `think_user` WHERE `id` > 10
使用fetchSql方法
fetchSql方法表示不进行查询而只是返回构建的SQL语句,并且不仅仅支持select,而是支持所有的CURD查询。
$subQuery = Db::table('think_user') ->field('id,name') ->where('id','>',10) ->fetchSql(true) ->select();
生成的subQuery结果为:
SELECT `id`,`name` FROM `think_user` WHERE `id` > 10
使用buildSql构造子查询
$subQuery = Db::table('think_user') ->field('id,name') ->where('id','>',10) ->buildSql();
生成的subQuery结果为:
( SELECT `id`,`name` FROM `think_user` WHERE `id` > 10 )
调用buildSql方法后不会进行实际的查询操作,而只是生成该次查询的SQL语句(为了避免混淆,会在SQL两边加上括号),然后我们直接在后续的查询中直接调用。
需要注意的是,使用前两种方法需要自行添加‘括号’。
然后使用子查询构造新的查询:
Db::table($subQuery.' a') ->where('a.name','like','thinkphp') ->order('id','desc') ->select();
生成的SQL语句为:
SELECT * FROM ( SELECT `id`,`name` FROM `think_user` WHERE `id` > 10 ) a WHERE a.name LIKE 'thinkphp' ORDER BY `id` desc
使用闭包构造子查询
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();
生成的SQL语句是
SELECT * FROM `think_user` WHERE `id` IN ( SELECT `id` FROM `think_profile` WHERE `status` = 1 )
Db::table('think_user') ->where(function($query){ $query->table('think_profile')->where('status',1); },'exists') ->find();
生成的SQL语句为
SELECT * FROM `think_user` WHERE EXISTS ( SELECT * FROM `think_profile` WHERE `status` = 1 )
-
原生查询
Db类支持原生SQL查询操作,主要包括下面两个方法:
query方法
query方法用于执行SQL查询操作,如果数据非法或者查询错误则返回false,否则返回查询结果数据集(同select方法)。
使用示例:
Db::query("select * from think_user where status=1");
如果当前采用了分布式数据库,并且设置了读写分离的话,query方法始终是在读服务器执行,因此query方法对应的都是读操作,而不管你的SQL语句是什么。
execute方法
execute用于更新和写入数据的sql操作,如果数据非法或者查询错误则返回false ,否则返回影响的记录数。
使用示例:
Db::execute("update think_user set name='thinkphp' where status=1");
如果当前采用了分布式数据库,并且设置了读写分离的话,execute方法始终是在写服务器执行,因此execute方法对应的都是写操作,而不管你的SQL语句是什么。
参数绑定
支持在原生查询的时候使用参数绑定,包括问号占位符或者命名占位符,例如:
Db::query("select * from think_user where id=? AND status=?",[8,1]); // 命名绑定 Db::execute("update think_user set name=:name where status=:status",['name'=>'thinkphp','status'=>1]);