

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 编写表达式查询
<a name="v10-panels-query-xform-expressions"></a>

****  
本文档主题专为支持 **Grafana 10.x 版本**的 Grafana 工作区而设计。  
对于支持 Grafana 9.x 版本的 Grafana 工作区，请参阅[使用 Grafana 版本 9](using-grafana-v9.md)。  
对于支持 Grafana 8.x 版本的 Grafana 工作区，请参阅[使用 Grafana 版本 8](using-grafana-v8.md)。

服务器端表达式使您能够用数学和其他运算查询返回的数据。表达式会创建新数据，但不操作数据来源返回的数据。

## 关于表达式
<a name="v10-panels-query-about"></a>

服务器端表达式使您能够用数学和其他运算查询返回的数据。表达式会创建新数据，但不操作数据来源返回的数据，除了一些小的数据重组，使数据成为表达式可接受的输入。

**使用表达式**

[Grafana Alerting](v10-alerts.md) 使用最常用的表达式。处理在服务器端完成，因此表达式可以在没有浏览器会话的情况下运行。但是，表达式也可以用于后端数据来源和可视化。

**注意**  
表达式不适用于旧版控制面板警报。

表达式旨在通过合并来自不同数据来源的查询或提供数据来源中不可用的运算来扩充数据来源。

**注意**  
在可能的情况下，应在数据来源内部进行数据处理。将数据从存储复制到 Grafana 服务器进行处理效率低下，因此表达式主要针对轻量级数据处理。

