在 Redshift 中更新整个 table 的正确方法,drop table + create table vs. truncate + insert into table

Proper way of updating a whole table in Redshift, drop table + create table vs. truncate + insert into table

目前我有很多表,我必须更新它们所持有的信息,有时每天或每周更新一次。到目前为止,我一直在通过 DROP TABLE IF EXIST some_schema.some_table_name;CREATE TABLE some_schema.some_table_name AS ( SELECT ... FROM ... WHERE ...); 的组合来做到这一点,我想知道什么是“最佳实践”或正确的做法。

我读到 Redshift 中的 INSERT 操作非常昂贵,所以我一直在避免使用它,但也许将 TRUNCATEINSERT 一起使用比丢弃和创建。

如何确定哪个选项更好?

我看过 Redshift 文档中的 this 文章,但我不确定它是否是最佳选择,因为我不仅可以删除记录,还可以保留和插入记录。

我认为您需要使用更新命令。我知道降低 table 是一个冒险的举动,因为您可能会丢失数据库中的所有数据。

Update some_table_name s set
   s.Id="whatever you want to update",
   s.Name="whatever you want to update",
   s.LastName="whatever you want to update",
   s.OtherTableColumn="whatever you want to update"
From
   some_table_name s

在上面的代码中,我假设您的 table 具有列(1-Id、2-Name、3-LastName、4-OtherTableColumn)。如果您有更多或更少的列,那么我会相应地进行调整。

我还会为此(以及每个 table)编写一个更新程序,所以如果您需要稍微频繁地更新,您只需使用该程序即可;我认为它更快。以下是我的程序:

Create Proc sp_UpdateSome_table_name
   @Id int,
   @name nvarchar(255),
   @lastname nvarchar(255),
   @OtherTableColumn int
AS
BEGIN
Update s some_table_name
       
   s.Name="whatever you want to update",
   s.LastName="whatever you want to update",
   s.OtherTableColumn="whatever you want to update"
From
   some_table_name s
Where
   s.Id=@Id
END

您要确保 table 中的每一列在过程中都定义了正确的数据类型。例如,我在上面假设@Id 是 int,Name 是 nvarchar(255) 等。如果你想让自己在更新时不在某些 table 列中输入任何数据(允许 null),那么在数据类型之后你可以写Null;例如,如果你写@Id int Null,那么你可以更新为null;但如果您不确定这是什么,请暂时忽略这句话。

一旦你确定上面的段落是正确的(数据类型是正确的),那么select整个过程然后执行(F5)。这将存储此过程。

然后每次你要更新你的table我都会写程序如下:

Exec sp_UpdateSome_table_name 1,John,Smith,77

如果您突出显示上述命令并执行 (f5),那么它将更新 ID=1 的 table 并且它将使姓名 John、姓氏 Smith 和其他列 77 来自任何内容这是以前。如果Id=1的table里面没有数据那么就可以执行了。

请记住,代码的最后一行可能没有逗号。上面的代码都写对了,只是指出一下,因为你可能会把逗号不习惯。

如果您希望完全删除 table 并替换数据,那么您遵循的一般模式很好。但是,您应该做一些事情来让事情变得更安全/更好。

有 3 种模式可以执行此操作,其中一种显然是性能最低的。它们是 Delete/Insert、Truncate/Insert 和 Drop/Insert。从性能的角度来看,其中 Delete/Create/Insert 不是您想要做的。此过程使 table 中的所有行无效(而不是删除它们)并添加新的有效行。这会使 table 的大小加倍,浪费 space,并且需要清理。这种方法的唯一优点是它没有其他方法的缺点,但这只在某些情况下很重要。只有在必要时才使用这种方法。

Truncate/Insert 速度很快,并且保持与原始 table 相同的 table id。因为 truncate 对 table 的块进行操作(取消链接),所以它速度很快,但在管理所有块链接时会有一些小的开销。由于 table 定义未更改,因此所有 DDL 都保持定义状态,并且从属视图可以继续指向 table。截断的缺点是它会强制执行 COMMIT,这意味着在 table 重新填充新数据之前,数据库的其他用户可以看到空的 table。在 windows 期间,这可能会导致不正确的结果。不好。

最后是Drop/Create/Insert。在理想情况下,这种方法比截断快一点(非常轻微且仅适用于大 tables)。它只是扔掉旧块。设置新的 table(同名)会产生一些额外成本,因此除非 table 很大,否则截断和删除的速度大致相同。由于 Drop 可以在交易块内部,空 table 不会被第三方看到(如果正确完成)。这种方法的缺点是旧的 table 和新的 table 是完全不同的 table(不同的 oid)——它们恰好具有相同的名称。这意味着任何依赖(常规)视图也需要被删除并重新创建。此外,由于此 table 正在“消失”,因此在 table 的所有使用都完成之前,事务的提交无法完成。当有人在他们的工作台上打开交易并回家过夜时,这会成为一个大问题。由于需要重新创建 table,因此您的进程需要知道 table.

的完整且正确的 DDL

希望这能让您了解何时使用这些不同的方法。我看到两件事在您当前的代码中可能会更好 - 1)您没有使用事务块(据我所知)所以有一个 window 当其他人会看到 table s 不存在或为空。这对您来说可能重要也可能不重要,但请注意。 2) “Create table As”没有在性能结构中定义 table 的 DDL(并且可能不正确)。您应该始终完整地指定您的永久 table。 Sort 和 Dist 键与 varchar 长度、数据类型等一样重要。这是一颗等待爆炸的定时炸弹。

每个 drop/create/insert 示例的请求:

正如我所提到的,此方法可能会出现锁依赖性问题,因此我喜欢对此路径使用“交换和删除”方法。这使得新信息在“交换”时对用户可见,因此即使“掉落”被阻止,事情也会按时发布。这并没有消除锁定风险,因为锁定仍然可以阻止进程(会话)完成,它只是让新数据在您追捕违规者时可见(发布)。

(请注意,为了正确执行事务,您需要确保没有将额外的 COMMIT 插入到进程中。这可能发生在配置为“自动提交”模式的工作台上。)

Create table new_table ( ... ) ...;  -- make the new table but with a different name (and unique from other tables) than the existing table
Insert into new_table ... ; -- put the desired data into the new table
Analyze new_table;  -- to ensure metadata is up to date
Begin; -- start transaction
Alter table perm_table rename to old_table; -- rename existing table
Alter table new_table rename to perm_table; -- complete the swap
Commit; -- publish the new data for all to see but transactions still using the original data can keep doing so
Drop table old_table;  -- remove the old data to free up space
Commit; 

这个过程只是一个例子。有时您希望将 table 的旧版本保留一段时间(历史记录/错误恢复),因此您将对旧数据进行日期标记,并有一个单独的过程来释放 space。这也有助于防止杂散锁堵塞工作——只有清理过程会停滞。您还可以在流程中重新创建视图,以便在同一事务中更新这些视图。等等。