课程2:使用 ANSI SQL 查询
目标
本节课展示在 Apache Drill 中如何去使用标准的 SQL 做分析:例如,使用聚合函数和 JOIN 用法。值得注意的是,Apache Drill 提供 ANSI SQL 支持,而不是一个 SQL 接口。
查询
现在,你知道了数据源的原始形式,使用 select * 去查询,尝试在每个数据源上运行一些简单但更有用的查询。这些查询演示了 Drill 是如何支持 ANSI SQL,并且是如何在一个 SELECT 语句中结合不同数据源的数据的。
- 展示聚合查询在一个单独的文件或是表中。使用
GROUP BY, WHERE, HAVING, ORDER BY语法。 - 在 Hive,MapR-DB,和文件系统的数据源中执行
JOIN。 - 使用表和列的别名。
- 创建一个 Drill 视图。
聚合
切换到 hive 的 schema:
1. 0: jdbc:drill:> use hive.`default`;
2. +-------+-------------------------------------------+
3. | ok | summary |
4. +-------+-------------------------------------------+
5. | true | Default schema changed to [hive.default] |
6. +-------+-------------------------------------------+
7. 1 row selected
按月返回销售总量:
1. 0: jdbc:drill:> select `month`, sum(order_total)
2. from orders group by `month` order by 2 desc;
3. +------------+---------+
4. | month | EXPR$1 |
5. +------------+---------+
6. | June | 950481 |
7. | May | 947796 |
8. | March | 836809 |
9. | April | 807291 |
10. | July | 757395 |
11. | October | 676236 |
12. | August | 572269 |
13. | February | 532901 |
14. | September | 373100 |
15. | January | 346536 |
16. +------------+---------+
17. 10 rows selected
Drill 是支持 SQL 的聚合函数的,例如 SUM,MAX,AVG 和 MIN。标准的 SQL 语法以同样的方式运行在 Drill 查询中是和关系型数据库一样的。
需要注意的是,标记返回的 “month” 列是因为其在 SQL 中是保留字符,所以,需要加上引号。
按 month 和 state 字段分组,返回前 20 行销售总额:
1. 0: jdbc:drill:> select `month`, state, sum(order_total) as sales from orders group by `month`, state
2. order by 3 desc limit 20;
3. +-----------+--------+---------+
4. | month | state | sales |
5. +-----------+--------+---------+
6. | May | ca | 119586 |
7. | June | ca | 116322 |
8. | April | ca | 101363 |
9. | March | ca | 99540 |
10. | July | ca | 90285 |
11. | October | ca | 80090 |
12. | June | tx | 78363 |
13. | May | tx | 77247 |
14. | March | tx | 73815 |
15. | August | ca | 71255 |
16. | April | tx | 68385 |
17. | July | tx | 63858 |
18. | February | ca | 63527 |
19. | June | fl | 62199 |
20. | June | ny | 62052 |
21. | May | fl | 61651 |
22. | May | ny | 59369 |
23. | October | tx | 55076 |
24. | March | fl | 54867 |
25. | March | ny | 52101 |
26. +-----------+--------+---------+
27. 20 rows selected
值得一提的是,这里将别名用在了 SUM 聚合函数上。Drill 是支持列别名和表别名的。
HAVING 语法
使用 HAVING 语法来约束聚合结果。
切换到 dfs.clicks 工作区间
1. 0: jdbc:drill:> use dfs.clicks;
2. +-------+-----------------------------------------+
3. | ok | summary |
4. +-------+-----------------------------------------+
5. | true | Default schema changed to [dfs.clicks] |
6. +-------+-----------------------------------------+
7. 1 row selected
按设备分组,设备点击数高过一定阀值的总数:
1. 0: jdbc:drill:> select t.user_info.device, count(*) from `clicks/clicks.json` t
2. group by t.user_info.device
3. having count(*) > 1000;
4. +---------+---------+
5. | EXPR$0 | EXPR$1 |
6. +---------+---------+
7. | IOS5 | 11814 |
8. | AOS4.2 | 5986 |
9. | IOS6 | 4464 |
10. | IOS7 | 3135 |
11. | AOS4.4 | 1562 |
12. | AOS4.3 | 3039 |
13. +---------+---------+
14. 6 rows selected
聚合函数从点击量的数据中,统计不同手机设备的点击量。在查询是附加激活和注册的设备总量必须大于 1000,方能返回统计结果。
UNION 操作
和之前相同的工作区间(dfs.clicks)。
clicks 和 campaign 的 UNION 用法
1. 0: jdbc:drill:> select t.trans_id transaction, t.user_info.cust_id customer from `clicks/clicks.campaign.json` t
2. union all
3. select u.trans_id, u.user_info.cust_id from `clicks/clicks.json` u limit 5;
4. +-------------+------------+
5. | transaction | customer |
6. +-------------+------------+
7. | 35232 | 18520 |
8. | 31995 | 17182 |
9. | 35760 | 18228 |
10. | 37090 | 17015 |
11. | 37838 | 18737 |
12. +-------------+------------+
UNION all 查询存在于 clicks.campaign.json 和 clicks.json 两个文件中的所有记录。
子查询
切换工作空间:
1. 0: jdbc:drill:> use hive.`default`;
2. +-------+-------------------------------------------+
3. | ok | summary |
4. +-------+-------------------------------------------+
5. | true | Default schema changed to [hive.default] |
6. +-------+-------------------------------------------+
7. 1 row selected
通过 state 比较 order 的总量:
1. 0: jdbc:drill:> select ny_sales.cust_id, ny_sales.total_orders, ca_sales.total_orders
2. from
3. (select o.cust_id, sum(o.order_total) as total_orders from hive.orders o where state = 'ny' group by o.cust_id) ny_sales
4. left outer join
5. (select o.cust_id, sum(o.order_total) as total_orders from hive.orders o where state = 'ca' group by o.cust_id) ca_sales
6. on ny_sales.cust_id = ca_sales.cust_id
7. order by ny_sales.cust_id
8. limit 20;
9. +------------+------------+------------+
10. | cust_id | ny_sales | ca_sales |
11. +------------+------------+------------+
12. | 1001 | 72 | 47 |
13. | 1002 | 108 | 198 |
14. | 1003 | 83 | null |
15. | 1004 | 86 | 210 |
16. | 1005 | 168 | 153 |
17. | 1006 | 29 | 326 |
18. | 1008 | 105 | 168 |
19. | 1009 | 443 | 127 |
20. | 1010 | 75 | 18 |
21. | 1012 | 110 | null |
22. | 1013 | 19 | null |
23. | 1014 | 106 | 162 |
24. | 1015 | 220 | 153 |
25. | 1016 | 85 | 159 |
26. | 1017 | 82 | 56 |
27. | 1019 | 37 | 196 |
28. | 1020 | 193 | 165 |
29. | 1022 | 124 | null |
30. | 1023 | 166 | 149 |
31. | 1024 | 233 | null |
32. +------------+------------+------------+
本示例演示 Drill 支持子查询。
CAST 函数
切换到 maprdb 工作区间:
1. 0: jdbc:drill:> use maprdb;
2. +-------+-------------------------------------+
3. | ok | summary |
4. +-------+-------------------------------------+
5. | true | Default schema changed to [maprdb] |
6. +-------+-------------------------------------+
7. 1 row selected (0.088 seconds)
返回消费数据的数据类型
1. 0: jdbc:drill:> select cast(row_key as int) as cust_id, cast(t.personal.name as varchar(20)) as name,
2. cast(t.personal.gender as varchar(10)) as gender, cast(t.personal.age as varchar(10)) as age,
3. cast(t.address.state as varchar(4)) as state, cast(t.loyalty.agg_rev as dec(7,2)) as agg_rev,
4. cast(t.loyalty.membership as varchar(20)) as membership
5. from customers t limit 5;
6. +----------+----------------------+-----------+-----------+--------+----------+-------------+
7. | cust_id | name | gender | age | state | agg_rev | membership |
8. +----------+----------------------+-----------+-----------+--------+----------+-------------+
9. | 10001 | "Corrine Mecham" | "FEMALE" | "15-20" | "va" | 197.00 | "silver" |
10. | 10005 | "Brittany Park" | "MALE" | "26-35" | "in" | 230.00 | "silver" |
11. | 10006 | "Rose Lokey" | "MALE" | "26-35" | "ca" | 250.00 | "silver" |
12. | 10007 | "James Fowler" | "FEMALE" | "51-100" | "me" | 263.00 | "silver" |
13. | 10010 | "Guillermo Koehler" | "OTHER" | "51-100" | "mn" | 202.00 | "silver" |
14. +----------+----------------------+-----------+-----------+--------+----------+-------------+
注意以下查询要点:
- CAST 函数是需要作用于表里的每个列的。函数将 MapR-DB/HBase 的二进制数据返回成可读的整形和字符串。另外,你需要使用
CONVERT_TO/CONVERT_FROM函数去解析字符串列。CONVERT_TO/CONVERT_FROM比CAST更高效。使用CONVERT_TO将二进制类型转换成任意类型。 - row_key 列函数作为一个表的主键(本案例中是客户的 ID)。
- 表别名是必须的;否则列簇名将会被解析为表名,从而返回查询错误。
从字符串中删除引号
你可以使用 regexp_replace 函数,去删除查询结果中的引号。例如,将一个 state 名下的 “va” 替换为 va:
1. 0: jdbc:drill:> select cast(row_key as int), regexp_replace(cast(t.address.state as varchar(10)),'"','')
2. from customers t limit 1;
3. +------------+------------+
4. | EXPR$0 | EXPR$1 |
5. +------------+------------+
6. | 10001 | va |
7. +------------+------------+
8. 1 row selected
创建视图命令
切换到工作区间
1. 0: jdbc:drill:> use dfs.views;
2. +-------+----------------------------------------+
3. | ok | summary |
4. +-------+----------------------------------------+
5. | true | Default schema changed to [dfs.views] |
6. +-------+----------------------------------------+
7. 1 row selected
使用一个可变的工作区间:
一个可变(或是可写)的工作区间是能够被执行“写”操作的。这个属性是存储插件配置的一部分。你可以创建 Drill 视图和表在可变的工作区间中。
创建一个视图在 MapR-DB 表
1. 0: jdbc:drill:> create or replace view custview as select cast(row_key as int) as cust_id,
2. cast(t.personal.name as varchar(20)) as name,
3. cast(t.personal.gender as varchar(10)) as gender,
4. cast(t.personal.age as varchar(10)) as age,
5. cast(t.address.state as varchar(4)) as state,
6. cast(t.loyalty.agg_rev as dec(7,2)) as agg_rev,
7. cast(t.loyalty.membership as varchar(20)) as membership
8. from maprdb.customers t;
9. +-------+-------------------------------------------------------------+
10. | ok | summary |
11. +-------+-------------------------------------------------------------+
12. | true | View 'custview' created successfully in 'dfs.views' schema |
13. +-------+-------------------------------------------------------------+
14. 1 row selected
Drill 提供了和关系型数据库创建视图相似的语法 CREATE OR REPLACE VIEW。使用 OR REPLACE 选项可以很方便的去更新视图到最新,而不需要我们在创建之前删除它。值得注意的是,FROM 语法在本示例中必需指向 maprdb.customers。MapR-DB 表不能直接在 dfs.views 的工作区间中可视化。
不像传统数据库那样,视图还需要 DBA/开发驱动业务,在 Drill 中视图是基于文件系统的,属于轻量级的。视图只是一个特殊的文件,具有特殊的扩展名(.drill)。你可以本地文件系统或指定特定的工作区间去存储视图。你可以创建视图的语句中指定任何查询,对任何数据源进行查询。
Drill 提供了一个分散的元数据模型。Drill 能够去查询一些定义好的元数据,例如 Hive,HBase 和 文件系统。Drill 还支持在文件系统中创建元数据。
从视图中查询数据:
1. 0: jdbc:drill:> select * from custview limit 1;
2. +----------+-------------------+-----------+----------+--------+----------+-------------+
3. | cust_id | name | gender | age | state | agg_rev | membership |
4. +----------+-------------------+-----------+----------+--------+----------+-------------+
5. | 10001 | "Corrine Mecham" | "FEMALE" | "15-20" | "va" | 197.00 | "silver" |
6. +----------+-------------------+-----------+----------+--------+----------+-------------+
7. 1 row selected
一旦,用户知道哪些数据是直接可以从文件系统中可用的,视图可以用来读取数据到下游工具,例如:Tableau 和 MicroStrategy 用于分析和可视化。对于这些工具,视图是以一个“表”出现的,在它的可选“列”中。
查询数据源
继续使用 dfs.views 做查询。
连接用户视图和订单表:
1. 0: jdbc:drill:> select membership, sum(order_total) as sales from hive.orders, custview
2. where orders.cust_id=custview.cust_id
3. group by membership order by 2;
4. +------------+------------+
5. | membership | sales |
6. +------------+------------+
7. | "basic" | 380665 |
8. | "silver" | 708438 |
9. | "gold" | 2787682 |
10. +------------+------------+
11. 3 rows selected
在该查询中,我们从 MapR-DB 表中读取数据,并且结合 Hive 中的订单信息。当跨数据源查询是,你需要完全限定表/视图名称。例如,订单表的前缀是 “hive”,这个是存储插件注册在 Drill 中的。对于 “custview” 我们不使用任何前缀,因为我们明确的把 dfs.views workspace 存储在工作区间。
值得注意的是,如果你的查询结果显示被截断,是因为你的行太长了,将显示的最大宽度设置为 10000:
在设置命令中不要使用分号。
连接客户,订单和点击量的数据:
1. 0: jdbc:drill:> select custview.membership, sum(orders.order_total) as sales from hive.orders, custview,
2. dfs.`/mapr/demo.mapr.com/data/nested/clicks/clicks.json` c
3. where orders.cust_id=custview.cust_id and orders.cust_id=c.user_info.cust_id
4. group by custview.membership order by 2;
5. +------------+------------+
6. | membership | sales |
7. +------------+------------+
8. | "basic" | 372866 |
9. | "silver" | 728424 |
10. | "gold" | 7050198 |
11. +------------+------------+
12. 3 rows selected
三者连接从三个不同的数据源中选择一个查询:
- hive.orders 表
- custview(HBase 客户表的一个视图)
- clicks.json 文件
连接列以连接两者的 cust_id 列为条件。视图工作区间用于该查询,以至于 custview 可以访问。而 hive.orders 表的查询也是可见的。
另外,需要注意的是,JSON 文件是不能直接在视图工作区间被可视化的,需要指定查询文件的全路径:
1. dfs.`/mapr/demo.mapr.com/data/nested/clicks/clicks.json`
