yii框架的query builder提供了以面向对象的方式编写sql语句,允许开发者使用类方法和属性来指定sql语句中的独立部分,并且将这些不同部分组装成一个可以通过调用如上一章节所述的dao方法来执行的sql语句。下面展示了使用query builder来构建select sql语句的典型方法:
$user = yii::app()->db->createcommand()
->select('id, username, profile')
->from('tbl_user u')
->join('tbl_profile p', 'u.id=p.user_id')
->where('id=:id', array(':id'=>$id))
->queryrow();
当你需要组装分块组装sql语句或者基于应用的特殊条件时最好使用query builder. 使用query builder的最大好处是:
可以构建复杂的 sql 语句.
自动引用表名和列名以避免sql保留字及特殊字符的冲突.
如果可能的化也可以使用参数值进行参数绑定, 这样可以减少sql注入攻击.
支持一定程度上的数据库抽象,这样可以简化不同数据库平台间的数据库迁移.
使用query builder并不是强制性的. 实际上, 如果你的查询很简单, 直接使用sql语句会更加简单快捷.
注意: query builder不能修改已存在的指定sql语句的查询,例如, 下面的代码是无效的:
$command = yii::app()->db->createcommand('select * from tbl_user');
// the following line will not append where clause to the above sql
$command->where('id=:id', array(':id'=>$id));
换句话说, 不要修复原生sql和sql和query builder的使用.
1. 准备 query builderyii框架的query builder 以cdbcommand的形式支持, 这个数据库查询类在dao这一章节中已经描述过.
在使用 query builder 之前,我们先要创建 cdbcommand 的实例,如下所示,
$command = yii::app()->db->createcommand();
也就是说我们使用 yii::app()->db 来获取 db 连接, 然后调用 cdbconnection::createcommand() 来创建所需的command 实例.
注意我们并没有像在dao中一样将整个sql语句传入createcommand()方法中调用,而是没有传入任何参数. 这是因为我们接下来将会使用query builder 的方法来构建独立的sql语句.
2. 构建数据检索查询数据检索查询使用 select 语句. query builder提供了一系列的方法来构建独立的select语句.因为这些方法返回的都是 cdbcommand 实例, 所以我们可以使用方法链, 正如我们这一章节的开头所示那样.
select(): 指定查询的select 部分
selectdistinct(): 指定查询的select 部分并且开启distinct标识符
from(): 指定查询的from部分
where(): 指定查询的where部分
andwhere(): 使用and操作符添加条件到查询的 where 部分
orwhere(): 使用or操作符添加条件到查询的 where 部分
join(): 添加内联查询
leftjoin(): 添加左外联接
rightjoin():添加右外联接查询
crossjoin(): 添加交叉联接查询
naturaljoin(): 添加自然联接查询
group(): 指定查询的group by部分
having(): 指定查询的having部分
order(): 指定查询的 order by 部分
limit(): 指定查询的limit 部分
offset(): 指定查询的 offset 部分
union(): 添加 union 查询
接下来我们会阐述如何使用这些构建方法. 简单起见, 我们假设使用的数据库是mysql. 注意如果你使用的是其他dbms, 示例中table/column/value 的引用可能会不一样.
select()
function select($columns='*')
select() 方法指定查询的select 部分. $columns 参数指定被查询的列, 可以是以逗号分割的字符串, 也可以是包含列名的数组.列名可以包含表前缀及列的别名. 这个方法会自动引用列名,除非该列是db expression.
下面是示例:
// select *
select()
// select `id`, `username`
select('id, username')
// select `tbl_user`.`id`, `username` as `name`
select('tbl_user.id, username as name')
// select `id`, `username`
select(array('id', 'username'))
// select `id`, count(*) as num
select(array('id', 'count(*) as num'))
selectdistinct()
function selectdistinct($columns)
selectdistinct() 和select()类似,不同之处是前者开启了 distinct . 例如,selectdistinct('id, username') 会生成如下sql:
select distinct `id`, `username`
from()
function from($tables)
from() 方法指定了查询的 from 部分. $tables 参数指定了要查询的表,它既可以是一个以逗号分隔的字符串表名,也可以是包含表名的数组.表名可能包含作用域前缀 (e.g. public.tbl_user) 和表别名 (e.g.tbl_user u). 这个方法会自动引用表名,除非包含插入语句 (表名以子查询或者 db expression的形式提供).
下面是一些示例:
// from `tbl_user`
from('tbl_user')
// from `tbl_user` `u`, `public`.`tbl_profile` `p`
from('tbl_user u, public.tbl_profile p')
// from `tbl_user`, `tbl_profile`
from(array('tbl_user', 'tbl_profile'))
// from `tbl_user`, (select * from tbl_profile) p
from(array('tbl_user', '(select * from tbl_profile) p'))
where()
function where($conditions, $params=array())
where() 方法指定了查询的 where 部分. $conditions 指定查询的条件,$params 指定查询条件中的参数.e $conditions参数可能是一个字符串也可能是数组如下所示:
array(operator, operand1, operand2, ...)
里面的 operator 可以下列所示的任意一个:
and: 操作数通过 and连接到一起. 例如, array('and', 'id=1', 'id=2') 将会生成 id=1 and id=2. 如果一个操作数是数组, 那么它将会通过同样的规则转化为字符串. 例如, array('and', 'type=1', array('or', 'id=1', 'id=2')) 将会生成 type=1 and (id=1 or id=2). 这个方法不会做任何引用和转义.
or: 和 and 操作符类似,不同之处在于操作数使用or连接到一起.
in: 第一个操作数operand1应该是一个列名或者db expression, 第二个操作数operand2是一个表示该列或db expression所在范围的数组, array('in', 'id', array(1,2,3)) 将会生成 id in (1,2,3). 这个方法将会引用列名并转义取值范围中的值.
not in: 和in 操作符类似,不同之处是在生成条件语句中将 in 换成 not in .
like: 操作符operand 1应该是一个列或者 db expression, 操作符operand 2是一个表示与列或者db expression相似的字符串或数组. 例如,array('like', 'name', '%tester%') 将会生成 name like '%tester%'. 当值的范围是数组形式的时候, 多个 like 语句会通过and 连接起来. 例如, array('like', 'name', array('%test%', '%sample%')) 将生成 name like '%test%' and name like '%sample%'. 这个方法将会引用列名并转义取值范围中的值.
not like: 和 like 操作符类似
or like: 和 like 操作符类似,不同之处是多个like之间使用 or 连接.
or not like: 和 not like 操作符类似.
下面是使用 where的一些示例:
// where id=1 or id=2
where('id=1 or id=2')
// where id=:id1 or id=:id2
where('id=:id1 or id=:id2', array(':id1'=>1, ':id2'=>2))
// where id=1 or id=2
where(array('or', 'id=1', 'id=2'))
// where id=1 and (type=2 or type=3)
where(array('and', 'id=1', array('or', 'type=2', 'type=3')))
// where `id` in (1, 2)
where(array('in', 'id', array(1, 2))
// where `id` not in (1, 2)
where(array('not in', 'id', array(1,2)))
// where `name` like '%qiang%'
where(array('like', 'name', '%qiang%'))
// where `name` like '%qiang' and `name` like '%xue'
where(array('like', 'name', array('%qiang', '%xue')))
// where `name` like '%qiang' or `name` like '%xue'
where(array('or like', 'name', array('%qiang', '%xue')))
// where `name` not like '%qiang%'
where(array('not like', 'name', '%qiang%'))
// where `name` not like '%qiang%' or `name` not like '%xue%'
where(array('or not like', 'name', array('%qiang%', '%xue%')))
值得注意的是当操作符包含like的时候,我们必须在patterns明确指定通配符字符串 (such as % and _). 如果 patterns 来自用户输入,我们还要使用下面的代码来转义特殊字符以避免被作为通配符处理:
$keyword=$_get['q'];
// escape % and _ characters
$keyword=strtr($keyword, array('%'=>'\%', '_'=>'\_'));
$command->where(array('like', 'title', '%'.$keyword.'%'));
andwhere()
function andwhere($conditions, $params=array())
addwhere() 方法通过and操作符添加额外的条件到查询的where部分,这个方法和行为和where()几乎一样,不同之处是它追加条件而不是替换.
orwhere()
function orwhere($conditions, $params=array())
orwhere() 方法通过or操作符添加额外的条件到查询的where部分, 这个方法和行为和where()几乎一样,不同之处是它追加条件而不是替换.
order()
function order($columns)
order() 方法指定查询的 order by 部分. $columns 参数指定要排序的列, 可以是以逗号分隔的列和排序方向(ascor desc)字符串,或者包含列和排序方向的数组. 列名可能包含表前缀. 该方法会自动引用列名,除非该列名是一些插入语句 (如db expression).
下面是一些示例:
// order by `name`, `id` desc
order('name, id desc')
// order by `tbl_profile`.`name`, `id` desc
order(array('tbl_profile.name', 'id desc'))
limit() 和 offset()
function limit($limit, $offset=null)
function offset($offset)
limit() 和 offset() 方法指定查询的 limit 和 offset 部分. 注意一些 dbms 可能不支持 limit 和 offset 语法. 在这种情况下, query builder 将会重写整个 sql 语句来模拟limit 和 offset的功能.
下面是一些示例:
// limit 10
limit(10)
// limit 10 offset 20
limit(10, 20)
// offset 20
offset(20)
join() 及其变种
function join($table, $conditions, $params=array())
function leftjoin($table, $conditions, $params=array())
function rightjoin($table, $conditions, $params=array())
function crossjoin($table)
function naturaljoin($table)
join() 方法及其变种指定了使用 inner join, left outer join,right outer join, cross join, 或者 natural join联接其他表进行查询. $table 参数指定了要联接的表名. 表名可以包含作用域前缀及别名. 这个方法将会引用表名(除非表名是db expression或者子查询). $conditions参数指定了联接条件,它的语法和where()类似. $params 指定了整个查询的参数绑定.
注意不同于其他 query builder 方法, 每一次调用 join 方法都会追加到前一个上去.
下面是一些示例:
// join `tbl_profile` on user_id=id
join('tbl_profile', 'user_id=id')
// left join `pub`.`tbl_profile` `p` on p.user_id=id and type=1
leftjoin('pub.tbl_profile p', 'p.user_id=id and type=:type', array(':type'=>1))
group()
function group($columns)
group() 方法指定查询的 group by 部分. $columns 参数指定分组的列, 可以是以逗号分隔的列字符串, 也可以是列数组。列名可能包含表前缀. 这个方法会自动引用列名,除非列是插入语句(which means the column is given as a db expression).
下面是一些示例:
// group by `name`, `id`
group('name, id')
// group by `tbl_profile`.`name`, `id`
group(array('tbl_profile.name', 'id'))
having()
function having($conditions, $params=array())
having() 方法指定了查询的 having 部分. 它的使用 where()类似.
下面是一些示例:
// having id=1 or id=2
having('id=1 or id=2')
// having id=1 or id=2
having(array('or', 'id=1', 'id=2'))
union()
function union($sql)
union() 方法指定了查询的 union 部分. 它使用union操作符追加 $sql 到已存在的 sql. 调用 union() 多次将会追加多个 sqls 到已存在的sql上.
下面是一些示例:
// union (select * from tbl_profile)
union('select * from tbl_profile')
执行查询在调用上述的查询构建方法构建一个查询之后, 我们可以调用如上一章节dao中所述的dao方法来执行查询. 例如, 我们可以调用 cdbcommand::queryrow() 来获取一行的结果, 或者 cdbcommand::queryall() 来一次获取所有结果. 例子:
$users = yii::app()->db->createcommand()
->select('*')
->from('tbl_user')
->queryall();
检索sql除了执行 query builder构建的查询之外, 我们还可以检索相应sql语句的结果. 这可以通过调用 cdbcommand::gettext()方法来解决.
$sql = yii::app()->db->createcommand()
->select('*')
->from('tbl_user')
->text;
如果有多个参数绑定到一个查询上, 那么它们可以通过 cdbcommand::paramsproperty来获取结果.
构建查询的可选语法有时候, 使用方法链来构建查询并不是一个明智的选择. yii框架的 query builder允许一个查询使用简单对象属性赋值的方式来构建 . 特别地,对每一个查询构建方法,有一个同名的相应属性. 赋值到该属性等价于调用相应的方法. 例如,下面的两个语句是等价的($command 代表 cdbcommand 对象):
$command->select(array('id', 'username'));
$command->select = array('id', 'username');
此外, cdbconnection::createcommand()方法可以传入一个数组参数. 数组中的键值对会被用来初始化已创建的cdbcommand实例的属性.这意味着,我们可以使用下面的代码来构建查询:
$row = yii::app()->db->createcommand(array(
'select' => array('id', 'username'),
'from' => 'tbl_user',
'where' => 'id=:id',
'params' => array(':id'=>1),
))->queryrow();
构建多个查询一个dbcommand 实例可以被多次复用来构建多个查询. 在构建一个新的查询之前一定要调用 cdbcommand::reset() 方法来清除上一个查询. 例如:
$command = yii::app()->db->createcommand();
$users = $command->select('*')->from('tbl_users')->queryall();
$command->reset(); // clean up the previous query
$posts = $command->select('*')->from('tbl_posts')->queryall();
3. 构建数据操纵(增、删、改)sql语句数据操纵查询指的是sql语句中数据库表格记录的插入、更新和删除. 与之相应的,查询构建器提供了insert,update和delete方法. 不同于 select 查询方法,每一个数据操纵查询方法都会构建一个完整的sql语句,并且立即执行.
insert(): 在数据表中插入一行
update(): 更新数据表数据
delete(): 从数据表删除数据
下面我们阐述数据操纵查询方法.
insert()
function insert($table, $columns)
insert() 方法构建并执行一个 insert sql 语句. $table参数指定要插入的数据表, $columns是一个键值对数组,指定了插入的列及对应的值. 这个方法会引用表名并且使用绑定参数.
below is an example:
// build and execute the following sql:
// insert into `tbl_user` (`name`, `email`) values (:name, :email)
$command->insert('tbl_user', array(
'name'=>'tester',
'email'=>'tester@example.com',
));
update()
function update($table, $columns, $conditions='', $params=array())
update() 方法构建并执行一个update sql语句. $table参数指定要更新的表; $columns是一个键值对数组,该数组指定了要更新的列及其对应的值;$conditions 和 $params 和 where()中的类似, 指定了update语句中的 where部分. 这个方法会自动引用相应的列名并且使用参数绑定.
below is an example:
// build and execute the following sql:
// update `tbl_user` set `name`=:name where id=:id
$command->update('tbl_user', array(
'name'=>'tester',
), 'id=:id', array(':id'=>1));
delete()
function delete($table, $conditions='', $params=array())
delete() 方法构建并执行一个delete sql语句. $table参数指定要删除的表; $conditions 和 $params 和 where()中的类似, 指定了delete语句中的 where部分. 这个方法会自动引用相应的列名.
下面是示例:
// build and execute the following sql:
// delete from `tbl_user` where id=:id
$command->delete('tbl_user', 'id=:id', array(':id'=>1));
4. 构建数据表操作sql语句除了正常的数据取回和操纵查询, query builder 还提供了一系列的方法来构建和执行操作数据库层面的sql操作. 特殊地,它支持下列查询:
createtable(): 创建表
renametable(): 重命名表
droptable(): 删除表
truncatetable(): 清空表
addcolumn(): 新增表的列
renamecolumn(): 重命名表的列
altercolumn(): 修改表的列
addforeignkey(): 添加外键(版本1.1.6起生效)
dropforeignkey(): 删除外键 (版本1.1.6起生效)
dropcolumn(): 删除表列
createindex(): 创建索引
dropindex(): 删除索引
info: 尽管实际的sql语句在不同的dbms中操作数据库的方法不尽相同,但是查询构建器试图提供一个统一接口来构建查询。这极大简化了数据库迁移时带来的麻烦。
抽象数据类型query builder 介绍了一系列抽象数据类型用来定义数据表的列. 不同于物理数据类型用来指定特殊的dbms而且在不同的dbms中不一样,这里的抽象数据类型独立于dbms. 当抽象数据类型用于定义表的列时,查询构建器将会将其转化为相应的物理数据类型.
下面的抽象数据类型都被 query builder 所支持.
pk: 一个通用的主键类型, 在mysql中将会被转化为 int(11) not null auto_increment primary key;
string: 字符串类型, 在mysql中将会被转化为 varchar(255);
text: 文本类型 (长字符串), 在mysql中将会被抓化为 text;
integer: 整型, 在mysql中将会被转化为 int(11);
float: 浮点数类型, 在mysql中将会被转化为will be converted into float;
decimal: 小数类型, 在mysql中将会被转化为 decimal;
datetime: 日期时间类型, 在mysql中将会被转化为 datetime ;
timestamp: 时间戳类型,在mysql中将会被转化为 timestamp ;
time: 时间类型, 在mysql中将会被转化为 time;
date: 日期类型, 在mysql中将会被转化为 date;
binary: 二进制数据类型, 在mysql中将会被转化为 blob;
boolean: 布尔类型, 在mysql中将会被转化为tinyint(1);
money: 金钱/货币类型, 在mysql中将会被转化为 decimal(19,4). 这个类型从yii版本1.1.8起生效.
createtable()
function createtable($table, $columns, $options=null)
createtable() 方法构建和执行创建数据表的 sql语句. $table参数指定了要创建的表名. $columns 参数指定了新表中的列. 它们必须定义为 name-definition 对 (e.g. 'username'=>'string'). $options 参数指定任意额外的应该追加到已生成的sql语句的sql 片段 . query builder 将会引用表名和合适的列名.
当指定一个列定义时,可以使用上述所叙的抽象数据类型. query builder 会基于当前使用的dbms转化抽象数据类型为相应的物理数据类型。
一个列定可以包含非抽象数据类型或者指定. 它们将会毫无改变的生成 sql 语句。例如, point 不是一个抽象数据类型, 如果用在了列定义中, 它将会出现在结果sql语句中,同时string not null 将会转化为 varchar(255) not null.
下面的例子展示了如何创建新表:
// create table `tbl_user` (
// `id` int(11) not null auto_increment primary key,
// `username` varchar(255) not null,
// `location` point
// ) engine=innodb
createtable('tbl_user', array(
'id' => 'pk',
'username' => 'string not null',
'location' => 'point',
), 'engine=innodb')
renametable()
function renametable($table, $newname)
renametable() 方法构建并执行重命名表的sql语句. $table参数指定了要重命名的表,$newname参数指定了表的新名称. 查询构建器将会引用合适的表名.
下面的示例展示了如何重命名一个表:
// rename table `tbl_users` to `tbl_user`
renametable('tbl_users', 'tbl_user')
droptable()
function droptable($table)
droptable() 方法构建并执行删除表的sql语句. $table 参数指定要删除的表名. query builder将会引用合适的表名.
下面是展示如何删除表的示例:
// drop table `tbl_user`
droptable('tbl_user')
truncatetable()
function truncatetable($table)
truncatetable() 方法会构建并执行清空表的sql语句. $table参数指定要清空的表名.query builder将会引用合适的表名.
下面是展示如何清空表的示例:
// truncate table `tbl_user`
truncatetable('tbl_user')
addcolumn()
function addcolumn($table, $column, $type)
addcolumn() 方法构建并执行 sql语句来添加一个新的列,$table参数指定新增列的表名. $column 参数指定新增列的名称. $type 指定了新增列的定义. 列定义可能包含抽象数据类型, 这在 createtable里已经说明过. query builder 将会引用表名和合适的列名.
下面展示的是如何为表新增列的例子:
// alter table `tbl_user` add `email` varchar(255) not null
addcolumn('tbl_user', 'email', 'string not null')
dropcolumn()
function dropcolumn($table, $column)
dropcolumn() 方法构建并执行删除表的列的sql语句. $table参数指定了要删除列的表名. $column 参数指定了要删除的列名. query builder 将会引用表名和合适的列名.
下面是如何删除表列的例子:
// alter table `tbl_user` drop column `location`
dropcolumn('tbl_user', 'location')
renamecolumn()
function renamecolumn($table, $name, $newname)
renamecolumn() 方法构建并执行重命名表的列的sql语句. $table参数指定了要重命名列的表名. $name参数指定了旧的列名. $newname 参数指定了新的列名. query builder 将会引用表名和合适的列名.
下面展示了如何重命名一个表的列:
// alter table `tbl_users` change `name` `username` varchar(255) not null
renamecolumn('tbl_user', 'name', 'username')
altercolumn()
function altercolumn($table, $column, $type)
altercolumn() 方法构建并执行修改表列的 sql 语句 . $table参数指定了将要被修改列的表名. $column 参数指定了将要被修改的列名.$type 指定了列的新定义,列定义可能包含抽象数据类型. query builder将会引用表名和合适的列名.
下面展示了如何修改一个表的列:
// alter table `tbl_user` change `username` `username` varchar(255) not null
altercolumn('tbl_user', 'username', 'string not null')
addforeignkey()
function addforeignkey($name, $table, $columns,
$reftable, $refcolumns, $delete=null, $update=null)
addforeignkey() 方法构建并执行为一个表新增外键约束的sql语句. $name 参数指定外键名称. $table 和 $columns 参数指定表名和添加外键的列名. 如果有多个列, 它们必须由逗号分隔. $reftable和 $refcolumns 参数指定了外键指向的表名和对应的列名.$delete 和 $update 参数分别指定sql语句中的on delete 和 on update 操作. 大多数 dbms 支持这些操作:restrict, cascade, no action, set default, set null. query builder 将会引用表名,索引名和列名
下面的例子展示了如何该表添加外键约束,
// alter table `tbl_profile` add constraint `fk_profile_user_id`
// foreign key (`user_id`) references `tbl_user` (`id`)
// on delete cascade on update cascade
addforeignkey('fk_profile_user_id', 'tbl_profile', 'user_id',
'tbl_user', 'id', 'cascade', 'cascade')
dropforeignkey()
function dropforeignkey($name, $table)
dropforeignkey() 方法构建和执行删除外键约束的 sql 语句. $name 参数指定了要删除的外键名. $table 参数指定了外键所在的表名. query builder 将会引用表名和合适的约束名.
下面的例子展示了如何删除外键约束:
// alter table `tbl_profile` drop foreign key `fk_profile_user_id`
dropforeignkey('fk_profile_user_id', 'tbl_profile')
createindex()
function createindex($name, $table, $column, $unique=false)
createindex() 方法构建并执行创建索引的 sql语句. $name 参数指定了要创建的索引名称. $table 参数指定了创建索引的表名. $column 参数指定了被索引的列名. $unique参数指定了是否要创建unique索引. 如果索引包含多个列,按么必须通过逗号分隔. query builder 将会引用表名, 索引名和列名.
下面的例子展示了如何创建索引:
// create index `idx_username` on `tbl_user` (`username`)
createindex('idx_username', 'tbl_user', 'username')
dropindex()
function dropindex($name, $table)
dropindex() 方法构建并执行了删除索引的 sql 语句. $name 参数指定要删除的索引名. $table 参数指定了索引所在的表名. query builder 将会引用表名和合适的索引名.
下面的例子展示了如何删除索引:
// drop index `idx_username` on `tbl_user`
dropindex('idx_username', 'tbl_user')
以上就是yii框架官方指南系列24——使用数据库:query builder的内容。