表达式适用于返回时间序列或数值数据的数据来源查询。还可以处理[多维数据](getting-started-grafanaui.md#time-series-dimensions)。例如，返回多个序列的查询，其中每个序列都由标签或标记标识。

单个表达式将一个或多个查询或其他表达式作为输入，并将数据添加到结果中。每个单独的表达式或查询都由一个变量表示，该变量是一个命名标识符，称为 RefID（例如，默认字母 `A` 或 `B`）。

要在另一个表达式中引用单个表达式或数据来源查询的输出，此标识符将用作变量。

**表达式类型**

表达式可以处理两种类型的数据。
+ 时间序列的集合。
+ 数字的集合，其中每个数字都是一个项。

每个集合均从单个数据来源查询或表达式返回，并由 ReFID 表示。每个集合都是一个集，而集中的每一项都由其维度唯一标识，这些维度以[标签](getting-started-grafanaui.md#labels)或键值对形式存储。

**数据来源查询**

服务器端表达式仅支持对后端数据来源的查询。通常假设数据是有标签的时间序列数据。

与表达式一起使用时，数据来源查询由表达式引擎运行。在此过程中，将数据重组为一个时间序列或每个数据帧一个数字。例如，如果使用的数据来源在表视图中的一个帧上返回多个序列，那么在使用表达式运行时，您可能会发现其看起来有所不同。

目前，在使用数据帧时，唯一支持的非时间序列格式（数字）是表响应，返回的是无时间的数据帧、字符串列和一个数字列：

以下示例表生成一个适用于表达式的数字。字符串列变成标签，数字列变成相应的值。例如 `{"Loc": "MIA", "Host": "A"}`，值为 1。


| Loc | 主机 | Avg\$1CPU | 
| --- | --- | --- | 
| MIA | A | 1 | 
| NYC | B | 2 | 

**操作**

您可以在表达式中使用以下运算：math、reduce 和 resample。

**数学**

Math 适用于时间序列或数字数据的自由格式数学公式。数学运算将数字和时间序列作为输入，并将其转换为不同的数字和时间序列。

在引用其他查询或表达式中的数据时，RefID 会以美元符号作为前缀，例如 `$A`。如果变量的名称中包含空格，则可以使用大括号语法，例如 `${my variable}`。

数字常量可以是十进制（`2.24`）、八进制（带前导 0 `072`）或十六进制（带前导 0x `0x2A`）。还支持指数和符号（例如 `-0.8e-2`）。

**运算符**

支持算术运算符（`+`、二元和一元 `-`、`*`、`/`、`%`、指数 `**`）、关系运算符（`<`、`>`、`==`、`!=`、`>=`、`<=`）和逻辑运算符（`&&`、`||` 和一元 `!`）。

运算对数据的行为方式取决于其是数字数据还是时间序列数据。

对于二元运算（例如 `$A + $B` 或 `$A || $B`），运算符根据数据类型按以下方式应用：
+ 如果 `$A` 和 `$B` 都是数字，则对这两个数字执行运算。
+ 如果一个变量是数字，而另一个变量是时间序列，则执行时间序列中每个点的值与数字之间的运算。
+ 如果 `$A` 和 `$B` 都是时间序列数据，则对 `$A` 和 `$B` 中存在的每个时间戳执行两个序列中每个值之间的运算。可使用 `Resample` 运算排列时间戳。

摘要：
+ 数字 <Operation> 数字 = 数字
+ 数字 <Operation> 序列 = 序列
+ 序列 <Operation> 序列 = 序列

由于表达式处理由单个变量表示的多个序列或数字，因此二进制运算还会在两个变量之间执行联合（连接）。这是根据与每个序列或数字关联的标识标签完成的。

因此，如果 `$A` 中的数字带有 `{host=web01}` 等标签，而 `$B` 中的另一个数字具有相同的标签，则在每个变量中的这两个项目之间执行运算，结果将共享相同的标签。此联合的行为规则如下：
+ 无标签的项将连接到任何项。
+ 如果 `$A` 和 `$B` 都只包含一个项（一个序列或一个数字），则二者将会连接。
+ 如果标签完全匹配，则会连接。
+ 如果标签是另一个标签的子集，例如 `$A` 中的一个项标记为 `{host=A,dc=MIA}`，而 `$B` 中的一个项标记为 `{host=A}`，则二者将会连接。
+ 如果在 `$A` 等变量中，每个项都有不同的标签键，则连接行为未定义。

关系运算符和逻辑运算符返回 0 表示 false，返回 1 表示 true。

**数学函数**

虽然大多数函数存在于自己的表达式运算中，但数学运算确实有一些类似于数学运算符或符号的函数。当函数可以取数字或序列时，将返回与参数相同的类型。如果是序列，则对序列中每个点的值执行运算。

*abs*

abs 返回其参数的绝对值，可以是数字或序列。例如，`abs(-1)` 或 `abs($A)`。

*is\$1inf*

is\$1inf 取一个数字或序列，`Inf` 值（负值或正值）返回 `1`，其他值返回 `0`。例如 `is_inf($A)`。

**注意**  
例如，如果需要特别检查负无穷大，可以做一个比较，比如 `$A == infn()`。

*is\$1nan*

is\$1nan 取一个数字或序列，`NaN` 值返回 `1`，其他值返回 `0`。例如 `is_nan($A)`。此项检查需要使用此函数，因为 `NaN` 不等于 `NaN`。

*is\$1null*

is\$1null 取一个数字或序列，`null` 值返回 `1`，其他值返回 `0`。例如 `is_null($A)`。

*is\$1number*

is\$1number 取一个数字或序列，所有实数值返回 `1`，其他值返回 `0`（即 `null`、`Inf+`、`Inf-` 和 `NaN`）。例如 `is_number($A)`。

*log*

log 返回其参数的自然对数，可以是数字或序列。如果值小于 0，则返回 `NaN`。例如，`log(-1)` 或 `log($A)`。

*inf、infn、nan 和 null*

inf、infn、nan 和 null 函数都返回一个名称值。主要用于测试。示例：`null()`。

*round*

Round 返回一个四舍五入的整数值。例如，`round(3.123)` 或 `round($A)`。

*ceil*

Ceil 将数字向上舍入到最接近的整数值。例如，`ceil(3.123)` 将返回 `4`。

*floor*

Floor 将数字向下舍入到最接近的整数值。例如，`floor(3.123` 将返回 `3`。

**Reduce**

Reduce 取查询或表达式返回的一个或多个时间序列，将每个序列转换为一个数字。时间序列的标签作为标签保留在每个输出的归约数上。

*字段：*
+ **函数**：要使用的归约函数
+ **输入**：要重采样的变量（RefID（如 `A`））
+ **模式**：当序列包含非数值（null、NaN、\$1-Inf）时，允许控制归约函数的行为

**归约函数**

*计数*

Count 返回每个序列中的点数。

*平均值*

Mean 返回每个序列中所有值的总和除以该序列中的点数。在 `strict` 模式下，如果序列中的任何值为 null 或 nan，或者序列为空，则返回 NaN。

*Min 和 Max*

Min 和 Max 分别返回序列中的最小值或最大值。在 `strict` 模式下，如果序列中的任何值为 null 或 nan，或者序列为空，则返回 NaN。

*总计*

Sum 返回序列中所有值的总和。如果序列长度为零，则总和为 0。在 `strict` 模式下，如果序列中的任何值为 NaN 或 Null，则返回 NaN。

*Last*

Last 返回序列中的最后一个数字。如果该序列没有值，则返回 NaN。

**归约模式**

*Strict*

在 Strict 模式下，按原样处理输入序列。如果序列中的任何值为非数字（null、NaN 或 \$1-Inf），则返回 NaN。

*Drop Non-Numeric*

在此模式下，在运行归约函数之前，将筛选掉输入序列中的所有非数字值（null、NaN 或 \$1-Inf）。

*Replace Non-Numeric*

在此模式下，所有非数字值都将替换为预定义值。

**Resample**

Resample 会更改每个时间序列中的时间戳，使其具有一致的时间间隔。主要用例是，您可以对不共享相同时间戳的时间序列进行重采样，以便在其之间执行数学运算。这可以通过对两个序列中的每一个进行重采样，然后在引用重采样变量的数学运算中来完成。

*字段：*
+ **输入**：要重采样的时间序列数据变量（refID（如 `A`））
+ **重采样至**：要重新采样的持续时间，例如 `10s`。单位 `s` 表示秒，`m` 表示分钟，`h` 表示小时，`d` 表示天，`w` 表示周，`y` 表示年。
+ **降采样**：当每个窗口样本有多个数据点时使用的归约函数。有关行为详细信息，请参阅归约运算。
+ **升采样**：用于填充无数据点窗口样本的方法。
  + **pad** 填充上一个已知值
  + **backfill** 回填下一个已知值
  + **fillna** 用来填充空白的样本窗口 NaNs

## 编写表达式
<a name="v10-panels-query-write"></a>

如果您的数据来源支持表达式，Grafana 会显示**表达式**按钮，并在查询编辑器列表中显示任何现有表达式。

**编写表达式**

1. 打开面板。

1. 在查询下方，选择**表达式**。

1. 在**运算**字段中，选择要编写的表达式类型。

1. 编写表达式。

1. 选择**应用**。

## 特殊情况
<a name="v10-panels-query-special"></a>

当任何查询的数据来源未返回序列或数字时，表达式引擎将返回 `NoData`。例如，如果请求包含两个由表达式合并的数据来源查询，而 `NoData` 由至少一个数据来源查询返回，则整个查询的返回结果为 `NoData`。有关 Grafana Alerting 如何处理 `NoData` 结果的更多信息，请参阅 [配置 Grafana 管理的警报规则](v10-alerting-configure-grafanamanaged.md)。

如果在多个查询中使用表达式，则表达式引擎要求所有查询都返回相同的时间戳。例如，如果使用数学方法合并多个 SQL 查询的结果（每个查询都使用 `SELECT NOW() AS "time"`），那么只有当所有查询都将 `NOW()` 评估为相同的时间戳时，表达式才会起作用，但这种情况并不总是发生。要解决此问题，可以将 `NOW()` 替换为任意时间，例如 `SELECT 1 AS "time"` 或任何其他有效的 UNIX 时间戳。