如何在 Redshift 中批量更新设置值?
how to do bulk update set values in Redshift?
我已经看到其他数据库的类似问题,但不确定它们是否适用于 Redshift。
我有一个像这样的更新语句,需要应用到很多列:
update t1 set col1 = '' where col1 is null;
现在我正在为 30 列执行此操作。有没有办法动态地或从列表中做?
我可以在存储过程或函数中做吗?
这里的问题有两个。您想要查找文本列的列表并且您不想 运行 N 更新,因为这可能会创建大量无效行,丢失 space,并且需要清理。
第二个问题是因为 Redshift 是一个列式数据库,并没有真正更新行,它会使现有行无效并添加一个新行。如果您要清除大型 table 中很多行中的 NULL,这可能会产生许多需要清理的无效行。如果您要 运行 进行许多更新,每列一个更新,情况会变得更糟,因为每个更新都可能为每个更新的同一行制作一个新版本(留下无效行)。在这种情况下,您只想 运行 一次更新所有列和所有行的一次 UPDATE 是最好的。 (下面的代码)
但是,如果您只更新 table 中的几行(低 NULL 计数),那么只更新那些具有 NULL 的行会更好。在这种情况下,您确实希望添加 WHERE 子句以防止精确复制不包含 NULL 的行。这很简单:
设置测试用例:
create table fred (
col_a varchar(8),
col_b varchar(8),
col_c varchar(8));
insert into fred values
('a', NULL, NULL),
(NULL, 'b', NULL),
(NULL, NULL, 'c');
现在,如果我们想更新所有行的所有列,我们可以 运行:
update fred set
col_a = nvl(col_a,''),
col_b = nvl(col_b,''),
col_c = nvl(col_c,'')
where col_a is null or col_b is null or col_c is null;
回到问题的第一部分 - 如何生成列列表并动态地进行此查询?。我喜欢为 Redshift 使用外部 DDL 定义文件,因为这使得链接 Redshift 数据和其他 AWS 数据更加容易。一个源文件,可以从中生成 Redshift DDL 和 Athena DDL 以及任何其他 DDL。但这不太可能是你的情况。
Table DDL 可在许多系统 table 的 Redshift 中访问。在这种情况下最容易访问的可能是 information_schema.columns。喜欢:
select column_name, data_type from information_schema.columns where table_schema = 'public' and table_name = 'fred';
这将为您提供任何 table 中列的名称和数据类型。由于您将值设置为“”,因此此过程仅适用于文本数据类型,因此您可能需要为要更改的数据类型添加一个额外的 WHERE 子句。
现在有了列列表,您需要创建上面的查询。这只是遍历列并生成 SELECT 部分的字符串部分和上面查询的 WHERE 部分。这些有很多方法可以做到这一点。我个人喜欢 jinja2 将列表应用于模板以生成配置的文本(如查询)。我还喜欢让 Redshift 专注于执行繁重的分析查询,而不是让它成为一个操作环境,所以我倾向于远离存储过程。但是,如果您愿意,没有理由不能将此过程编码为存储过程。如果我需要定期 运行 这个数据清理过程,我会将其编码为我的 ETL(ETL 工具发出编译查询)或 Lambda 函数的一部分。但这就是我。
我已经看到其他数据库的类似问题,但不确定它们是否适用于 Redshift。
我有一个像这样的更新语句,需要应用到很多列:
update t1 set col1 = '' where col1 is null;
现在我正在为 30 列执行此操作。有没有办法动态地或从列表中做?
我可以在存储过程或函数中做吗?
这里的问题有两个。您想要查找文本列的列表并且您不想 运行 N 更新,因为这可能会创建大量无效行,丢失 space,并且需要清理。
第二个问题是因为 Redshift 是一个列式数据库,并没有真正更新行,它会使现有行无效并添加一个新行。如果您要清除大型 table 中很多行中的 NULL,这可能会产生许多需要清理的无效行。如果您要 运行 进行许多更新,每列一个更新,情况会变得更糟,因为每个更新都可能为每个更新的同一行制作一个新版本(留下无效行)。在这种情况下,您只想 运行 一次更新所有列和所有行的一次 UPDATE 是最好的。 (下面的代码)
但是,如果您只更新 table 中的几行(低 NULL 计数),那么只更新那些具有 NULL 的行会更好。在这种情况下,您确实希望添加 WHERE 子句以防止精确复制不包含 NULL 的行。这很简单:
设置测试用例:
create table fred (
col_a varchar(8),
col_b varchar(8),
col_c varchar(8));
insert into fred values
('a', NULL, NULL),
(NULL, 'b', NULL),
(NULL, NULL, 'c');
现在,如果我们想更新所有行的所有列,我们可以 运行:
update fred set
col_a = nvl(col_a,''),
col_b = nvl(col_b,''),
col_c = nvl(col_c,'')
where col_a is null or col_b is null or col_c is null;
回到问题的第一部分 - 如何生成列列表并动态地进行此查询?。我喜欢为 Redshift 使用外部 DDL 定义文件,因为这使得链接 Redshift 数据和其他 AWS 数据更加容易。一个源文件,可以从中生成 Redshift DDL 和 Athena DDL 以及任何其他 DDL。但这不太可能是你的情况。
Table DDL 可在许多系统 table 的 Redshift 中访问。在这种情况下最容易访问的可能是 information_schema.columns。喜欢:
select column_name, data_type from information_schema.columns where table_schema = 'public' and table_name = 'fred';
这将为您提供任何 table 中列的名称和数据类型。由于您将值设置为“”,因此此过程仅适用于文本数据类型,因此您可能需要为要更改的数据类型添加一个额外的 WHERE 子句。
现在有了列列表,您需要创建上面的查询。这只是遍历列并生成 SELECT 部分的字符串部分和上面查询的 WHERE 部分。这些有很多方法可以做到这一点。我个人喜欢 jinja2 将列表应用于模板以生成配置的文本(如查询)。我还喜欢让 Redshift 专注于执行繁重的分析查询,而不是让它成为一个操作环境,所以我倾向于远离存储过程。但是,如果您愿意,没有理由不能将此过程编码为存储过程。如果我需要定期 运行 这个数据清理过程,我会将其编码为我的 ETL(ETL 工具发出编译查询)或 Lambda 函数的一部分。但这就是我。