

 从补丁 198 开始，Amazon Redshift 将不再支持创建新的 Python UDF。现有的 Python UDF 将继续正常运行至 2026 年 6 月 30 日。有关更多信息，请参阅[博客文章](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)。

# 分析表
<a name="t_Analyzing_tables"></a>

ANALYZE 操作更新查询计划程序用来选择最佳计划的统计元数据。

在大多数情况下，您无需显式运行 ANALYZE 命令。Amazon Redshift 监控您工作负载的更改，并在后台自动更新统计数据。此外，COPY 命令会在将数据加载到空表时自动执行分析。

要明确分析表或整个数据库，请运行 [ANALYZE](r_ANALYZE.md) 命令。

## 自动分析
<a name="t_Analyzing_tables-auto-analyze"></a>

Amazon Redshift 持续监控您的数据库，并自动在后台执行分析操作。为了最大限度地降低对系统性能的影响，自动分析将在工作负载较轻的时段运行。

默认情况下会启用自动分析。要关闭自动分析，请通过修改集群的参数组来将 `auto_analyze` 参数设置为 **false**。

为了减少处理时间并提高整体系统性能，Amazon Redshift 将跳过对任何修改程度较小的表的自动分析。

分析操作将跳过具有最新统计数据的表。如果您将 ANALYZE 作为提取、转换和加载 (ETL) 工作流的一部分运行，则自动分析将跳过具有最新统计数据的表。类似地，在自动分析更新表的统计数据后，显式 ANALYZE 将跳过表。

## 分析新表数据
<a name="t_Analyzing_tables-new-tables"></a>

 默认情况下，COPY 命令会在将数据加载到空表后执行 ANALYZE。无论表是否为空，您都可以通过设置 STATUPDATE ON 来强制执行 ANALYZE。如果您指定 STATUPDATE OFF，则不会执行 ANALYZE。仅表所有者或超级用户才可以运行 ANALYZE 命令，或在 STATUPDATE 设置为 ON 时运行 COPY 命令。

Amazon Redshift 还分析您使用以下命令创建的新表：
+ CREATE TABLE AS (CTAS) 
+ CREATE TEMP TABLE AS 
+ SELECT INTO 

当您对最初加载其数据后未分析的新表运行查询时，Amazon Redshift 将返回一条警告消息。在后续更新或加载后查询表时，不会出现警告。在您对引用未经分析的表的查询运行 EXPLAIN 命令时，将返回相同的警告消息。

在通过将数据添加到非空表来明显更改表的大小时，您可以明确更新统计数据。可以通过运行 ANALYZE 命令或将 STATUPDATE ON 选项用于 COPY 命令来做到这一点。要查看有关自上次执行 ANALYZE 以来插入或删除的行数的详细信息，请查询 [PG\$1STATISTIC\$1INDICATOR](r_PG_STATISTIC_INDICATOR.md) 系统目录表。

您可以将 [ANALYZE](r_ANALYZE.md) 命令的范围指定为下列选项之一：
+ 整个当前数据库
+ 单个表
+ 单个表中的一个或多个特定列
+ 有可能在查询中用作谓词的列

 ANALYZE 命令将从表中获取行的采样，执行一些计算，并保存生成的列统计数据。预设情况下，Amazon Redshift 将为 DISTKEY 列运行一个采样过程，并为表中所有其他列运行另一个采样过程。如果您希望为一部分列生成统计数据，则可指定一个逗号分隔的列列表。您可以将 ANALYZE 与 PREDICATE COLUMNS 子句一起运行来跳过用作谓词的列。

 ANALYZE 操作是资源密集型的，因此仅对实际需要统计数据更新的表和列运行此类操作。您无需定期或按相同的计划分析所有表中的所有列。如果数据发生重大更改，请分析在以下操作中常用的列：
+ 排序和分组操作
+ 联接
+ 查询谓词

为了减少处理时间并提高整体系统性能，对于具有较低的更改行数百分比（由 [analyze\$1threshold\$1percent](r_analyze_threshold_percent.md) 参数决定）的任何表，Amazon Redshift 将跳过 ANALYZE。默认情况下，分析阈值将设置为 10%。可以通过运行 [SET](r_SET.md) 命令来更改当前会话的分析阈值。

不太可能需要频繁分析的列是表示从未被实际查询的事实和度量以及任何相关属性的列（例如，大量 VARCHAR 列）。例如，请考虑 TICKIT 数据库中的 LISTING 表。

```
select "column", type, encoding, distkey, sortkey
from pg_table_def where tablename = 'listing';


column         |        type        | encoding | distkey | sortkey 
---------------+--------------------+----------+---------+---------
listid         | integer            | none     | t       | 1       
sellerid       | integer            | none     | f       | 0       
eventid        | integer            | mostly16 | f       | 0       
dateid         | smallint           | none     | f       | 0       
numtickets     | smallint           | mostly8  | f       | 0       
priceperticket | numeric(8,2)       | bytedict | f       | 0       
totalprice     | numeric(8,2)       | mostly32 | f       | 0       
listtime       | timestamp with...  | none     | f       | 0
```

