Looker 如何生成 SQL

如果您是从 SQL 背景来到 Looker,可能很想知道 Looker 如何生成 SQL。从根本上讲,Looker 是一个用来生成 SQL 查询并针对数据库连接提交这些查询的工具。Looker 基于 LookML 项目(用于描述数据库中的表和列之间的关系)构建 SQL 查询。通过了解 Looker 如何生成查询,您可以更好地了解 LookML 代码如何转换为高效的 SQL 查询。

每个 LookML 参数都会通过更改查询的结构、内容或行为,来控制 Looker 生成 SQL 的方式。本页介绍了 Looker 如何生成 SQL 的原则,但并未涵盖所有 LookML 元素。如需了解完整详情,请参阅 LookML 参考文档文档页面。

查看查询

在已保存的 Look 或 Discover 中,您可以使用数据部分中的 SQL 标签页查看 Looker 向数据库发送的数据以获取数据。您还可以使用底部的链接在 SQL Runner 中查看查询,或查看数据库的查询计划。如需详细了解 SQL Runner,请参阅 SQL Runner 基础知识文档页面。如需详细了解如何使用 SQL Runner 优化查询,请参阅如何使用 EXPLAIN 优化 SQL 帮助中心文章。

Looker 查询的规范形式

Looker 的 SQL 查询始终采用以下形式。

SELECT
   <dimension>, <dimension>, ...
   <measure>, <measure>, ...
FROM <explore>
LEFT JOIN <view> ON ...
LEFT JOIN <view> ON ...
WHERE (<dimension_filter_expression>) AND (<dimension_filter_expression>) AND ...
GROUP BY <dimension>, <dimension>, <dimension>, ...
HAVING <measure_filter_expression> AND <measure_filter_expression> AND ...
ORDER BY <dimension> | <measure>
LIMIT <limit>

LookML 项目定义了以上公式中引用的所有维度、度量单位、探索和视图。过滤条件表达式由用户在 Looker 中指定,以构建临时查询。您还可以在 LookML 中直接声明过滤条件表达式以应用于所有查询。

Looker 查询的基本组件

所有 Looker 查询都由应用于 LookML 项目的这些基本参数表示,如上公式所示。

Looker 使用以下参数生成完整的 SQL 查询:

  • model:要定位的 LookML 模型的名称,用于指定目标数据库
  • explore:要查询的探索的名称,用于填充 SQL FROM 子句
  • 字段:要包含在查询中的 dimensionmeasure 参数,用于填充 SQL SELECT 子句
  • filter:应用于零个或多个字段的 Looker 过滤条件表达式,用于填充 SQL WHEREHAVING 子句
  • 排序顺序:要排序的字段,以及填充 SQL ORDER BY 子句的排序顺序

这些参数恰好是用户在 Looker 探索页面上构建查询时指定的元素。在执行 Looker 的所有查询模式中,这些元素都会显示在生成的 SQL 中、在代表查询的网址中、在 Looker API 中等。

LEFT JOIN 子句指定的视图会怎么样?JOIN 子句根据 LookML 模型的结构进行填充,该结构指定视图如何加入“探索”。构造 SQL 查询时,Looker 仅在需要时包含 JOIN 子句。当用户在 Looker 中构建查询时,无需指定表的联接方式,因为这些信息在模型中进行了编码,这是 Looker 对企业用户最强大的优势之一。

示例查询和生成的 SQL

我们在 Looker 中构建查询,以演示如何根据上述模式生成查询。假设某家电商设有表格,用来跟踪用户和订单。字段和表关系如下所示。

我们按订单(用户状态)分组并按订单创建日期(订单创建日期)过滤的订单数量(订单数)。

以下是 Looker 探索页面中的查询结果。

点击 SQL 标签页会显示由 Looker 生成和执行的 SQL。

请注意与上述规范公式的相似之处。Looker 的 SQL 会显示机器生成的代码的一些特征(例如 COALESCE(users.state,'') AS "_g1"),但始终符合公式。

SELECT
   <dimension>,<dimension>,...
   <measure>,<measure>,...
FROM <explore>
LEFT JOIN <view> ON ...
LEFT JOIN <view> ON ...
WHERE (<dimension_filter_expression>) AND (<dimension_filter_expression>) AND ...
GROUP BY <dimension>,<dimension>,<dimension>,...
ORDER BY <dimension> | <measure>
HAVING <measure_filter_expression> AND <measure_filter_expression> AND ...
LIMIT <limit>

