Java艺术
坚持技术分享、坚持原创、深耕后端架构、探索底层实现原理,关注Java艺术,我们在架构师道路上一起成长!
135篇原创内容
Official Account
可能是为了优化查询性能,也可能是做大数据的惯性思维,才会把
MySql
数据库当分析型数仓去使用吧。
本篇分享的内容是:当我们把
MySql
当作列存数据库(
OLAP
分析型数仓)使用时多条件
and
查询如何实现。
假设某个表有
n
列,那么把一行记录按列存储后就需要存约
n-1
行记录。同时,转为列存后每行都需要多出几列标记这一条记录属于哪个“行记录”,以及存储的是“行记录”哪一列的值。
以用户表为例,假设
tb_row_user
表结构如下。
那么转为列存后创建的表
tb_column_user
结构如下。
现在我们往表
tb_row_user
里面插入一条记录。
如果使用列存,则需要往表
tb_column_user
插入如下
3
条记录。
当然,我们还需要为列
user_id
、
column_name
、
column_value
分别创建索引。
现在我们需要查询同时满足年龄大于
25
岁且性别为男的所有用户的
id
,我们应该怎样写
sql
?现在条件只有两个,假如条件有
5
个,
10
个呢?
select user_id,
count(id)
as cnt
from tb_column_user
where
(column_name
=
'age'
and column_value
>
'25')
or
(column_name
=
'sex'
and column_value
=
'男')
group
by user_id
having cnt
=
2;
如果把
group by
去掉,那么
sql
就是查询条件满足
age
大于
25
或者性别是男的用户,即。
select user_id
from tb_column_user
where
(column_name
=
'age'
and column_value
>
'25')
or
(column_name
=
'sex'
and column_value
=
'男');
如果一个用户同时满足这两个条件,那么查询结果同个
user_id
就会有两条记录,否则如果只满足其中一个条件,那么查询结果这个
user_id
就只会有一条记录。比如:
所以我们按
user_id
分组统计得到的
cnt
就是匹配的条件个数,最后通过
having
就可以筛选出同时满足两个条件(
having cnt = 2
)的用户的
id
。
这种方法的
sql
更便于动态拼接,不过使用这个
sql
有个前提条件,要求
tb_row_user
表里面的每一行记录的每一列对应到
tb_column_user
表都只有一行记录(也可以没有)。
由于使用了
OR
查询,因此
Type
为
index
,即全索引扫描(遍历索引树),因此性能会随着数据量的增加而越来越慢。
select u1.user_id
from tb_column_user
as u1
inner
join (
select user_id
from tb_column_user
where column_name
=
'sex'
and column_value
=
'男'
)
as u2
where u1.user_id
=u2.user_id
and u1.column_name
=
'age'
and u1.column_value
>
'25';
select u1.user_id
from tb_column_user
as u1
inner
join (
select uu1.user_id
from tb_column_user uu1
inner
join(
select user_id
from tb_column_user
where column_name
=
'tag'
and column_value
=
'穷'
)uu2
where uu1.user_id
=uu2.user_id
and uu1.column_name
=
'sex'
and uu1.column_value
=
'男'
)
as u2
where u1.user_id
=u2.user_id
and u1.column_name
=
'age'
and u1.column_value
>
'25';
可以看出,使用
join
查询每个查询都能用到索引,并且外层查询扫描的行数总比内层查询扫描的行数少,经过了层层过滤,最终扫描的行数就是最内层的查询语句所扫描的行数。
总结
方法二:优点是性能相对更好,但缺点是条件越多
sql
看着越难理解。