如果此表每天加载了大量新记录，则必须定期分析查询中频繁用作联接键的 LISTID 列。如果 TOTALPRICE 和 LISTTIME 是查询中的常用约束，则可在每个工作日分析这些列和分配键。

```
analyze listing(listid, totalprice, listtime);
```

假设应用程序中的卖家和事件变动小得多，且日期 ID 引用仅涵盖两年或三年的一组固定天数。在此情况下，这些列的唯一值将不会发生明显更改。但是，每个唯一值的实例数将平稳增加。

此外，请考虑查询 NUMTICKETS 和 PRICEPERTICKET 度量的频率低于查询 TOTALPRICE 列的频率这种情况。在这种情况下，您可在每个周末对整个表运行一次 ANALYZE 命令，以便更新未每日分析的 5 个列的统计数据：
<a name="t_Analyzing_tables-predicate-columns"></a>
**谓词列**  
作为指定列列表的便利替代方法，您可以选择仅分析可能用作谓词的列。当您运行查询时，在联接、筛选条件或 group by 子句中使用的任意列将在系统目录中标记为谓词列。当您使用 PREDICATE COLUMNS 子句运行 ANALYZE 时，分析操作仅包括满足以下标准的列：
+ 标记为谓词列的列。
+ 该列为分配键。
+ 该列为排序键的一部分。

如果未将任何表的列标记为谓词，则即使指定了 PREDICATE COLUMNS，ANALYZE 仍将包括所有列。如果未将任何列标记为谓词列，这可能是因为尚未查询表。

在工作负载的查询模式相对稳定时，您可以选择使用 PREDICATE COLUMNS。当查询模式可变并且不同的列频繁用作谓词时，使用 PREDICATE COLUMNS 可能会临时得到过时的统计数据。过时的统计数据可能导致查询运行时间计划不够理想和运行时间较长。不过，当您下次使用 PREDICATE COLUMNS 运行 ANALYZE 时，将包括新的谓词列。

要查看谓词列的详细信息，请使用以下 SQL 创建名为 PREDICATE\$1COLUMNS 的视图。

```
CREATE VIEW predicate_columns AS
WITH predicate_column_info as (
SELECT ns.nspname AS schema_name, c.relname AS table_name, a.attnum as col_num,  a.attname as col_name,
        CASE
            WHEN 10002 = s.stakind1 THEN array_to_string(stavalues1, '||') 
            WHEN 10002 = s.stakind2 THEN array_to_string(stavalues2, '||')
            WHEN 10002 = s.stakind3 THEN array_to_string(stavalues3, '||')
            WHEN 10002 = s.stakind4 THEN array_to_string(stavalues4, '||')
            ELSE NULL::varchar
        END AS pred_ts
   FROM pg_statistic s
   JOIN pg_class c ON c.oid = s.starelid
   JOIN pg_namespace ns ON c.relnamespace = ns.oid
   JOIN pg_attribute a ON c.oid = a.attrelid AND a.attnum = s.staattnum)
SELECT schema_name, table_name, col_num, col_name,
       pred_ts NOT LIKE '2000-01-01%' AS is_predicate,
       CASE WHEN pred_ts NOT LIKE '2000-01-01%' THEN (split_part(pred_ts, '||',1))::timestamp ELSE NULL::timestamp END as first_predicate_use,
       CASE WHEN pred_ts NOT LIKE '%||2000-01-01%' THEN (split_part(pred_ts, '||',2))::timestamp ELSE NULL::timestamp END as last_analyze
FROM predicate_column_info;
```

假设您对 LISTING 表运行以下查询。请注意，LISTID、LISTTIME 和 EVENTID 均用于联接、筛选和 group by 子句中。

```
select s.buyerid,l.eventid, sum(l.totalprice)
from listing l
join sales s on l.listid = s.listid
where l.listtime > '2008-12-01'
group by l.eventid, s.buyerid;
```

当您查询 PREDICATE\$1COLUMNS 视图时，如下例中所示，可以看到 LISTID、EVENTID 和 LISTTIME 标记为谓词列。

```
select * from predicate_columns 
where table_name = 'listing';
```

