GET_QUERY_OPERATOR_STATS¶
返回有关已完成查询中各个查询运算符的统计信息。您可以对过去 14 天内执行的任何已完成的查询运行此函数。
您可以使用此信息来了解查询的结构,并识别引起性能问题的查询运算符(例如,联接运算符)。
例如,您可以使用此信息来确定哪些运算符消耗的资源最多。此外,您还可以使用此函数来识别联接中输出行多于输入行的情况,这种情况可能是 “分解”联接 (例如,意外的笛卡尔积)的标志。
Snowsight 中的 查询配置文件 选项卡中也提供了这些统计信息。GET_QUERY_OPERATOR_STATS()
函数通过编程接口提供相同的信息。
有关查找有问题的查询运算符的更多信息,请参阅 查询配置文件识别的常见查询问题。
语法¶
GET_QUERY_OPERATOR_STATS( <query_id> )
实参¶
query_id
查询的 ID。您可以使用:
字符串字面量(由单引号括起来的字符串)。
包含查询 ID 的 会话变量。
LAST_QUERY_ID 函数调用的返回值。
返回¶
GET_QUERY_OPERATOR_STATS 函数是一个 表函数。该函数返回包含有关查询中每个查询运算符的统计信息的行。有关更多信息,请参阅以下章节:使用说明 和 输出。
使用说明¶
此函数仅返回已完成查询的统计信息。
您必须具有运行查询的仓库的 OPERATE 或 MONITOR 权限。
此函数提供有关指定查询中使用的每个查询运算符的详细统计信息。可能的查询运算符包括:
Aggregate:对输入进行分组并计算聚合函数。
CartesianJoin:一种特殊类型的联接。
Delete:从表中移除记录。
ExternalFunction:表示外部函数执行的处理。
ExternalScan:表示对存储在暂存区对象中的数据的访问。
Filter:表示筛选行的操作。
Flatten:处理 VARIANT 记录,可能在指定路径上展平记录。
Generator:使用 TABLE(GENERATOR(...)) 构造生成记录。
GroupingSets:表示 GROUPING、SETS、ROLLUP 和 CUBE 等构造。
Insert:通过 INSERT 或 COPY 操作将记录添加到表中。
InternalObject:表示对内部数据对象(例如在 Information Schema 或上一个查询的结果中)的访问。
Join:在给定条件下组合两个输入。
JoinFilter:特殊筛选操作,用于移除识别为可能不符合查询计划中进一步联接条件的元组。
Merge:对表执行 MERGE 操作。
Pivot:将唯一值从一列转换为多列,并进行任何必要的聚合。
Result:返回查询结果。
Sort:对给定表达式的输入进行排序。
SortWithLimit:排序后生成输入序列的一部分,通常是
ORDER BY ... LIMIT ... OFFSET ...
构造的结果。TableScan:表示对单个表的访问。
UnionAll:连接两个输入。
Unload:表示将数据从表导出到暂存区中的文件的 COPY 操作。
Unpivot:通过将列转换为行来旋转表。
Update:更新表中的记录。
ValuesClause:随 VALUES 子句提供的值列表。
WindowFunction:计算窗口函数。
WithClause:位于 SELECT 语句正文之前,并定义一个或多个 CTEs。
WithReference:WITH 子句的实例。
信息以表的形式返回。表中的每一行对应一个运算符。行包含该运算符的执行细分和查询统计信息。
该行还可以列出 操作符属性 (这些属性取决于操作符的类型)。
细分查询执行时间的统计信息可表示为总查询所用时间的百分比。
有关特定统计信息的更多信息,请参阅 输出 (本主题内容)。
因为此函数是表函数,所以必须在 FROM 子句中使用它,并且必须将它封装在
TABLE()
中。例如:select * from table(get_query_operator_stats(last_query_id()));
输出¶
该函数返回以下列:
列名称 |
数据类型 |
描述 |
---|---|---|
QUERY_ID |
VARCHAR |
查询 ID,它是 SQL 语句的内部系统生成的标识符。 |
STEP_ID |
NUMBER(38, 0) |
查询计划中步骤的标识符。 |
OPERATOR_ID |
NUMBER(38, 0) |
运算符的标识符。这在查询中是唯一的。值从 0 开始。 |
PARENT_OPERATORS |
包含一个或多个 NUMBER(38, 0) 的 ARRAY |
此运算符的父运算符的标识符;如果这是查询计划中的最后一个运算符(通常是 Result 运算符),则值为 NULL。 |
OPERATOR_TYPE |
VARCHAR |
查询运算符的类型(例如 |
包含 OBJECT 的 VARIANT |
有关运算符的统计信息(例如运算符的输出行数)。 |
|
包含 OBJECT 的 VARIANT |
有关运算符执行时间的信息。 |
|
包含 OBJECT 的 VARIANT |
有关运算符的信息。此信息取决于运算符类型。 |
如果运算符的特定列没有信息,则值为 NULL。
其中三列包含 OBJECTs。每个对象都包含键/值对。下表显示了与这些表中的键有关的信息。
OPERATOR_STATISTICS¶
OPERATOR_STATISTICS
列的 OBJECTs 中的字段提供有关运算符的其他信息。这些信息可以包括:
键 |
嵌套键(如果适用) |
数据类型 |
描述 |
---|---|---|---|
|
数据操作语言 (DML) 查询的统计信息: |
||
|
DOUBLE |
插入到一个表(或多个表)中的行数。 |
|
|
DOUBLE |
表中更新的行数。 |
|
|
DOUBLE |
从表中删除的行数。 |
|
|
DOUBLE |
数据导出期间卸载的行数。 |
|
|
与调用外部函数有关的信息。如果某个字段(例如 |
||
|
DOUBLE |
调用外部函数的次数。(这可能与 SQL 语句文本中的外部函数调用次数不同,原因可能是行被划分为的批次数量、重试次数[如果存在暂时性的网络问题]等) |
|
|
DOUBLE |
发送到外部函数的行数。 |
|
|
DOUBLE |
从外部函数接收的行数。 |
|
|
DOUBLE |
发送到外部函数的字节数。如果密钥包含 |
|
|
DOUBLE |
从外部函数接收的字节数。如果密钥包含 |
|
|
DOUBLE |
由于瞬时错误导致的重试次数。 |
|
|
DOUBLE |
Snowflake 发送数据和接收返回数据之间的每次调用花费的平均时间(以毫秒为单位)。 |
|
|
INTEGER |
返回 4xx 状态代码的 HTTP 请求的总数。 |
|
|
INTEGER |
返回 5xx 状态代码的 HTTP 请求的总数。 |
|
|
DOUBLE |
成功 HTTP 请求的平均延迟。 |
|
|
DOUBLE |
由于节流导致的速度减慢 (HTTP 429),每个成功请求的平均开销。 |
|
|
DOUBLE |
由于 HTTP 429 错误而重试的批次数。 |
|
|
DOUBLE |
成功 HTTP 请求的第 50 个百分位延迟。在所有成功请求中,有 50% 的请求完成时间少于此时间。 |
|
|
DOUBLE |
成功 HTTP 请求的第 90 个百分位延迟。在所有成功请求中,有 90% 的请求完成时间少于此时间。 |
|
|
DOUBLE |
成功 HTTP 请求的第 95 个百分位延迟。在所有成功请求中,有 95% 的请求完成时间少于此时间。 |
|
|
DOUBLE |
成功 HTTP 请求的第 99 个百分位延迟。在所有成功请求中,有 99% 的请求完成时间少于此时间。 |
|
|
INTEGER |
输入行数。对于没有来自其他运算符的输入边的运算符,这可能是缺失的。 |
|
|
有关查询期间执行的 I/O(输入/输出)操作的信息: |
||
|
DOUBLE |
到目前为止为给定表扫描的数据百分比。 |
|
|
DOUBLE |
到目前为止扫描的字节数。 |
|
|
DOUBLE |
从本地磁盘缓存扫描的数据所占的百分比。 |
|
|
DOUBLE |
写入的字节数(例如,加载到表中时)。 |
|
|
DOUBLE |
写入结果对象的字节数。 例如, 通常,结果对象表示作为查询结果生成的任何内容, |
|
|
DOUBLE |
从结果对象中读取的字节数。 |
|
|
DOUBLE |
从外部对象(例如暂存区)读取的字节数。 |
|
|
|
DOUBLE |
通过网络发送的数据量。 |
|
INTEGER |
输出行数。对于将结果返回给用户的运算符(通常是 RESULT 运算符),这可能是缺失的。 |
|
|
有关表修剪的信息。 |
||
|
DOUBLE |
到目前为止扫描的分区数。 |
|
|
DOUBLE |
给定表中的分区总数。 |
|
|
有关中间结果不适合内存的操作的磁盘使用情况的信息。 |
||
|
DOUBLE |
溢出到远程磁盘的数据量。 |
|
|
DOUBLE |
溢出到本地磁盘的数据量。 |
|
|
与调用扩展函数有关的信息。如果字段的值为零,则不显示该字段。 |
||
|
DOUBLE |
加载 Java UDF 处理程序的时间量。 |
|
|
DOUBLE |
调用 Java UDF 处理程序的次数。 |
|
|
DOUBLE |
执行 Java UDF 处理程序的最大时间量。 |
|
|
DOUBLE |
执行 Java UDF 处理程序的平均时间量。 |
|
|
DOUBLE |
调用 Java UDTF 过程方法 的次数。 |
|
|
DOUBLE |
执行 Java UDTF 过程的时间量。 |
|
|
DOUBLE |
执行 Java UDTF 过程的平均时间量。 |
|
|
DOUBLE |
调用 Java UDTF 构造函数 的次数。 |
|
|
DOUBLE |
执行 Java UDTF 构造函数的时间量。 |
|
|
DOUBLE |
执行 Java UDTF 构造函数的平均时间量。 |
|
|
DOUBLE |
调用 Java UDTF endPartition 方法 的次数。 |
|
|
DOUBLE |
执行 Java UDTF endPartition 方法的时间量。 |
|
|
DOUBLE |
执行 Java UDTF endPartition 方法的平均时间量。 |
|
|
DOUBLE |
下载 Java UDF 依赖项的最大时间量。 |
|
|
DOUBLE |
JVM 报告的内存使用量峰值。 |
|
|
DOUBLE |
Java UDF 内联代码的编译时间。 |
|
|
DOUBLE |
调用 Python UDF 处理程序的次数。 |
|
|
DOUBLE |
Python UDF 处理程序的总执行时间。 |
|
|
DOUBLE |
执行 Python UDF 处理程序的平均时间量。 |
|
|
DOUBLE |
Python 沙盒环境的内存使用量峰值。 |
|
|
DOUBLE |
创建 Python 环境(包括下载和安装包)的平均时间量。 |
|
|
DOUBLE |
运行 Conda 求解器对 Python 包求解所需的时间量。 |
|
|
DOUBLE |
创建 Python 环境的时间量。 |
|
|
DOUBLE |
初始化 Python UDF 的时间量。 |
|
|
DOUBLE |
读取的 UDFs 外部文件字节数。 |
|
|
DOUBLE |
访问的 UDFs 外部文件数。 |
EXECUTION_TIME_BREAKDOWN¶
EXECUTION_TIME_BREAKDOWN
列的 OBJECTs 中的字段如下所示。
键 |
数据类型 |
描述 |
---|---|---|
|
DOUBLE |
此运算符花费的总查询时间的百分比。 |
|
DOUBLE |
设置查询处理所用的时间。 |
|
DOUBLE |
CPU 处理数据所用的时间。 |
|
DOUBLE |
同步参与进程之间的活动所用的时间。 |
|
DOUBLE |
等待本地磁盘访问时阻止处理的时间。 |
|
DOUBLE |
等待远程磁盘访问时阻止处理的时间。 |
|
DOUBLE |
处理等待网络数据传输的时间。 |
OPERATOR_ATTRIBUTES¶
每个输出行描述查询中的一个运算符。下表显示了可能的运算符类型(例如 Filter 运算符)。对于每种类型的运算符,该表显示了可能的属性(例如,用于筛选行的表达式)。
运算符属性存储在 OPERATOR_ATTRIBUTES
列中,该列的类型为 VARIANT 并且包含 OBJECT。OBJECT 包含键值对。每个键对应运算符的一个属性。
运算符名称 |
键 |
数据类型 |
描述 |
---|---|---|---|
|
|||
|
VARCHAR 的 ARRAY |
计算的函数列表。 |
|
|
VARCHAR 的 ARRAY |
分组依据表达式。 |
|
|
|||
|
VARCHAR |
非相等联接表达式。 |
|
|
VARCHAR |
相等联接表达式。 |
|
|
VARCHAR |
联接 (INNER) 的类型。 |
|
|
|
VARCHAR |
更新表的名称。 |
|
|||
|
VARCHAR |
从中读取数据的暂存区的名称。 |
|
|
VARCHAR |
暂存区的类型。 |
|
|
|
VARCHAR |
用于筛选数据的表达式。 |
|
|
VARCHAR |
用于展平数据的输入表达式。 |
|
|||
|
NUMBER |
输入参数 ROWCOUNT 的值。 |
|
|
NUMBER |
输入参数 TIMELIMIT 的值。 |
|
|
|||
|
VARCHAR 的 ARRAY |
计算的函数列表。 |
|
|
VARCHAR 的 ARRAY |
分组集的列表。 |
|
|
|||
|
VARCHAR |
插入了哪些表达式。 |
|
|
VARCHAR 的 ARRAY |
添加记录的表名列表。 |
|
|
|
VARCHAR |
访问对象的名称。 |
|
|||
|
VARCHAR |
非相等联接表达式。 |
|
|
VARCHAR |
相等联接表达式。 |
|
|
VARCHAR |
联接的类型(INNER、OUTER、LEFT JOIN 等)。 |
|
|
|
NUMBER |
用于标识可筛选掉的元组的联接的运算符 ID。 |
|
|
VARCHAR |
更新表的名称。 |
|
|||
|
VARCHAR 的 ARRAY |
聚合结果的其余列。 |
|
|
VARCHAR 的 ARRAY |
生成的透视值列。 |
|
|
|
VARCHAR 的 ARRAY |
生成的表达式列表。 |
|
|
VARCHAR 的 ARRAY |
定义排序顺序的表达式。 |
|
|||
|
NUMBER |
生成的元组在已排列序列中的发出位置。 |
|
|
NUMBER |
生成的行数。 |
|
|
VARCHAR 的 ARRAY |
定义排序顺序的表达式。 |
|
|
|||
|
VARCHAR 的 ARRAY |
已扫描列的列表 |
|
|
VARCHAR 的 ARRAY |
从变体列提取的路径列表。 |
|
|
VARCHAR |
正在访问的表的别名。 |
|
|
VARCHAR |
正在访问的表的名称。 |
|
|
|
VARCHAR |
保存数据的暂存区。 |
|
|
VARCHAR 的 ARRAY |
取消透视查询的输出列。 |
|
|
VARCHAR |
更新表的名称。 |
|
|||
|
NUMBER |
生成的值的数量。 |
|
|
VARCHAR |
值的列表。 |
|
|
|
VARCHAR 的 ARRAY |
计算的函数列表。 |
|
|
VARCHAR |
WITH 子句的别名。 |
如果未列出运算符,则不生成任何属性,并将值报告为 {}
。
备注
以下运算符没有任何运算符属性,因此未包含在
OPERATOR_ATTRIBUTES
表 中:UnionAll
ExternalFunction
示例¶
检索关于单个查询的数据¶
此示例显示了联接两个小表的 SELECT 的统计信息。
运行 SELECT 语句:
select x1.i, x2.i
from x1 inner join x2 on x2.i = x1.i
order by x1.i, x2.i;
获取查询 ID:
set lqid = (select last_query_id());
调用 GET_QUERY_OPERATOR_STATS() 获取有关查询中各个查询运算符的统计信息:
select * from table(get_query_operator_stats($lqid));
+--------------------------------------+---------+-------------+--------------------+---------------+-----------------------------------------+-----------------------------------------------+----------------------------------------------------------------------+
| QUERY_ID | STEP_ID | OPERATOR_ID | PARENT_OPERATORS | OPERATOR_TYPE | OPERATOR_STATISTICS | EXECUTION_TIME_BREAKDOWN | OPERATOR_ATTRIBUTES |
|--------------------------------------+---------+-------------+--------------------+---------------+-----------------------------------------+-----------------------------------------------+----------------------------------------------------------------------|
| 01a8f330-0507-3f5b-0000-43830248e09a | 1 | 0 | NULL | Result | { | { | { |
| | | | | | "input_rows": 64 | "overall_percentage": 0.000000000000000e+00 | "expressions": [ |
| | | | | | } | } | "X1.I", |
| | | | | | | | "X2.I" |
| | | | | | | | ] |
| | | | | | | | } |
| 01a8f330-0507-3f5b-0000-43830248e09a | 1 | 1 | [ 0 ] | Sort | { | { | { |
| | | | | | "input_rows": 64, | "overall_percentage": 0.000000000000000e+00 | "sort_keys": [ |
| | | | | | "output_rows": 64 | } | "X1.I ASC NULLS LAST", |
| | | | | | } | | "X2.I ASC NULLS LAST" |
| | | | | | | | ] |
| | | | | | | | } |
| 01a8f330-0507-3f5b-0000-43830248e09a | 1 | 2 | [ 1 ] | Join | { | { | { |
| | | | | | "input_rows": 128, | "overall_percentage": 0.000000000000000e+00 | "equality_join_condition": "(X2.I = X1.I)", |
| | | | | | "output_rows": 64 | } | "join_type": "INNER" |
| | | | | | } | | } |
| 01a8f330-0507-3f5b-0000-43830248e09a | 1 | 3 | [ 2 ] | TableScan | { | { | { |
| | | | | | "io": { | "overall_percentage": 0.000000000000000e+00 | "columns": [ |
| | | | | | "bytes_scanned": 1024, | } | "I" |
| | | | | | "percentage_scanned_from_cache": 1, | | ], |
| | | | | | "scan_progress": 1 | | "table_name": "MY_DB.MY_SCHEMA.X2" |
| | | | | | }, | | } |
| | | | | | "output_rows": 64, | | |
| | | | | | "pruning": { | | |
| | | | | | "partitions_scanned": 1, | | |
| | | | | | "partitions_total": 1 | | |
| | | | | | } | | |
| | | | | | } | | |
| 01a8f330-0507-3f5b-0000-43830248e09a | 1 | 4 | [ 2 ] | JoinFilter | { | { | { |
| | | | | | "input_rows": 64, | "overall_percentage": 0.000000000000000e+00 | "join_id": "2" |
| | | | | | "output_rows": 64 | } | } |
| | | | | | } | | |
| 01a8f330-0507-3f5b-0000-43830248e09a | 1 | 5 | [ 4 ] | TableScan | { | { | { |
| | | | | | "io": { | "overall_percentage": 0.000000000000000e+00 | "columns": [ |
| | | | | | "bytes_scanned": 1024, | } | "I" |
| | | | | | "percentage_scanned_from_cache": 1, | | ], |
| | | | | | "scan_progress": 1 | | "table_name": "MY_DB.MY_SCHEMA.X1" |
| | | | | | }, | | } |
| | | | | | "output_rows": 64, | | |
| | | | | | "pruning": { | | |
| | | | | | "partitions_scanned": 1, | | |
| | | | | | "partitions_total": 1 | | |
| | | | | | } | | |
| | | | | | } | | |
+--------------------------------------+---------+-------------+--------------------+---------------+-----------------------------------------+-----------------------------------------------+----------------------------------------------------------------------+
识别“分解”联接运算符¶
以下示例演示了如何使用 GET_QUERY_OPERATOR_STATS 检查复杂查询。此示例在查询中查找生成的行数比输入到该运算符的行数多的运算符。
这是要分析的查询:
select *
from t1
join t2 on t1.a = t2.a
join t3 on t1.b = t3.b
join t4 on t1.c = t4.c
;
获取上一个查询的查询 ID :
set lid = last_query_id();
以下查询显示了查询中每个联接运算符的输出行与输入行的比率。
select
operator_id,
operator_attributes,
operator_statistics:output_rows / operator_statistics:input_rows as row_multiple
from table(get_query_operator_stats($lid))
where operator_type = 'Join'
order by step_id, operator_id;
+---------+-------------+--------------------------------------------------------------------------+---------------+
| STEP_ID | OPERATOR_ID | OPERATOR_ATTRIBUTES | ROW_MULTIPLE |
+---------+-------------+--------------------------------------------------------------------------+---------------+
| 1 | 1 | { "equality_join_condition": "(T4.C = T1.C)", "join_type": "INNER" } | 49.969249692 |
| 1 | 3 | { "equality_join_condition": "(T3.B = T1.B)", "join_type": "INNER" } | 116.071428571 |
| 1 | 5 | { "equality_join_condition": "(T2.A = T1.A)", "join_type": "INNER" } | 12.20657277 |
+---------+-------------+--------------------------------------------------------------------------+---------------+
识别分解联接后,可以查看每个联接条件以验证条件是否正确。