mysql5.7.8以后开始原生支持json. 在相似mongodb这种nosql数据库中,json存储数据是很是天然的, 在mysql中合理的使用json,可以带来极大的便利php
在读laravel手册举例子时,咱们常常会看到 $user->is_admin
来判断用户是否为管理员,可是在用户表中,admin每每只占很小一部分.若是单开一个is_admin字段是很没有必要的行为.数据库中会有大量的无心义数据存储, 咱们能够为user表建立一个 json 字段,来存储咱们的is_admin字段mysql
[
{
id: 1,
username: 'weiwenhao',
rest: { // 冗余字段
is_admin: 1
}
},
{
id: 2,
username: 'eienao',
rest: null
}
]
复制代码
固然即便不使用json,咱们也不会使用is_admin来判断是否为管理员.laravel
能够经过新增admin表或者RABC来标志管理员sql
依旧是用户表, 很常见的一个需求是第三方登陆,若是咱们使直接在user表新增facebook_id,facebook_email,facebook_phone_number,google_id,....
字段, 能够预见这会形成大量的无心义数据(即便他们不占用内存,或者影响性能)mongodb
一种解决办法是 使用一对多关系来解决, 既创建一个 第三方登陆表来存储第三方登陆的id/email/phone_number等数据库
可是我更喜欢使用json字段来解决这个问题express
[
{
id: 1,
username: 'weiwenhao',
rest: {
is_admin: 1,
facebook_id: 2348234,
facebook_phone_number: 2834723234,
}
},
{
id: 2,
username: 'eienao',
rest: {
google_id: 2348234,
google_email: xxx@gmail.com
}
}
]
复制代码
能够看出,使用json字段使数据表的设计更加天然,集中,业务也相应的更加的简单方便.json
首先是迁移文件 $table->json('rest')->nullable();
bash
laravel对json的使用进行了必定的优化,对于更新和建立咱们能够.nosql
$user = new User;
$user->{'rest->google_id'} = 'xxx';
# 若是你的rest字段为null,那么上面的操做会使 null 会变成 {google_id: "xxx"}, 不须要再作 是否为null的断定啦
# 若是仅使用上面的插入操做,也不须要在使用模型的修改器来吧 json => array, array => json啦
复制代码
当rest字段的值为null时,批量操做没法执行, 相似
update(['rest->google_id' => 'xxx'])
这样的操做执行无效,所以更推荐上面的方式来进行更新操做
对于查找操做能够方便的使用
User::where('rest->google_id','xxx')->firstOrFail()
关于检索的效率问题,在后面内容中给出解决方案
5.7新增了生成列, 生成列的值是根据列定义中包含的表达式计算得来.官方示例:计算直角三角形的斜边的长度
CREATE TABLE triangle (
sidea DOUBLE,
sideb DOUBLE,
sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) # AS (expression) 为生成列的核心语法
);
INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);
# 对于上面的插入,查询能够获得以下结果
mysql> SELECT * FROM triangle;
+-------+-------+--------------------+
| sidea | sideb | sidec |
+-------+-------+--------------------+
| 1 | 1 | 1.4142135623730951 |
| 3 | 4 | 5 |
| 6 | 8 | 10 |
+-------+-------+--------------------+
复制代码
上面的 sidec的值 是根据sidea和sideb计算得来, 并未实际的存储在磁盘中.mysql5.7以前咱们想要实现上面的需求可能会这样写sql语句
SELECT *,(SQRT(sidea * sidea + sideb * sideb)) as sidec FROM triangle;
复制代码
上面既生成列的主要做用, 实际上生成列有两种子类型,上面的例子属于 virtual (虚拟) 类型的生成列, 其并无将sidec的值实际存储在磁盘中.
除了virtual, 生成列还支持 stored类型,其建立语句为
#...
sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) STORED # stored不指定则默认为 virtual
#...
复制代码
当行建立或者更新时, 会从新计算 sidec并将其存储在磁盘中
生成列的另外一个重要的特性是能够根据生成列表达式的计算结果创建索引. 其创建索引的方式和普通字段建立索引的方式一致.1
CREATE TABLE triangle (
sidea DOUBLE,
sideb DOUBLE,
sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) # AS (expression) 为生成列的核心语法
INDEX(`sidec`)
);
复制代码
索引自己也是存储在磁盘中的实际存在的物质, 所以 virtual 生成列 + 索引,能够达到存储空间的最有效利用.
对于stored 生成列 + 索引, 一般不会访问到存储在磁盘中stored 生成列,而是直接访问索引.所以没有必要使用stored生成列
已user表的rest.google_id为例,建表操做
#...
`rest` json NULL,
# JSON_EXTRACT(`rest`,'$.google_id') 等价于 `rest`->'$.google_id'
# 5.7.13版本后
# JSON_UNQUOTE(JSON_EXTRACT(`rest`,'$.google_id')) 等价于 `rest`->>'$.google_id'
# 使用生成列为json添加索引时,请务必使用 JSON_UNQUOTE(JSON_EXTRACT(`rest`,'$.google_id'))/->>
`google_id` varchar GENERATED ALWAYS AS (`rest`->>'$.google_id')) NULL
UNIQUE INDEX(`google_id`)
#...
复制代码
在laravel迁移文件中
$table->json('rest')->nullable();
$table->string('rest')->nullable()->unique()->virtualAs('`oauth`->>"$.google_id"');
复制代码
有了索引后,当咱们执行查询操做
select * from users where `rest`->'$.google_id' = 'xxx' # 一般使用这种更加简单的形式
select * from users where `rest`->>'$.google_id' = 'xxx'
# 上面两种表达式会被mysql的优化器在查询阶段自动优化为 select * from users where google_id = 'xxx'
复制代码
virtualAs(oauth->"$.google_id"');
使用 **->**符号来建立生成列会出现没法使用索引的状况, 缘由不是很明了,须要继续研究一下手册. 另外对于建立语句 GENERATED ALWAYS
的做用也不是很明了.
关于null, 常常会看到一种言论是mysql中使用null做为字段默认值会出现没法索引的状况.但通过查询了解,发现这是一种老中医理论. 我更倾向于使用null做为默认值, 而不是 ''/0/0.0 ,我认为null的表达性更好, laravel中也无时无刻不在提现这种思想.
关于json的使用, 最近的项目中,我大部分核心表都有一个json字段,作一些非核心数据的存储和冗余. 比