```
schema_name | table_name | col_num | col_name       | is_predicate | first_predicate_use | last_analyze       
------------+------------+---------+----------------+--------------+---------------------+--------------------
public      | listing    |       1 | listid         | true         | 2017-05-05 19:27:59 | 2017-05-03 18:27:41
public      | listing    |       2 | sellerid       | false        |                     | 2017-05-03 18:27:41
public      | listing    |       3 | eventid        | true         | 2017-05-16 20:54:32 | 2017-05-03 18:27:41
public      | listing    |       4 | dateid         | false        |                     | 2017-05-03 18:27:41
public      | listing    |       5 | numtickets     | false        |                     | 2017-05-03 18:27:41
public      | listing    |       6 | priceperticket | false        |                     | 2017-05-03 18:27:41
public      | listing    |       7 | totalprice     | false        |                     | 2017-05-03 18:27:41
public      | listing    |       8 | listtime       | true         | 2017-05-16 20:54:32 | 2017-05-03 18:27:41
```

通过允许查询计划程序选择最佳计划，使统计数据保持最新可提高查询性能。Amazon Redshift 在后台自动刷新统计数据，您也可以明确运行 ANALYZE 命令。如果您选择明确运行 ANALYZE，请执行以下操作：
+ 在运行查询之前运行 ANALYZE 命令。
+ 在每次定期加载或更新循环结束时，常规性地对数据库运行 ANALYZE 命令。
+ 对您创建的任何新表和正在进行重大更改的任何现有表或列运行 ANALYZE 命令。
+ 考虑按不同计划对不同类型的表和列运行 ANALYZE 操作，具体取决于它们在查询中的使用和它们的更改倾向。
+ 为节省时间和集群资源，在运行 ANALYZE 时请使用 PREDICATE COLUMNS 子句。

您不必在将快照还原到预置集群或无服务器命名空间之后显式运行 ANALYZE 命令，也不必在恢复已暂停的预置集群之后显式运行此命令。在这些情况下，Amazon Redshift 会保留系统表信息，从而无需手动执行 ANALYZE 命令。Amazon Redshift 将继续根据需要运行自动分析操作。

分析操作将跳过具有最新统计数据的表。如果您将 ANALYZE 作为提取、转换和加载 (ETL) 工作流的一部分运行，则自动分析将跳过具有最新统计数据的表。类似地，在自动分析更新表的统计数据后，显式 ANALYZE 将跳过表。

## ANALYZE 命令历史记录
<a name="c_check_last_analyze"></a>

了解上次对表或数据库运行 ANALYZE 命令的时间很有用。运行 ANALYZE 命令时，Amazon Redshift 将运行与以下内容类似的多个查询：

```
padb_fetch_sample: select * from table_name
```

查询 STL\$1ANALYZE 以查看分析操作的历史记录。如果 Amazon Redshift 使用自动分析功能来分析表，则 `is_background` 列将设置为 `t`（真）。否则，它将设置为 `f`（假）。以下示例联接 STV\$1TBL\$1PERM 以显示表名称和运行时间详细信息。

```
select distinct a.xid, trim(t.name) as name, a.status, a.rows, a.modified_rows, a.starttime, a.endtime
from stl_analyze a 
join stv_tbl_perm t  on t.id=a.table_id
where name = 'users'
order by starttime;


xid    | name  | status          | rows  | modified_rows | starttime           | endtime            
-------+-------+-----------------+-------+---------------+---------------------+--------------------
  1582 | users | Full            | 49990 |         49990 | 2016-09-22 22:02:23 | 2016-09-22 22:02:28
244287 | users | Full            | 24992 |         74988 | 2016-10-04 22:50:58 | 2016-10-04 22:51:01
244712 | users | Full            | 49984 |         24992 | 2016-10-04 22:56:07 | 2016-10-04 22:56:07
245071 | users | Skipped         | 49984 |             0 | 2016-10-04 22:58:17 | 2016-10-04 22:58:17
245439 | users | Skipped         | 49984 |          1982 | 2016-10-04 23:00:13 | 2016-10-04 23:00:13
(5 rows)
```

或者，您可以运行一个更复杂的查询，该查询将返回在每个包括 ANALYZE 命令的已完成事务中运行的所有语句：

```
select xid, to_char(starttime, 'HH24:MM:SS.MS') as starttime,
datediff(sec,starttime,endtime ) as secs, substring(text, 1, 40)
from svl_statementtext
where sequence = 0
and xid in (select xid from svl_statementtext s where s.text like 'padb_fetch_sample%' )
order by xid desc, starttime;

xid  |  starttime   | secs |                  substring
-----+--------------+------+------------------------------------------
1338 | 12:04:28.511 |    4 | Analyze date
1338 | 12:04:28.511 |    1 | padb_fetch_sample: select count(*) from
1338 | 12:04:29.443 |    2 | padb_fetch_sample: select * from date
1338 | 12:04:31.456 |    1 | padb_fetch_sample: select * from date
1337 | 12:04:24.388 |    1 | padb_fetch_sample: select count(*) from
1337 | 12:04:24.388 |    4 | Analyze sales
1337 | 12:04:25.322 |    2 | padb_fetch_sample: select * from sales
1337 | 12:04:27.363 |    1 | padb_fetch_sample: select * from sales
...
```