Looker 如何生成 SQL

如果您具有 SQL 背景,可能会想了解 Looker 如何生成 SQL。从根本上说,Looker 是一个工具,可以生成 SQL 查询,并通过数据库连接提交这些查询。Looker 根据 LookML 项目来制定 SQL 查询,用于描述数据库中表和列之间的关系。了解 Looker 如何生成查询后,您可以更好地了解 LookML 代码如何转换为高效的 SQL 查询。

每个 LookML 参数都会通过更改查询的结构、内容或行为来控制 Looker 生成 SQL 的某些方面。本页面介绍了 Looker 如何生成 SQL 的原则,但未详细介绍所有 LookML 元素。建议您首先查看 LookML 快速参考文档页面,了解 LookML 参数。

查看查询

在已保存的 Look探索中,您可以使用数据面板中的 SQL 标签页,查看 Looker 向数据库发送的数据以获取数据。您还可以使用 SQL 标签页底部的在 SQL Runner 中打开在 SQL Runner 中打开链接,在 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 项目定义 SQL 查询中引用的所有维度、测量值、探索和视图。过滤条件表达式由用户在 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 模型的结构填充的,LookML 模型的结构指定了视图如何联接到“探索”部分。构建 SQL 查询时,Looker 仅在需要时包含 JOIN 子句。用户在 Looker 中构建查询时,不需要指定表的联接方式,因为这类信息是在模型中编码的,而 Looker 为企业用户带来的最有力的好处之一就是这类信息。

示例查询和生成的 SQL

我们来在 Looker 中构建一个查询,以演示如何根据前面的模式生成查询。假设某电子商务商店的数据库中有 ordersusers 两个表,用于跟踪用户和订单。

orders
id INT
created_at DATETIME
users_id INT
status VARCHAR(255)
traffic_source VARCHAR(15)
users
id INT
email VARCHAR(255)
first_name VARCHAR(255)
last_name VARCHAR(255)
created_at DATETIME
zip INT
country VARCHAR(255)
state VARCHAR(255)
city VARCHAR(255)
age INT
traffic_source VARCHAR(15)

我们在 Looker 探索中查找按状态(用户状态)分组的订单数量(ORDERS 数量),并按订单创建日期(ORDERS 创建日期)进行过滤。

“探索”数据表显示过去 30 天内所下订单按用户状态分组的订单数量。

如需查看由 Looker 生成和执行的 SQL 查询,请点击数据面板中的 SQL 标签页。

SELECT COALESCE(users.state, ' ') AS "_g1",
   users.state AS 'users.state',
   COUNT(DISTINCT orders.id) AS 'orders.count'
FROM orders
LEFT JOIN users ON orders.user_id = users.id

WHERE
  orders.created_at BETWEEN (CONVERT_TZ(DATE_ADD(CURDATE(), INTERVAL -29 day), 'America/Los_Angeles', 'UTC',)) AND (CONVERT_TZ(DATE_ADD(DATE_ADD(DATE_ADD(CURDATE(), INTERVAL -29 day), INTERVAL 30 day), INTERVAL -1 second), 'America/Los_Angeles', 'UTC'))
GROUP BY 1
ORDER BY COUNT(DISTINCT orders.id) DESC
LIMIT 500

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

在 Looker 中尝试更多查询,证明查询结构始终相同。

在 Looker 的 SQL Runner 中运行原始 SQL

Looker 提供了一项名为 SQL Runner 的功能,该功能可让您针对在 Looker 中设置的数据库连接运行任何 SQL。

由于 Looker 生成的每个查询都会生成完整且有效的 SQL 命令,因此您可以使用 SQL Runner 来调查或试用查询。

在 SQL Runner 中执行的原始 SQL 查询会生成相同的结果集。如果 SQL 含有任何错误,SQL Runner 将突出显示 SQL 命令中第一个错误的位置,并在错误消息中包含错误的位置。

检查展开后网址中的查询组成部分

在 Looker 中运行查询后,您可以检查展开后的网址,了解 Looker 查询的基本组成部分。首先,从“探索”的齿轮菜单中选择分享,打开分享网址菜单。

展开后的网址会提供足够的信息来重新创建查询。例如,以下展开后的网址示例提供以下信息:

https://<Looker instance URL>.cloud.looker.com/explore/e_thelook/events?fields=users.state,users.count
&f[users.created_year]=2020&sorts=users.count+desc&limit=500
model e_thelook
探索 events
要查询和显示的字段 fields=users.state,users.count
排序字段和顺序 sorts=users.count+desc
过滤字段和值 f[users.created_year]=2020

Looker 如何构建联接

上述示例查询中,请注意 orders Discover 显示在主 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_name。如需详细了解每个参数,请参阅 LookML 参考文档

维度和测量的 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 块来指定派生表的查询。下面是一个基于 SQL 的派生表示例:

view: user_order_facts {
  derived_table: {
    sql:
      SELECT
        user_id
        , COUNT(*) 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});;
}