在 Looker 中试验更多查询,向自己证明查询结构始终相同。

在 Looker 的 SQL Runner 中运行原始 SQL

Looker 包含一项名为 SQL 运行程序 的功能,可供您针对在 Looker 中设置的数据库连接运行任何您想要的 SQL。

由于 Looker 生成的每个查询都会产生一个完整、实用的 SQL 命令,因此您可以使用 SQL Runner 来调查或查询。

在 SQL Runner 中执行的原始 SQL 查询会生成相同的结果集

如果 SQL 包含任何错误,SQL Runner 将突出显示 SQL 命令中第一个错误的位置,并在错误消息中包括错误的位置。

检查网址中的查询组件

在 Looker 中运行查询后,您可以检查扩展共享网址,以查看 Looker 查询的基本组件。首先,从“探索”齿轮菜单中选择共享

如果您是从 Look 开始,请点击 Looker 的从此处探索链接以在“探索”中打开查询。

系统会显示共享网址窗口,其中显示了展开后的网址:

例如,上述查询会生成以下扩展共享网址:

https://docsexamples.dev.looker.com/explore/e_thelook/events?fields=users.state,users.count
&f[users.created_year]=2020&sorts=users.count+desc&limit=500

该网址提供了足够的信息来重新创建查询:

模型 e_thelook
探索 events
要查询和显示的字段 fields=users.state,users.count
对字段和顺序进行排序 sorts=users.count+desc
过滤字段和值 f[users.created_year]=2020

Looker 结构如何联接

请注意,在上述查询 SQL 中,orders 探索显示在主 FROM 子句中,联接的视图显示在 LEFT JOIN 子句中。Looker 联接的编写方式多种多样,在 LookML 中使用联接页面对此进行了更详细的说明。

SQL 代码块指定自定义 SQL 子句

Looker 查询的所有元素并非由机器生成的。有时,数据模型需要为 Looker 提供特定详细信息,以访问底层表并计算派生值。在 LookML 中,SQL 块是数据建模者提供的 SQL 代码段,Looker 使用它们来合成完整的 SQL 表达式。

最常见的 SQL 块参数是 sql,它用于维度和度量定义。sql 参数指定用于引用底层列或执行聚合函数的 SQL 子句。通常,以 sql_ 开头的所有 LookML 参数都需要采用某种形式的 SQL 表达式。例如:sql_always_wheresql_onsql_table_nameLookML 参考提供了有关每个参数的详细信息。

维度和指标的 SQL 块示例

以下是针对维度和度量方式的一些 SQL 代码块示例。LookML 替换运算符 ($) 会使这些 sql 声明看起来与 SQL 看起来有所不同。不过,替换后,生成的字符串是纯 SQL,Looker 会注入查询的 SELECT 子句。

dimension: id {
  primary_key: yes
  sql: ${TABLE}.id ;;  # Specify the primary key, id
}
measure: average_cost {
  type: average
  value_format: "0.00"
  sql: ${cost} ;;      # Specify the field that you want to average
                       # The field 'cost' is declared elsewhere
}
dimension: name {
  sql: CONCAT(${first_name}, ' ', ${last_name}) ;;
}
dimension: days_in_inventory {
  type: number
  sql: DATEDIFF(${sold_date}, ${created_date}) ;;
}

如上文的最后两个维度所示,SQL 块可以使用底层数据库支持的函数(在本例中为 MySQL 函数 CONCATDATEDIFF)。SQL 块中使用的代码必须与数据库使用的 SQL 方言一致。

派生表的 SQL 块示例

派生表也使用 SQL 代码块来指定派生该表的查询。示例如下:

view: user_order_facts {
  derived_table: {
    sql:
      SELECT
        user_id
        , COUNT(&#42;) as lifetime_orders
      FROM orders
      GROUP BY 1 ;;
  }

  # later, dimension declarations reference the derived column(s)…
  dimension: lifetime_orders {
    type: number
  }
}

用于过滤探索的 SQL 块示例

借助 sql_always_wheresql_always_having LookML 参数,您可以通过将 SQL 块注入 SQL WHERE 或 HAVING 子句来限制可供查询使用的数据。在此示例中,LookML 替换运算符 ${view_name.SQL_TABLE_NAME} 用于引用派生表:

explore: trips {
  view_label: "Long Trips"
  # This will ensure that we only see trips that are longer than average!
  sql_always_where: ${trips.trip_duration}>=(SELECT tripduration FROM ${average_trip_duration.SQL_TABLE_NAME});;
}