课程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_FROMCAST 更高效。使用 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`