删除 Redshift 中的循环
Posted
技术标签:
【中文标题】删除 Redshift 中的循环【英文标题】:Remove loop in Redshift 【发布时间】:2021-05-31 12:51:03 【问题描述】:我有一个表,其中包含有关我数据库中不同表的信息。对于其中的每个表,我希望(每天)进行定期日志记录。问题是我已经使用 for 循环完成了这项工作,并且在 Redshift 中执行需要花费大量时间。我给出了用于创建表、日志记录表和使用 for 循环的过程的查询。 请建议一种替代方法来实现这一点而无需 for 循环。 包含不同表信息的表结构:
CREATE TABLE public.info_schema_table
(
info_schema_name character varying(200) ENCODE lzo,
info_object_name character varying(200) ENCODE lzo,
info_object_type character varying(200) ENCODE lzo,
info_object_full_name character varying(400) ENCODE lzo
)
DISTSTYLE EVEN;
这里,object name 是指表名,object_type 包含表是视图还是表,object_full_name 存储表的连接名称和模式,即如果模式名称是“test_schema”,那么全名是“test_schema” .table_name”。需要存储日志的表结构:
CREATE TABLE public.redshift_logging_table
(
log_schema_name character varying(30) ENCODE lzo,
log_object_name character varying(30) ENCODE lzo,
log_object_type character varying(30) ENCODE lzo,
log_refresh_date date ENCODE az64,
log_refresh_count bigint ENCODE az64,
log_total_count bigint ENCODE az64
)
DISTSTYLE EVEN;
这里,refresh_date 存储记录日期,refresh_count 存储在特定日期插入表中的记录数,total_count 包含到记录日期为止表中的记录总数。
为了澄清,这里是 info_schema_table 的一个示例记录:
这是我用来填充记录表的过程:
CREATE OR REPLACE PROCEDURE public.REDSHIFT_LOGGING_PROCEDURE()
AS $$
DECLARE
var_total_count bigint;
var_records_today bigint;
my_row record;
my_cursor CURSOR
FOR select info_schema_name, info_object_name, info_object_type , info_object_full_name from INFO_SCHEMA_TABLE ;
BEGIN
open my_cursor;
LOOP
FETCH my_cursor INTO my_row;
EXIT WHEN NOT FOUND;
BEGIN
EXECUTE ' select count(1)::bigint from '||my_row.INFO_OBJECT_FULL_NAME INTO var_total_count;
var_records_today=var_total_count-(select log_total_count from REDSHIFT_LOGGING_TABLE where REFRESH_DATE=current_date-1 and LOG_OBJECT_NAME=my_row.INFO_OBJECT_NAME);
insert into REDSHIFT_LOGGING_TABLE
(LOG_SCHEMA_NAME,LOG_OBJECT_NAME,LOG_OBJECT_TYPE,LOG_REFRESH_DATE, LOG_REFRESH_COUNT, LOG_TOTAL_COUNT )
values
(my_row.info_schema_name, my_row.info_object_name, my_row.info_object_type , current_date, var_records_today, var_total_count);
END;
END LOOP;
END;
$$ LANGUAGE plpgsql
SECURITY INVOKER;
所有表的记录数每天都在增加,因此在任何特定日期插入的记录始终 >=0。
问题是,此过程有效,但仅适用于 info_schema_table
中的小记录,如果我对大约 1000 条记录运行它,即使在一小时内,该过程也不会在 Redshift 上完成。
请建议一种替代方法来执行它而不使用 for 循环。谢谢。
【问题讨论】:
我首先要将此循环重写为单个(希望是)INSERT 操作。似乎可以实现,给我一些时间。 未来你也可以考虑改变 diststyles 以避免在节点之间移动数据。 【参考方案1】:好的,我们开始吧。
insert
正在工作,它显示了这如何在一个语句中工作:
insert into redshift_logging_table(log_schema_name, log_object_name, log_object_type, log_refresh_date, log_refresh_count, log_total_count)
select distinct
ist.info_schema_name,
ist.info_object_name,
ist.info_object_type,
current_date
, sti.tbl_rows - last_value(log_total_count ignore nulls) over (
partition by log_object_name order by log_refresh_date asc
rows between unbounded preceding and unbounded following
)
, sti.tbl_rows
from svv_table_info sti
inner join info_schema_table ist on ist.info_schema_name = sti.schema and ist.info_object_name = sti.table
left join redshift_logging_table rlt on rlt.log_schema_name = ist.info_schema_name and rlt.log_object_name = ist.info_object_name;
您可以仅使用select
而不使用insert
来验证它是如何工作的,这很好,因为它会显示将插入的内容而不实际执行。
这里发生了什么:
从from
开始:
info_schema_table
加入到 svv_table_info
以获取实际行数。它允许避免为每一行动态创建select count
语句;
接下来,对 redshift_logging_table
进行左连接 - 在某些对象尚不存在(新创建的等)的情况下使用左连接
在select
:
last_value
窗口函数(参见https://docs.aws.amazon.com/redshift/latest/dg/r_WF_first_value.html),它允许从partition by
和order by
描述的某些行中的特定列中查找值)。此值是为每个对象名称分别计算的日志表中的最后一次刷新计数。
您可能需要以一种或另一种方式对其进行调整,但我认为这是一个好的开始。此外,这可以只是移动到您的存储过程中。
享受吧!
【讨论】:
此查询运行良好。非常感谢@Adam Tokarski 帮助我。但是有什么方法可以通过动态获取列info_object_full_name
中不同表的计数来实现这一点,因为 svv_table_info tbl_rows 和实际数据存在一些差异。实际上,我想在不使用 svv 表的情况下执行此操作,而是使用 info_object_full_name
列。我问这个是因为我还有其他这样的程序要优化,它们使用 for 循环,并在它们的列上完成一些计算,然后插入到另一个表中。
在谈论计数时,您总是可以预料到这样的差异,尤其是在谈论 Redshit 时(参见 Max Ganz II (***.com/a/67778456/5172513) 的精彩回答)。但是如果想直接在每个表上做一些count(*)
,你需要构建动态sql,它可以在子查询中完成,但这又会很慢。可能不像循环那么慢,但仍然如此。
另外,我忘了提及 - 如果您在处理过程中出现漏洞,您的原始解决方案将失败 - 例如,如果有一天它因为 where REFRESH_DATE=current_date-1
而失败。在使用 Windows 功能的解决方案中 - 例如,参见我的 - 无论是昨天、上周还是去年,都有上次出现的使用值。所以应该更安全。
感谢@Adam Tokarski 的宝贵建议。我现在正在使用此查询进行日志记录,并且运行良好。除了有时不同表的 svv_table_info 表中的统计信息不会刷新,并且与实际计数存在一些差异。是否有任何查询可以包含在我的程序中,可以在我的实际程序开始之前刷新这些统计信息?非常感谢!!以上是关于删除 Redshift 中的循环的主要内容,如果未能解决你的问题,请参考以下文章