![v2-a45ccf1024425058f54dbd2d48237087_1440w.jpg?source=172ae18b](http://img-01.proxy.5ce.com/view/image?&type=2&guid=c33236fd-162f-eb11-8da9-e4434bdf6706&url=https://pic4.zhimg.com/v2-a45ccf1024425058f54dbd2d48237087_1440w.jpg?source=172ae18b)
Superset查询数据用了SQLAlchemy的两种接口。
一种是带模型定义的,像Dashboard、Slice这样的对象,都是先定义模型(superset/models/core.py),才能在视图中访问(superset/views/core.py)。
另一种是不带模型定义的,在Sources::Tables界面上添加的表,都是用的不带模型的接口。按SQLAlchemy的文档,这种接口叫“Core”。
connectors/sqla/models.py文件,大量使用Core的功能,Superset生成SQL语句的函数,就在SqlaTable类里面。
要生成带join的语句(函数名:get_query_str_with_join),先要明确需要传递哪些参数:
kpi_dict:业务要查询的kpi列表。
on_list:关联条件列表。
table_list:从属表。这个概念要稍加解释,多表关联,是以一个表为主,串联起多张表,所以从概念上,可以分为主表和从属表。get_query_str_with_join函数要通过主表的对象来调用,所以只需要传递从属表。
kpi_dict:每个元素是一个list。key:table.id,value:kpi_list。
on_list:每个元素是一个Dict。key:主表id_从属表id,value:关联条件。
说明:虽然SqlaTable定义中不包括id字段,从表结构能看到是有id的:
![v2-88feb9d8b8c0c960678d1c23290e60e5_b.jpg](http://img-02.proxy.5ce.com/view/image?&type=2&guid=c33236fd-162f-eb11-8da9-e4434bdf6706&url=https://pic2.zhimg.com/v2-88feb9d8b8c0c960678d1c23290e60e5_b.jpg)
流程:
- 先处理select_exprs。
依次处理main_table
assist_tables
因为不同表可能存在同名的字段,需要在outer里面加入表名。
- 生成qry主体:
qry = sa.select(select_exprs)
qry = qry.limit(row_limit)
tbl = self.get_sqla_table()
qry = qry.select_from(tbl)
遍历辅助表,增加join
left_column = on['left_table_name'] + '.' + on['left_code']
left_tmp = literal_column(left_column)
left_sqla_col = self.make_sqla_column_compatible(left_tmp)
right_column = on['right_table_name'] + '.' + on['right_code']
right_tmp = literal_column(right_column)
right_sqla_col = self.make_sqla_column_compatible(right_tmp)
on_clause.append(left_sqla_col == right_sqla_col)
tbl = tbl.join(table.name, and_(*on_clause))
大概就是这样了。
2019-11-07更新:
tbl.join接受的参数,必须是sqlalchemy.sql.table、或者sqlalchemy.sql.table.alias(),所以上述代码中的tbl.join(table.name, and_(*on_clause)),需要修改。
先根据表名创建一个sqlalchemy.sql.table对象,再用该对象作为join函数的参数。
另,上述代码中的qry = qry.select_from(tbl),需要调整到tbl.join之后。
2019-11-08更新:
三表关联的sql:
2019-11-08 03:50:45,514:DEBUG:root:SELECT count_time AS count_time_19480, count_number AS count_number_19479, production.name AS name_19487, production.brand AS brand_19486, store.name AS name_19495
FROM main.count_record JOIN production ON count_record.production = production.id JOIN store ON count_record.store = store.id
LIMIT 300000 OFFSET 0