可以在 Vertica 中创建两种类型的原生表(ROS 格式):列式表和 Flex 表。可以将这两种类型的表创建为永久表或临时表。还可以创建查询一组特定表列的视图。
此部分中描述的表将其数据存储在 Vertica 数据库中,并由 Vertica 数据库进行管理。Vertica 还支持在数据库中定义并在外部存储数据的外部表。有关外部表的详细信息,请参阅使用外部数据。
可以在 Vertica 中创建两种类型的原生表(ROS 格式):列式表和 Flex 表。可以将这两种类型的表创建为永久表或临时表。还可以创建查询一组特定表列的视图。
此部分中描述的表将其数据存储在 Vertica 数据库中,并由 Vertica 数据库进行管理。Vertica 还支持在数据库中定义并在外部存储数据的外部表。有关外部表的详细信息,请参阅使用外部数据。
使用 CREATE TABLE 语句在 Vertica 逻辑架构中创建原生表。可以直接指定列,如以下示例所示,也可以使用 LIKE 或 AS 子句从另一个表派生表定义。可以指定约束、分区、分段和其他因素。有关详细信息和限制,请参阅参考页面。
以下示例显示了基本表定义:
=> CREATE TABLE orders(
orderkey INT,
custkey INT,
prodkey ARRAY[VARCHAR(10)],
orderprices ARRAY[DECIMAL(12,2)],
orderdate DATE
);
与传统数据库将数据存储在表中不同,Vertica 会将表数据以物理方式存储在 投影(即表列的集合)中。投影以优化查询执行的格式存储数据。与实体化视图类似,它们会将结果集存储在磁盘上,而不是每次在查询中使用时都进行计算。
为了对 Vertica 表进行查询或执行任何操作,该表必须具有一个或多个与之关联的 投影。有关详细信息,请参阅投影。
可以使用 INFER_TABLE_DDL 函数检查 Parquet、ORC、JSON 或 Avro 数据,并为表定义生成一个起点。此函数返回 CREATE TABLE 语句,它可能需要进一步编辑。对于函数无法推断数据类型的列,该函数会将类型标记为未知并发出警告。对于 VARCHAR 和 VARBINARY 列,可能需要调整长度。请始终查看函数返回的语句,但特别是对于具有很多列的表,使用该函数可以节省时间和精力。
Parquet、ORC 和 Avro 文件包含架构信息,但 JSON 文件并不包含。对于 JSON,该函数检查原始数据以生成一个或多个候选表定义。有关 JSON 示例,请参阅函数参考页面。
在以下示例中,该函数从 Parquet 输入推断出完整的表定义,但 VARCHAR 列使用默认大小,可能需要进行调整:
=> SELECT INFER_TABLE_DDL('/data/people/*.parquet'
USING PARAMETERS format = 'parquet', table_name = 'employees');
WARNING 9311: This generated statement contains one or more varchar/varbinary columns which default to length 80
INFER_TABLE_DDL
-------------------------------------------------------------------------
create table "employees"(
"employeeID" int,
"personal" Row(
"name" varchar,
"address" Row(
"street" varchar,
"city" varchar,
"zipcode" int
),
"taxID" int
),
"department" varchar
);
(1 row)
对于 Parquet 文件,可以使用 GET_METADATA 函数检查文件并报告元数据,包括有关列的信息。
CREATE TEMPORARY TABLE
创建数据仅存在于当前会话中的表。临时表数据容始终对其他会话不可见。
默认情况下,所有临时表数据都为事务范围数据,也就是说,当 COMMIT
语句结束当前事务后,这些数据将被丢弃。如果 CREATE TEMPORARY TABLE
包括参数 ON COMMIT PRESERVE ROWS
,表数据会保留下来,直至当前会话结束。
临时表可用于将复杂查询处理分为多步来进行。通常情况下,报告工具会容纳创建报告过程中产生的中间结果 — 例如,工具首先获取一个结果集,然后查询该结果集,等等。
创建临时表后,Vertica 会自动为该表生成默认 投影。有关详细信息,请参阅自动投影。
CREATE TEMPORARY TABLE
可分别通过关键字 GLOBAL
和 LOCAL
在两个范围(全局和本地)创建表:
您可以指定临时表数据为事务范围还是会话范围数据:
[ON COMMIT DELETE ROWS](#on)
(默认):Vertica 会在每个事务结束时自动移除所有表数据。
[ON COMMIT PRESERVE ROWS](#on2)
:Vertica 会在当前会话中跨事务保留表数据。Vertica 会在会话结束时自动截断表。
如果使用 ON COMMIT PRESERVE ROWS
创建临时表,则在该表含有数据时无法为其添加投影。您必须先使用
TRUNCATE TABLE
移除该表中的所有数据。
您可以为使用 ON COMMIT DELETE ROWS
创建的临时表(无论其中是否填充了数据)创建投影。但是,CREATE PROJECTION
会结束您可能已添加数据的任何事务,因此投影始终为空。
ON COMMIT DELETE ROWS
默认情况下,Vertica 会在当前事务结束时从临时表(全局或本地)中移除所有数据。
例如:
=> CREATE TEMPORARY TABLE tempDelete (a int, b int);
CREATE TABLE
=> INSERT INTO tempDelete VALUES(1,2);
OUTPUT
--------
1
(1 row)
=> SELECT * FROM tempDelete;
a | b
---+---
1 | 2
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM tempDelete;
a | b
---+---
(0 rows)
如果需要,您可以在同一个事务中多次使用
DELETE
,以便反复刷新表数据。
ON COMMIT PRESERVE ROWS
您可以通过为临时表定义 ON COMMIT PRESERVE ROWS
关键字,指定临时表在当前会话中跨事务保留数据。Vertica 仅在当前会话结束时自动移除表中的所有数据。
例如:
=> CREATE TEMPORARY TABLE tempPreserve (a int, b int) ON COMMIT PRESERVE ROWS;
CREATE TABLE
=> INSERT INTO tempPreserve VALUES (1,2);
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM tempPreserve;
a | b
---+---
1 | 2
(1 row)
=> INSERT INTO tempPreserve VALUES (3,4);
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM tempPreserve;
a | b
---+---
1 | 2
3 | 4
(2 rows)
以下 Eon 模式限制适用于临时表:
CREATE TEMPORARY TABLE
语句将 k‑num 设置为大于 0,Vertica 会返回警告。
如果对当前会话的订阅发生更改,则该会话中的临时表将变得不可访问。会话订阅发生更改的原因包括:
某个节点离开了参与节点的列表。
新节点出现在了参与节点的列表中。
为一个或多个分片更改了活动节点。
通过显式调用
DO_TM_TASK('mergeout')
或使用
ALTER TABLE...ALTER COLUMN
更改列数据类型,在同一会话中由用户触发的合并操作。
可以使用带有 LIKE
子句的
CREATE TABLE
从现有表创建一个表:
CREATE TABLE [schema.]table-name LIKE [schema.]existing-table
[ {INCLUDING | EXCLUDING} PROJECTIONS ]
[ {INCLUDE | EXCLUDE} [SCHEMA] PRIVILEGES ]
使用 LIKE
创建表会复制源表定义以及与其关联的任何存储策略。它不会复制列上的表数据或表达式。
CREATE TABLE...LIKE
会复制所有表约束,但以下情况除外:
外键约束
从序列获取值的任何列,包括 IDENTITY
和 AUTO_INCREMENT
列。Vertica 会将列值复制到新表中,但会移除原始约束。例如,以下表定义对列 ID
设置 IDENTITY
约束:
CREATE TABLE public.Premium_Customer
(
ID IDENTITY ,
lname varchar(25),
fname varchar(25),
store_membership_card int
);
以下 CREATE TABLE...LIKE
语句将此表复制为 All_Customers
。Vertica 会从 All_Customers.ID
中移除 IDENTITY
约束,从而将其更改为具有 NOT NULL
约束的整数列:
=> CREATE TABLE All_Customers like Premium_Customer;
CREATE TABLE
=> select export_tables('','All_Customers');
export_tables
---------------------------------------------------
CREATE TABLE public.All_Customers
(
ID int NOT NULL,
lname varchar(25),
fname varchar(25),
store_membership_card int
);
(1 row)
可以使用 INCLUDING PROJECTIONS
或 EXCLUDING PROJECTIONS
限定 LIKE
子句,指定是否从源表中复制投影:
EXCLUDING PROJECTIONS
(默认值):不从源表复制投影。
INCLUDING PROJECTIONS
:从源表复制当前投影。Vertica 会根据 Vertica 命名约定命名新投影,以避免与现有对象发生名称冲突。
可以为新表指定架构权限的默认继承:
EXCLUDE [SCHEMA] PRIVILEGES
(默认)禁用从架构继承权限
INCLUDE [SCHEMA] PRIVILEGES
为表授予向其架构授予的相同权限
有关详细信息,请参阅设置表和视图的权限继承。
对于源表存在以下限制:
不能具有过期投影。
不能为临时表。
创建表 states
:
=> CREATE TABLE states (
state char(2) NOT NULL, bird varchar(20), tree varchar (20), tax float, stateDate char (20))
PARTITION BY state;
向表中填充数据:
INSERT INTO states VALUES ('MA', 'chickadee', 'american_elm', 5.675, '07-04-1620');
INSERT INTO states VALUES ('VT', 'Hermit_Thrasher', 'Sugar_Maple', 6.0, '07-04-1610');
INSERT INTO states VALUES ('NH', 'Purple_Finch', 'White_Birch', 0, '07-04-1615');
INSERT INTO states VALUES ('ME', 'Black_Cap_Chickadee', 'Pine_Tree', 5, '07-04-1615');
INSERT INTO states VALUES ('CT', 'American_Robin', 'White_Oak', 6.35, '07-04-1618');
INSERT INTO states VALUES ('RI', 'Rhode_Island_Red', 'Red_Maple', 5, '07-04-1619');
查看表内容:
=> SELECT * FROM states;
state | bird | tree | tax | stateDate
-------+---------------------+--------------+-------+----------------------
VT | Hermit_Thrasher | Sugar_Maple | 6 | 07-04-1610
CT | American_Robin | White_Oak | 6.35 | 07-04-1618
RI | Rhode_Island_Red | Red_Maple | 5 | 07-04-1619
MA | chickadee | american_elm | 5.675 | 07-04-1620
NH | Purple_Finch | White_Birch | 0 | 07-04-1615
ME | Black_Cap_Chickadee | Pine_Tree | 5 | 07-04-1615
(6 rows
创建示例投影并刷新:
=> CREATE PROJECTION states_p AS SELECT state FROM states;
=> SELECT START_REFRESH();
创建一个类似于 states
表的表并包括其投影:
=> CREATE TABLE newstates LIKE states INCLUDING PROJECTIONS;
查看这两个表的投影。Vertica 已将投影从 states
复制到 newstates
:
=> \dj
List of projections
Schema | Name | Owner | Node | Comment
-------------------------------+-------------------------------------------+---------+------------------+---------
public | newstates_b0 | dbadmin | |
public | newstates_b1 | dbadmin | |
public | newstates_p_b0 | dbadmin | |
public | newstates_p_b1 | dbadmin | |
public | states_b0 | dbadmin | |
public | states_b1 | dbadmin | |
public | states_p_b0 | dbadmin | |
public | states_p_b1 | dbadmin | |
查看表 newstates
,它显示从 states
复制的列:
=> SELECT * FROM newstates;
state | bird | tree | tax | stateDate
-------+------+------+-----+-----------
(0 rows)
使用 CREATE TABLE...LIKE
语句时,系统也会复制与表关联的存储策略对象。添加到新表的数据会使用与源表相同的标记存储位置,除非您更改了存储策略。有关详细信息,请参阅处理存储位置。
CREATE TABLE 可以指定 AS 子句以从查询创建表,如下所示:
CREATE [TEMPORARY] TABLE [schema.]table-name
[ ( column-name-list ) ]
[ {INCLUDE | EXCLUDE} [SCHEMA] PRIVILEGES ]
AS [ /*+ LABEL */ ] [ AT epoch ] query [ ENCODED BY column‑ref‑list ]
Vertica 会从查询结果创建一个表,并向其中加载结果集。例如:
=> CREATE TABLE cust_basic_profile AS SELECT
customer_key, customer_gender, customer_age, marital_status, annual_income, occupation
FROM customer_dimension WHERE customer_age>18 AND customer_gender !='';
CREATE TABLE
=> SELECT customer_age, annual_income, occupation FROM cust_basic_profile
WHERE customer_age > 23 ORDER BY customer_age;
customer_age | annual_income | occupation
--------------+---------------+--------------------
24 | 469210 | Hairdresser
24 | 140833 | Butler
24 | 558867 | Lumberjack
24 | 529117 | Mechanic
24 | 322062 | Acrobat
24 | 213734 | Writer
...
可以使用以下一个或两个选项来限定 AS 子句:
LABEL 提示,标识用于分析和调试的语句
AT epoch 子句,用于指定查询返回历史数据
可以在 AS 子句中的两个位置嵌入 LABEL 提示:
紧跟在关键字 AS 之后:
CREATE TABLE myTable AS /*+LABEL myLabel*/...
在 SELECT 语句中:
CREATE TABLE myTable AS SELECT /*+LABEL myLabel*/
如果 AS 子句在两个位置均包含 LABEL 提示,则第一个标签具有优先权。
可以使用 AT epoch 子句限定 CREATE TABLE AS 查询,以指定查询返回历史数据,其中 epoch 是以下值之一:
EPOCH LATEST:返回数据直到当前时期(但不包括当前时期)。结果集包括来自最新提交的 DML 事务的数据。
EPOCH integer:返回数据直到 integer 指定的时期(包括该指定时期)。
TIME 'timestamp':从 timestamp 指定的时期返回数据。
有关 Vertica 如何使用时期的详细信息,请参阅时期。
有关详细信息,请参阅历史查询。
如果查询返回零宽度列,Vertica 会自动将其转换为 VARCHAR(80) 列。例如:
=> CREATE TABLE example AS SELECT '' AS X;
CREATE TABLE
=> SELECT EXPORT_TABLES ('', 'example');
EXPORT_TABLES
----------------------------------------------------------
CREATE TEMPORARY TABLE public.example
(
X varchar(80)
);
如果从查询创建临时表,则必须指定 ON COMMIT PRESERVE ROWS 才能向该表中加载结果集。否则,Vertica 会创建一个空表。
如果查询输出含有除简单列以外的表达式,例如常量或函数,则必须为该表达式指定别名,或在列名列表中列出所有列。
不能将 CREATE TABLE AS SELECT 与返回复杂类型值的 SELECT 一起使用。但是,可以使用 CREATE TABLE LIKE。
许多安全系统包含的记录必须能够证明不会受到更改的影响。行和块校验和等保护策略会产生高开销。此外,对于数据库管理员或其他具有足够权限的用户进行的未经授权的更改(无论是有意还是无意),这些方法并非万无一失。
不可变表只能插入,无论用户权限如何,都无法修改其中的现有数据。禁止更新行值和删除行。还禁止对表元数据进行某些更改(例如,重命名表列),以防止试图规避这些限制。从外部源获取数据的扁平表或外部表不能设置为不可变。
可以使用 ALTER TABLE 将现有表定义为不可变表:
ALTER TABLE table SET IMMUTABLE ROWS;
一旦设置,表不变性将无法恢复,并立即应用于所有现有的表数据,以及此后加载的所有数据。为了修改不可变表的数据,必须将数据复制到新表,例如,使用 COPY、CREATE TABLE...AS 或 COPY_TABLE。
当对表执行 ALTER TABLE...SET IMMUTABLE ROWS 时,Vertica 会在系统表 TABLES 中为该表设置两列。这两列一起显示该表何时变为不可变表:
禁止对不可变表执行以下操作:
ALTER TABLE...ADD COLUMN,其中新列使用 SET USING 子句进行定义。
当目标表不可变时,不允许使用以下分区管理函数:
通常,可以对不影响现有行数据的不可变表执行任何 DML 操作,例如,使用 COPY 或 INSERT 添加行。将数据添加到不可变表后,将无法更改。
其他允许的操作通常分为两类:
对表的 DDL 的更改对其数据没有影响:
ALTER TABLE...SET SCHEMA:更改表架构。
ALTER TABLE...OWNER TO:转移表所有权。
ALTER TABLE...ALTER COLUMN...SET DATATYPE:更改列的数据类型。仅当列数据类型更改对列的存储数据没有影响时,才允许进行列数据类型更改。
对多个表行或整个表的块操作:
默认情况下,架构和表仅受可用磁盘空间和许可证容量的限制。可以为架构或单个表设置磁盘配额,例如为了支持多租户。设置、修改或移除磁盘配额需要超级用户权限。
增加存储大小的大多数用户操作都会强制实施磁盘配额。在某些操作(例如恢复)期间,表可能会暂时超出其配额。如果将配额降低到当前使用量以下,虽然不会丢失任何数据,但无法添加更多数据。请将配额视为建议,而不是硬性限制。
架构配额(如果已设置)必须大于其中的最大表配额。
磁盘配额是由整数和度量单位(K、M、G 或 T)组成的字符串,例如“15G”或“1T”。不要在数字和单位之间使用空格。不支持其他度量单位。
要在创建时设置配额,请为 CREATE SCHEMA 或 CREATE TABLE 使用 DISK_QUOTA 选项:
=> CREATE SCHEMA internal DISK_QUOTA '10T';
CREATE SCHEMA
=> CREATE TABLE internal.sales (...) DISK_QUOTA '5T';
CREATE TABLE
=> CREATE TABLE internal.leads (...) DISK_QUOTA '12T';
ROLLBACK 0: Table can not have a greater disk quota than its Schema
要修改、添加或移除现有架构或表的配额,请使用 ALTER SCHEMA 或 ALTER TABLE:
=> ALTER SCHEMA internal DISK_QUOTA '20T';
ALTER SCHEMA
=> ALTER TABLE internal.sales DISK_QUOTA SET NULL;
ALTER TABLE
可以设置低于当前使用量的配额。ALTER 操作成功后,架构或表暂时超出配额,此时将无法执行增加数据使用量的操作。
在 Eon 模式下,磁盘使用量是用于架构或表的所有分片使用的所有空间的总和。此值仅针对主订阅计算。
在企业模式下,磁盘使用量是所有节点上用于架构或表的所有存储容器使用的空间总和。该总和不包括伙伴实例投影,但包括所有其他投影。
磁盘使用量根据压缩大小进行计算。
配额(如果存在)影响大多数 DML 和 ILM 操作,包括:
添加或刷新列(请参阅 ALTER COLUMN)
表的子集还原(如果它会超出架构配额)
以下示例显示了超出表配额导致故障:
=> CREATE TABLE stats(score int) DISK_QUOTA '1k';
CREATE TABLE
=> COPY stats FROM STDIN;
1
2
3
4
5
\.
ERROR 0: Disk Quota Exceeded for the Table object public.stats
HINT: Delete data and PURGE or increase disk quota at the table level
DELETE 不会释放空间,因为已删除的数据仍保留在存储容器中。由删除操作添加的删除向量不计入配额,因此删除是与配额无关的操作。清除数据时回收已删除数据的磁盘空间;请参阅移除表数据。
一些不常见的操作(例如 ADD COLUMN、RESTORE 和 SWAP PARTITION)可能会在事务期间创建新的存储容器。这些操作在完成时会清理多余的位置,但在操作进行过程中,表或架构可能会超出其配额。如果在这些操作期间收到磁盘配额错误,则可以暂时增加配额,执行该操作,然后重置配额。
配额不影响恢复、重新平衡或 Tuple Mover 操作。
DISK_QUOTA_USAGES 系统表显示具有配额的表和架构的当前磁盘使用量。此表不报告没有配额的对象。
您可以使用此表监视使用量,并做出调整配额的决策:
=> SELECT * FROM DISK_QUOTA_USAGES;
object_oid | object_name | is_schema | total_disk_usage_in_bytes | disk_quota_in_bytes
-------------------+-------------+-----------+---------------------+---------------------
45035996273705100 | s | t | 307 | 10240
45035996273705104 | public.t | f | 614 | 1024
45035996273705108 | s.t | f | 307 | 2048
(3 rows)
在定义表之后,可以使用
ALTER TABLE
修改现有表列。可以对列执行以下操作:
使用 ALTER TABLE
重命名列,如下所示:
ALTER TABLE [schema.]table-name RENAME [ COLUMN ] column-name TO new-column-name
以下示例将 Retail.Product_Dimension
表中的列从 Product_description
重命名为 Item_description
:
=> ALTER TABLE Retail.Product_Dimension
RENAME COLUMN Product_description TO Item_description;
如果重命名视图引用的列,该列不会显示在视图的结果集中,即使视图使用通配符 (*) 来表示表中的所有列。重新创建视图以合并列的新名称。
通常,可以使用 ALTER TABLE 更改列的数据类型(如果这样做不需要重新组织存储)。修改列的数据类型后,加载的数据符合新定义。
以下几个部分介绍与更改列的数据类型相关的要求和限制。
Vertica 支持转换以下数据类型:
Vertica 不允许对需要重新组织存储的类型进行数据类型转换:
Boolean
日期/时间
近似数字类型
BINARY 到 VARBINARY,反之亦然
如果列属于以下情况之一,也不能更改列的数据类型:
主键
外键
包含在该表的任何投影的 SEGMENTED BY 子句中。
复杂类型列。有一个例外:在外部表中,可以将基元列类型更改为复杂类型。
可以绕过其中一些限制。有关详细信息,请参阅使用列数据转换。
可以扩展同一类数据类型中的列。这样做对于在列中存储较大的项目很有用。Vertica 在执行转换之前会验证数据。
通常,还可以减小数据类型类中的列宽。如果原始声明的长度比您需要的要长,这对于回收存储很有用,特别是对于字符串。仅当满足以下条件时,才能减小列宽:
现有列数据不大于新宽度。
数据库群集中的所有节点都已启动。
否则,Vertica 会返回错误并且转换失败。例如,如果尝试将列从 varchar(25)
转换为 varchar(10)
,只要所有列数据不超过 10 个字符,Vertica 就允许转换。
在以下示例中,列 y
和 z
最初定义为 VARCHAR 数据类型,并分别加载值 12345
和 654321
。将列 z
的宽度减小到 5 的尝试失败,因为它包含六个字符的数据。将列 y
的宽度减小到 5 的尝试成功,因为它的内容符合新的宽度:
=> CREATE TABLE t (x int, y VARCHAR, z VARCHAR);
CREATE TABLE
=> CREATE PROJECTION t_p1 AS SELECT * FROM t SEGMENTED BY hash(x) ALL NODES;
CREATE PROJECTION
=> INSERT INTO t values(1,'12345','654321');
OUTPUT
--------
1
(1 row)
=> SELECT * FROM t;
x | y | z
---+-------+--------
1 | 12345 | 654321
(1 row)
=> ALTER TABLE t ALTER COLUMN z SET DATA TYPE char(5);
ROLLBACK 2378: Cannot convert column "z" to type "char(5)"
HINT: Verify that the data in the column conforms to the new type
=> ALTER TABLE t ALTER COLUMN y SET DATA TYPE char(5);
ALTER TABLE
如果列是集合数据类型,则可以使用 ALTER TABLE 更改其边界或最大二进制大小。这些属性是在表创建时设置的,随后可以更改。
您可以使集合有界,设置其最大元素数,如下面的示例所示。
=> ALTER TABLE test.t1 ALTER COLUMN arr SET DATA TYPE array[int,10];
ALTER TABLE
=> \d test.t1
List of Fields by Tables
Schema | Table | Column | Type | Size | Default | Not Null | Primary Key | Foreign Key
--------+-------+--------+-----------------+------+---------+----------+-------------+-------------
test | t1 | arr | array[int8, 10] | 80 | | f | f |
(1 row)
或者,可以设置整个集合的二进制大小,而不是设置边界。二进制大小是显式设置的,也可以通过 DefaultArrayBinarySize 配置参数设置。下面的示例从默认值创建一个数组列,更改默认值,然后使用 ALTER TABLE 将其更改为新的默认值。
=> SELECT get_config_parameter('DefaultArrayBinarySize');
get_config_parameter
----------------------
100
(1 row)
=> CREATE TABLE test.t1 (arr array[int]);
CREATE TABLE
=> \d test.t1
List of Fields by Tables
Schema | Table | Column | Type | Size | Default | Not Null | Primary Key | Foreign Key
--------+-------+--------+-----------------+------+---------+----------+-------------+-------------
test | t1 | arr | array[int8](96) | 96 | | f | f |
(1 row)
=> ALTER DATABASE DEFAULT SET DefaultArrayBinarySize=200;
ALTER DATABASE
=> ALTER TABLE test.t1 ALTER COLUMN arr SET DATA TYPE array[int];
ALTER TABLE
=> \d test.t1
List of Fields by Tables
Schema | Table | Column | Type | Size | Default | Not Null | Primary Key | Foreign Key
--------+-------+--------+-----------------+------+---------+----------+-------------+-------------
test | t1 | arr | array[int8](200)| 200 | | f | f |
(1 row)
或者,可以显式设置二进制大小,而不是使用默认值。
=> ALTER TABLE test.t1 ALTER COLUMN arr SET DATA TYPE array[int](300);
如果 Vertica 保留任何超过新宽度的历史数据,则无法减小列的宽度。要减小列宽,首先从表中移除该数据:
将 AHM 推进到比需要从表中移除的历史数据更近的时期。
使用函数
PURGE_TABLE
清除表中 AHM 之前的所有历史数据。
例如,在前面的示例中,您可以更新 t.z
列中的数据,如下所示:
=> UPDATE t SET z = '54321';
OUTPUT
--------
1
(1 row)
=> SELECT * FROM t;
x | y | z
---+-------+-------
1 | 12345 | 54321
(1 row)
尽管现在 z 列中没有数据超过 5 个字符,但 Vertica 保留了其早期数据的历史记录,因此尝试将列宽减小到 5 会返回错误:
=> ALTER TABLE t ALTER COLUMN z SET DATA TYPE char(5);
ROLLBACK 2378: Cannot convert column "z" to type "char(5)"
HINT: Verify that the data in the column conforms to the new type
您可以通过清除表的历史数据来减小列宽,如下所示:
=> SELECT MAKE_AHM_NOW();
MAKE_AHM_NOW
-------------------------------
AHM set (New AHM Epoch: 6350)
(1 row)
=> SELECT PURGE_TABLE('t');
PURGE_TABLE
----------------------------------------------------------------------------------------------------------------------
Task: purge operation
(Table: public.t) (Projection: public.t_p1_b0)
(Table: public.t) (Projection: public.t_p1_b1)
(1 row)
=> ALTER TABLE t ALTER COLUMN z SET DATA TYPE char(5);
ALTER TABLE
Vertica 通过禁止对表列进行某些数据转换来符合 SQL 标准。但是,当从非 SQL 数据库转换数据时,有时需要绕过此限制。以下示例描述了一种这样的解决方法,使用下表:
=> CREATE TABLE sales(id INT, price VARCHAR) UNSEGMENTED ALL NODES;
CREATE TABLE
=> INSERT INTO sales VALUES (1, '$50.00');
OUTPUT
--------
1
(1 row)
=> INSERT INTO sales VALUES (2, '$100.00');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM SALES;
id | price
----+---------
1 | $50.00
2 | $100.00
(2 rows)
要将 price
列的现有数据类型从 VARCHAR 转换为 NUMERIC,请完成以下步骤:
添加一个新列供临时使用。为该列分配 NUMERIC 数据类型,并从现有 price 列中派生其默认值。
将列 temp_price
添加到表 sales
。可以暂时使用新列,将其数据类型设置为所需的数据类型 (NUMERIC),并从 price
列中派生其默认值。将新列的默认值转换为 NUMERIC 数据类型并查询该表:
=> ALTER TABLE sales ADD COLUMN temp_price NUMERIC(10,2) DEFAULT
SUBSTR(sales.price, 2)::NUMERIC;
ALTER TABLE
=> SELECT * FROM SALES;
id | price | temp_price
----+---------+------------
1 | $50.00 | 50.00
2 | $100.00 | 100.00
(2 rows)
使用 ALTER TABLE 从新列 temp_price
中删除默认表达式。Vertica 会保留存储在此列中的值:
=> ALTER TABLE sales ALTER COLUMN temp_price DROP DEFAULT;
ALTER TABLE
删除无关的 price
列。在这样做之前,必须先推进
AHM 以清除会阻止删除操作的历史数据:
推进 AHM:
=> SELECT MAKE_AHM_NOW();
MAKE_AHM_NOW
-------------------------------
AHM set (New AHM Epoch: 6354)
(1 row)
删除原始 price 列:
=> ALTER TABLE sales DROP COLUMN price CASCADE;
ALTER COLUMN
现在可以将 temp_price
列重命名为 price
:
使用 ALTER TABLE
重命名列:
=> ALTER TABLE sales RENAME COLUMN temp_price to price;
再次查询 sales 表:
=> SELECT * FROM sales;
id | price
----+--------
1 | 50.00
2 | 100.00
(2 rows)
您可以定义一个列,以便 Vertica 通过以下子句之一从表达式自动设置其值:
DEFAULT
SET USING
DEFAULT USING
DEFAULT 选项将列值设置为指定值。它具有以下语法:
DEFAULT default-expression
当您执行以下操作时会设置 Default 值:
将新行加载到表中,例如,使用 INSERT 或 COPY。Vertica 使用默认值填充新行中的 DEFAULT 列。现有行中的值(包括具有 DEFAULT 表达式的列)保持不变。
对表执行 UPDATE,并将 DEFAULT 列的值设置为 DEFAULT
:
=> UPDATE table-name SET column-name=DEFAULT;
将具有 DEFAULT 表达式的列添加到现有表。将新列添加到表中时,Vertica 会使用其默认值填充新列。
DEFAULT 表达式无法使用 ALTER TABLE...ADD COLUMN 指定易变函数。要指定易变函数,请使用 CREATE TABLE 或 ALTER TABLE...ALTER COLUMN 语句。
当对列调用 REFRESH_COLUMNS 函数时,SET USING 选项将该列值设置为表达式。此选项具有以下语法:
SET USING using-expression
这种方法对于大型非标准化(扁平)表很有用,其中多个列通过查询其他表来获取它们的值。
SET USING 具有以下限制:
不允许使用易变函数。
表达式不能指定序列。
Vertica 限制了复制表数据的多个元函数的使用:COPY_TABLE、COPY_PARTITIONS_TO_TABLE、MOVE_PARTITIONS_TO_TABLE 和 SWAP_PARTITIONS_BETWEEN_TABLES:
如果源表和目标表都具有 SET USING 列,则仅当每个源 SET USING 列具有相应的目标 SET USING 列时,才允许执行该操作。
如果只有源表具有 SET USING 列,则不允许使用 SWAP_PARTITIONS_BETWEEN_TABLES。
如果只有目标表具有 SET USING 列,则不允许执行该操作。
DEFAULT USING 选项对列设置 DEFAULT 和 SET USING 约束,相当于对同一列分别使用 DEFAULT 和 SET USING 执行相同的表达式。它具有以下语法:
DEFAULT USING expression
例如,以下列定义实际上是相同的:
=> ALTER TABLE public.orderFact ADD COLUMN cust_name varchar(20)
DEFAULT USING (SELECT name FROM public.custDim WHERE (custDim.cid = orderFact.cid));
=> ALTER TABLE public.orderFact ADD COLUMN cust_name varchar(20)
DEFAULT (SELECT name FROM public.custDim WHERE (custDim.cid = orderFact.cid))
SET USING (SELECT name FROM public.custDim WHERE (custDim.cid = orderFact.cid));
DEFAULT USING 支持与 SET USING 相同的表达式,并受到相同的限制。
通常,DEFAULT 和 SET USING 支持相同的表达式。这些包括:
以下限制适用于 DEFAULT 和 SET USING 表达式:
返回值数据类型必须与列数据类型匹配,或强制转换为列数据类型。
表达式必须返回一个符合列边界的值。例如,定义为 VARCHAR(1)
的列不能设置为默认字符串 abc
。
在临时表中,DEFAULT 和 SET USING 不支持子查询。如果尝试创建一个临时表,其中 DEFAULT 或 SET USING 使用子查询表达式,Vertica 会返回错误。
一个列的 SET USING 表达式不能在同一个表中指定另一个也使用 SET USING 设置其值的列。同样,一个列的 DEFAULT 表达式不能在同一个表中指定另一个也使用 DEFAULT 设置其值的列,或者其值自动设置为序列的列。但是,一个列的 SET USING 表达式可以指定另一个使用 DEFAULT 设置其值的列。
NULL
,因为它仅在最初将 SET USING 列设置为 NULL
的加载操作上设置。
DEFAULT 和 SET USING 表达式仅支持一个 SELECT 语句;尝试在该表达式中包含多个 SELECT 语句会返回错误。例如,给定表 t1
:
=> SELECT * FROM t1;
a | b
---+---------
1 | hello
2 | world
(2 rows)
尝试使用以下 DEFAULT 表达式创建表 t2
会返回错误:
=> CREATE TABLE t2 (aa int, bb varchar(30) DEFAULT (SELECT 'I said ')||(SELECT b FROM t1 where t1.a = t2.aa));
ERROR 9745: Expressions with multiple SELECT statements cannot be used in 'set using' query definitions
如果 SET USING 或 DEFAULT 查询表达式联接两个同名的列,则列名称必须包含它们的表名称。否则,Vertica 假定两个列都引用维度表,并且谓词的计算结果始终为 true。
例如,表 orderFact 和 custDim 都包含 cid 列。扁平表 orderFact 使用 SET USING 查询表达式定义列 cust_name。由于查询谓词引用两个表中的 cid 列,因此列名称是完全限定的:
=> CREATE TABLE public.orderFact
(
...
cid int REFERENCES public.custDim(cid),
cust_name varchar(20) SET USING (
SELECT name FROM public.custDim WHERE (custDIM.cid = orderFact.cid)),
...
)
创建表 t
,其中包含两个列 date
和 state
,并插入一行数据:
=> CREATE TABLE t (date DATE, state VARCHAR(2));
CREATE TABLE
=> INSERT INTO t VALUES (CURRENT_DATE, 'MA');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMMIT
SELECT * FROM t;
date | state
------------+-------
2017-12-28 | MA
(1 row)
使用 ALTER TABLE 添加第三个列,它从列 date
中提取整数月份值:
=> ALTER TABLE t ADD COLUMN month INTEGER DEFAULT date_part('month', date);
ALTER TABLE
当查询表 t
时,Vertica 会在列 date
中返回月份数:
=> SELECT * FROM t;
date | state | month
------------+-------+-------
2017-12-28 | MA | 12
(1 row)
通过从 date
中减去 30 天来更新表 t
:
=> UPDATE t SET date = date-30;
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM t;
date | state | month
------------+-------+-------
2017-11-28 | MA | 12
(1 row)
month
中的值保持不变。
从列 date
刷新 month
中的默认值:
=> UPDATE t SET month=DEFAULT;
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM t;
date | state | month
------------+-------+-------
2017-11-28 | MA | 11
(1 row)
本示例显示了添加了两个整数值的用户定义的标量函数。该函数名为 add2ints
,采用两个实参。
开发和部署函数,如标量函数 (UDSF) 中所述。
创建示例表 t1
,其中包含两个整数列:
=> CREATE TABLE t1 ( x int, y int );
CREATE TABLE
在 t1 中插入一些值:
=> insert into t1 values (1,2);
OUTPUT
--------
1
(1 row)
=> insert into t1 values (3,4);
OUTPUT
--------
1
(1 row)
使用 ALTER TABLE 向 t1
中添加列,其默认列值派生自 UDSF add2ints
:
alter table t1 add column z int default add2ints(x,y);
ALTER TABLE
列出新列:
select z from t1;
z
----
3
7
(2 rows)
定义表 t1
和 t2
。列 t2.b
被定义为通过其 SET USING 子句中的查询从列 t1.b
获取其数据:
=> CREATE TABLE t1 (a INT PRIMARY KEY ENABLED, b INT);
CREATE TABLE
=> CREATE TABLE t2 (a INT, alpha VARCHAR(10),
b INT SET USING (SELECT t1.b FROM t1 WHERE t1.a=t2.a))
ORDER BY a SEGMENTED BY HASH(a) ALL NODES;
CREATE TABLE
表 t2
的定义包括排除 SET USING 列 b
的 SEGMENTED BY 和 ORDER BY 子句。如果省略这些子句,Vertica 会为此表创建一个自动投影,在其 SEGMENTED BY 和 ORDER BY 子句中指定列 b
。在任何投影的分段或排序顺序中包含 SET USING 列会阻止 REFRESH_COLUMNS 函数填充此列。相反,它返回一个错误。
有关此限制和其他限制的详细信息,请参阅 REFRESH_COLUMNS。
使用数据填充表:
=> INSERT INTO t1 VALUES(1,11),(2,22),(3,33),(4,44);
=> INSERT INTO t2 VALUES (1,'aa'),(2,'bb');
=> COMMIT;
COMMIT
查看表 t2
中的数据:SET USING 列 b
中的列为空,等待调用 Vertica 函数 REFRESH_COLUMNS:
=> SELECT * FROM t2;
a | alpha | b
---+-------+---
1 | aa |
2 | bb |
(2 rows)
通过调用 REFRESH_COLUMNS 函数刷新表 t2
中的列数据:
=> SELECT REFRESH_COLUMNS ('t2','b', 'REBUILD');
REFRESH_COLUMNS
---------------------------
refresh_columns completed
(1 row)
在此示例中,使用可选实参 REBUILD 调用 REFRESH_COLUMNS。此实参指定替换 SET USING 列 b
中的所有数据。对任何新的 SET USING 列使用 REBUILD 调用 REFRESH_COLUMNS 通常是一种很好的做法。有关详细信息,请参阅REFRESH_COLUMNS。
查看刷新后的列 b
中的数据,其数据是从表 t1
中获取的,如列的 SET USING 查询中指定的那样:
=> SELECT * FROM t2 ORDER BY a;
a | alpha | b
---+-------+----
1 | aa | 11
2 | bb | 22
(2 rows)
DEFAULT 和 SET USING 表达式支持可以从其他表中获取值的子查询,并将这些子查询与当前表中的值结合使用来计算列值。以下示例将列 gmt_delivery_time
添加到事实表 customer_orders
中。该列指定一个 DEFAULT 表达式来设置新列中的值,如下所示:
调用元函数 NEW_TIME,它执行以下任务:
使用 customer_orders
中的客户密钥查询 customers
维度表以获得客户时区。
使用查询到的时区数据将本地交付时间转换为 GMT。
使用转换后的值填充 gmt_delivery_time
列。
=> CREATE TABLE public.customers(
customer_key int,
customer_name varchar(64),
customer_address varchar(64),
customer_tz varchar(5),
...);
=> CREATE TABLE public.customer_orders(
customer_key int,
order_number int,
product_key int,
product_version int,
quantity_ordered int,
store_key int,
date_ordered date,
date_shipped date,
expected_delivery_date date,
local_delivery_time timestamptz,
...);
=> ALTER TABLE customer_orders ADD COLUMN gmt_delivery_time timestamp
DEFAULT NEW_TIME(customer_orders.local_delivery_time,
(SELECT c.customer_tz FROM customers c WHERE (c.customer_key = customer_orders.customer_key)),
'GMT');
可以使用
ALTER TABLE
修改表的定义,以响应不断变化的数据库架构要求。更改表定义通常比在临时表中暂存数据更有效,它消耗的资源和存储空间更少。
对于列级别更改,请参阅管理表列。
有关更改和重新组织表分区的详细信息,请参阅对现有表数据进行分区。
可以使用 ALTER TABLE..ADD COLUMN 将列添加到持久表:
ALTER TABLE
...
ADD COLUMN [IF NOT EXISTS] column datatype
[column‑constraint]
[ENCODING encoding‑type]
[PROJECTIONS (projections‑list) | ALL PROJECTIONS ]
当使用 ADD COLUMN 更改表时,Vertica 会对该表采用 O 锁,直到操作完成。该锁可防止 DELETE、UPDATE、INSERT 和 COPY 语句访问表。该锁还会阻止在 SERIALIZABLE 隔离级别发出的 SELECT 语句,直到操作完成。
您可以在节点下线时添加列。
向表添加列时,Vertica 会自动将该列添加到该表的 超投影。ADD..COLUMN 子句还可以使用以下选项之一指定将列添加到一个或多个非超投影:
PROJECTIONS (projections-list
):将新列添加到该表的一个或多个投影,以投影基本名称的逗号分隔列表形式指定。Vertica 会将该列添加到每个投影的所有伙伴实例。投影列表不能包含具有预聚合数据的投影,例如实时聚合投影;否则,Vertica 会回退 ALTER TABLE 语句。
ALL PROJECTIONS
会将列添加到该表的所有投影,不包括具有预聚合数据的投影。
例如,store_orders
表有两个投影:超投影 store_orders_super
和用户创建的投影 store_orders_p
。以下 ALTER TABLE..ADD COLUMN 语句将列 expected_ship_date
添加到 store_orders
表。由于该语句省略了 PROJECTIONS
选项,因此 Vertica 仅将该列添加到表的超投影:
=> ALTER TABLE public.store_orders ADD COLUMN expected_ship_date date;
ALTER TABLE
=> SELECT projection_column_name, projection_name FROM projection_columns WHERE table_name ILIKE 'store_orders'
ORDER BY projection_name , projection_column_name;
projection_column_name | projection_name
------------------------+--------------------
order_date | store_orders_p_b0
order_no | store_orders_p_b0
ship_date | store_orders_p_b0
order_date | store_orders_p_b1
order_no | store_orders_p_b1
ship_date | store_orders_p_b1
expected_ship_date | store_orders_super
order_date | store_orders_super
order_no | store_orders_super
ship_date | store_orders_super
shipper | store_orders_super
(11 rows)
以下 ALTER TABLE...ADD COLUMN 语句包括 PROJECTIONS 选项。这指定在添加操作中包含投影 store_orders_p
。Vertica 将新列添加到此投影和表的超投影:
=> ALTER TABLE public.store_orders ADD COLUMN delivery_date date PROJECTIONS (store_orders_p);
=> SELECT projection_column_name, projection_name FROM projection_columns WHERE table_name ILIKE 'store_orders'
ORDER BY projection_name, projection_column_name;
projection_column_name | projection_name
------------------------+--------------------
delivery_date | store_orders_p_b0
order_date | store_orders_p_b0
order_no | store_orders_p_b0
ship_date | store_orders_p_b0
delivery_date | store_orders_p_b1
order_date | store_orders_p_b1
order_no | store_orders_p_b1
ship_date | store_orders_p_b1
delivery_date | store_orders_super
expected_ship_date | store_orders_super
order_date | store_orders_super
order_no | store_orders_super
ship_date | store_orders_super
shipper | store_orders_super
(14 rows)
将新列添加到具有关联视图的表时,系统不会更新视图的结果集,即便该视图使用了通配符 (*) 来表示所有表列。要整合新列,您必须重新创建视图。
ALTER TABLE...DROP COLUMN 会删除指定的表列以及与已删除的列对应的 ROS 容器:
ALTER TABLE [schema.]table DROP [ COLUMN ] [IF EXISTS] column [CASCADE | RESTRICT]
删除操作完成后,将恢复自当前时期起备份的数据,但不包含列。从当前时期之前的备份恢复的数据将重新添加表列。由于删除操作会从表中物理清除对象存储和编录定义(表历史记录),AT EPOCH(历史)查询对于已删除的列不返回任何内容。
更改的表保留其对象 ID。
不能删除或更改主键列或参与表分区子句的列。
不能删除任何投影排序顺序中的第一列,或参与投影分段表达式的列。
在企业模式下,所有节点必须都处于启动状态。此限制不适用于 Eon 模式。
不能删除与访问策略关联的列。尝试删除可能会导致以下错误:ERROR 6482: Failed to parse Access Policies for table "t1"
如果要删除的表列具有依赖项,则必须使用 CASCADE 选项来限定 DROP COLUMN 子句。例如,目标列可能会按投影排序顺序进行指定。在这种情况和其他情况下,DROP COLUMN...CASCADE 将通过重组编录定义或删除投影来处理依赖项。在所有情况下,CASCADE 将执行删除列所需的最低水平的重组。
使用 CASCADE 删除具有以下依赖项的列:
您可能要删除被另一列作为其默认值引用的表列。例如,下表定义为具有两个列 a
和 b
,其中 b
将从列 a
获取其默认值。
=> CREATE TABLE x (a int) UNSEGMENTED ALL NODES;
CREATE TABLE
=> ALTER TABLE x ADD COLUMN b int DEFAULT a;
ALTER TABLE
这种情况下,删除列 a
需要执行以下过程:
通过 ALTER COLUMN..DROP DEFAULT 移除默认依赖项:
=> ALTER TABLE x ALTER COLUMN b DROP DEFAULT;
如果以下条件中的一条或两条为 true,则为目标表创建替换超投影:
目标列为表的第一个排序顺序列。如果表没有显式排序顺序,则默认表排序顺序将第一个表列指定为第一个排序顺序列。在这种情况下,新的超投影必须指定排除目标列的排序顺序。
如果将表分段,则在分段表达式中指定目标列。在这种情况下,新的超投影必须指定排除目标列的分段表达式。
假定上一个示例中表 x
具有默认排序顺序 (a,b)。由于列 a
为该表的第一个排序顺序列,因此必须创建对列 b
进行排序的替换超投影:
=> CREATE PROJECTION x_p1 as select * FROM x ORDER BY b UNSEGMENTED ALL NODES;
运行
START_REFRESH
:
=> SELECT START_REFRESH();
START_REFRESH
----------------------------------------
Starting refresh background process.
(1 row)
运行 MAKE_AHM_NOW:
=> SELECT MAKE_AHM_NOW();
MAKE_AHM_NOW
-------------------------------
AHM set (New AHM Epoch: 1231)
(1 row)
删除列:
=> ALTER TABLE x DROP COLUMN a CASCADE;
Vertica 将实施 CASCADE 指令,如下所示:
删除表 x
的原始超投影 (x_super
)。
通过删除列 a
更新替换超投影 x_p1
。
下面的一系列命令成功删除了 BYTEA 数据类型列:
=> CREATE TABLE t (x BYTEA(65000), y BYTEA, z BYTEA(1));
CREATE TABLE
=> ALTER TABLE t DROP COLUMN y;
ALTER TABLE
=> SELECT y FROM t;
ERROR 2624: Column "y" does not exist
=> ALTER TABLE t DROP COLUMN x RESTRICT;
ALTER TABLE
=> SELECT x FROM t;
ERROR 2624: Column "x" does not exist
=> SELECT * FROM t;
z
---
(0 rows)
=> DROP TABLE t CASCADE;
DROP TABLE
下面的一系列命令尝试删除 FLOAT(8) 列,但失败,因为没有足够的投影来维持 K-safety。
=> CREATE TABLE t (x FLOAT(8),y FLOAT(08));
CREATE TABLE
=> ALTER TABLE t DROP COLUMN y RESTRICT;
ALTER TABLE
=> SELECT y FROM t;
ERROR 2624: Column "y" does not exist
=> ALTER TABLE t DROP x CASCADE;
ROLLBACK 2409: Cannot drop any more columns in t
=> DROP TABLE t CASCADE;
ALTER TABLE...ALTER CONSTRAINT
可以启用或禁用主键、唯一和检查约束的强制实施。必须使用关键字 ENABLED
或 DISABLED
来限定此子句:
ENABLED
强制实施指定的约束。
DISABLED
禁用指定约束的强制实施。
例如:
ALTER TABLE public.new_sales ALTER CONSTRAINT C_PRIMARY ENABLED;
有关详细信息,请参阅约束强制执行。
ALTER TABLE...RENAME TO
重命名一个或多个表。重命名的表保留其原始 OID。
可以通过提供两个逗号分隔的列表来重命名多个表。Vertica 根据两个列表中的顺序映射这些名称。只有第一个列表可以使用架构限定表名。例如:
=> ALTER TABLE S1.T1, S1.T2 RENAME TO U1, U2;
RENAME TO
参数将以原子方式应用:重命名所有表,或不重命名任何表。例如,如果要重命名的表数量与新名称数量不匹配,则所有表都不会被重命名。
可以使用 ALTER TABLE...RENAME TO
在同一架构内交换表,而无需实际移动数据。不能跨架构交换表。
以下示例通过中间表 temp
交换表 T1
和 T2
中的数据:
t1
到 temp
t2
到 t1
temp
到 t2
=> DROP TABLE IF EXISTS temp, t1, t2;
DROP TABLE
=> CREATE TABLE t1 (original_name varchar(24));
CREATE TABLE
=> CREATE TABLE t2 (original_name varchar(24));
CREATE TABLE
=> INSERT INTO t1 VALUES ('original name t1');
OUTPUT
--------
1
(1 row)
=> INSERT INTO t2 VALUES ('original name t2');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> ALTER TABLE t1, t2, temp RENAME TO temp, t1, t2;
ALTER TABLE
=> SELECT * FROM t1, t2;
original_name | original_name
------------------+------------------
original name t2 | original name t1
(1 row)
ALTER TABLE...SET SCHEMA
可以将表从一个架构移动到另一个架构。Vertica 会自动将锚定到源表的所有投影移动到目标架构。它还会将所有 IDENTITY
和 AUTO_INCREMENT
列移动到目标架构。
在架构之间移动表需要对当前架构具有 USAGE
权限,并对目标架构具有 CREATE
权限。一次只能在架构之间移动一个表。不能在架构之间移动临时表。
如果新架构中已经存在要移动的同名表或任何投影,该语句将回退并且不会移动该表或任何投影。要解决名称冲突:
重命名要移动的任何冲突表或投影。
再次运行
ALTER TABLE...SET SCHEMA
。
以下示例将表 T1
从架构 S1
移至架构 S2
。锚定到表 T1
的所有投影都会自动移至架构 S2
:
=> ALTER TABLE S1.T1 SET SCHEMA S2;
作为超级用户或表所有者,可以使用
ALTER TABLE...OWNER TO
重新分配表所有权,如下所示:
ALTER TABLE [schema.]table-name OWNER TO owner-name
将表从一个架构移动到另一个架构时,更改表所有权很有用。当表所有者离职或更换工作职责时,所有权重新分配也很有用。由于可以更改表所有者,因此表无需完全重写,进而可以避免生产率下降。
更改表所有权会自动导致以下更改:
由原始所有者对表进行的授权将被删除,对该表的所有现有权限都将从前一个所有者那里撤销。表所有权的更改对架构权限没有影响。
从属 IDENTITY/AUTO-INCREMENT
序列的所有权随表一起转移。但是,对于使用
CREATE SEQUENCE
创建的指定序列,所有权不会更改。要转移这些序列的所有权,请使用
ALTER SEQUENCE
。
新的表所有权会传播到其投影。
在以下示例中,用户 Bob 连接到数据库,查找表,然后将表 t33
的所有权从自己转移到用户 Alice。
=> \c - Bob
You are now connected as user "Bob".
=> \d
Schema | Name | Kind | Owner | Comment
--------+--------+-------+---------+---------
public | applog | table | dbadmin |
public | t33 | table | Bob |
(2 rows)
=> ALTER TABLE t33 OWNER TO Alice;
ALTER TABLE
当 Bob 再次查找数据库表时,他不再看到表 t33
:
=> \d List of tables
List of tables
Schema | Name | Kind | Owner | Comment
--------+--------+-------+---------+---------
public | applog | table | dbadmin |
(1 row)
当用户 Alice 连接到数据库并查找表时,她将看到她是表 t33
的所有者。
=> \c - Alice
You are now connected as user "Alice".
=> \d
List of tables
Schema | Name | Kind | Owner | Comment
--------+------+-------+-------+---------
public | t33 | table | Alice |
(2 rows)
Alice 或超级用户可以将表所有权转移回 Bob。在以下情况下,超级用户执行转移操作。
=> \c - dbadmin
You are now connected as user "dbadmin".
=> ALTER TABLE t33 OWNER TO Bob;
ALTER TABLE
=> \d
List of tables
Schema | Name | Kind | Owner | Comment
--------+----------+-------+---------+---------
public | applog | table | dbadmin |
public | comments | table | dbadmin |
public | t33 | table | Bob |
s1 | t1 | table | User1 |
(4 rows)
也可以查询系统表
V_CATALOG.TABLES
以查看表和所有者信息。注意,所有权更改不会更改表 ID。
在下面的一系列命令中,超级用户将表所有权更改回 Alice,并查询系统表 TABLES
。
=> ALTER TABLE t33 OWNER TO Alice;
ALTER TABLE
=> SELECT table_schema_id, table_schema, table_id, table_name, owner_id, owner_name FROM tables;
table_schema_id | table_schema | table_id | table_name | owner_id | owner_name
-------------------+--------------+-------------------+------------+-------------------+------------
45035996273704968 | public | 45035996273713634 | applog | 45035996273704962 | dbadmin
45035996273704968 | public | 45035996273724496 | comments | 45035996273704962 | dbadmin
45035996273730528 | s1 | 45035996273730548 | t1 | 45035996273730516 | User1
45035996273704968 | public | 45035996273795846 | t33 | 45035996273724576 | Alice
(5 rows)
现在,超级用户将表所有权更改回 Bob,并再次查询 TABLES
表。除了 owner_name
行从 Alice 更改为 Bob,没有其他改变。
=> ALTER TABLE t33 OWNER TO Bob;
ALTER TABLE
=> SELECT table_schema_id, table_schema, table_id, table_name, owner_id, owner_name FROM tables;
table_schema_id | table_schema | table_id | table_name | owner_id | owner_name
-------------------+--------------+-------------------+------------+-------------------+------------
45035996273704968 | public | 45035996273713634 | applog | 45035996273704962 | dbadmin
45035996273704968 | public | 45035996273724496 | comments | 45035996273704962 | dbadmin
45035996273730528 | s1 | 45035996273730548 | t1 | 45035996273730516 | User1
45035996273704968 | public | 45035996273793876 | foo | 45035996273724576 | Alice
45035996273704968 | public | 45035996273795846 | t33 | 45035996273714428 | Bob
(5 rows)
序列可用于将列的默认值设置为连续整数值。序列保证了唯一性,并避免了约束强制执行问题和开销。序列对于主键列特别有用。
虽然序列对象值保证唯一,但不能保证它们连续,因此您可能会将返回的值解释为缺失。例如,两个节点可以用不同的速率递增序列。具有较大处理负载的节点将递增序列,但在具有较小处理负载的节点上递增的值不连续。
Vertica 支持以下序列类型:
命名序列是以升序或降序顺序生成唯一编号的数据库对象。命名序列是通过
CREATE SEQUENCE
语句独立定义的,并且独立于引用它们的表进行管理。一个表可以将一个或多个列的默认值设置为命名序列。
AUTO_INCREMENT/IDENTITY 列序列:列约束 AUTO_INCREMENT
和 IDENTITY
是同义词,用于指定在添加新行时递增或递减列的值。此序列类型与表相关,不会独立保留。一个表只能包含一个 AUTO_INCREMENT
或 IDENTITY
列。
下表列出了两种序列类型之间的差异:
命名序列是由
CREATE SEQUENCE
定义的序列。虽然可以将表列的值设置为命名序列,但与 AUTO_INCREMENT 和 IDENTITY 序列不同,命名序列独立于表而存在。
当应用程序要求表或表达式中使用唯一标识符时,最常使用命名序列。在命名序列返回一个值后,它永远不会在同一会话中再次返回相同的值。
使用
CREATE SEQUENCE
创建命名序列。该语句只需提供序列名称即可;所有其他参数均为可选参数。要创建序列,用户必须对包含该序列的架构具有 CREATE 权限。
以下示例将创建一个起始值为 100 的升序命名序列 my_seq
:
=> CREATE SEQUENCE my_seq START 100;
CREATE SEQUENCE
在创建命名序列对象时,还可以通过设置其 INCREMENT
参数指定其递增量或递减量值。如果省略此参数(如上一示例中所示),则默认值设置为 1。
可以通过对序列调用函数
NEXTVAL
来递增或递减序列(直接在序列本身上递增或递减,或通过向引用该序列的表中添加新行来间接地递增或递减)。对新序列第一次调用时,NEXTVAL
将该序列初始化为其起始值。Vertica 还会为序列创建缓存。随后对序列调用 NEXTVAL
会递增其值。
下面对 NEXTVAL
的调用会将新 my_seq
序列初始化为 100:
=> SELECT NEXTVAL('my_seq');
nextval
---------
100
(1 row)
可以通过对序列调用
CURRVAL
来获取该序列的当前值。例如:
=> SELECT CURRVAL('my_seq');
CURRVAL
---------
100
(1 row)
CURRVAL
在以下情况下将返回错误:如果对尚未由 NEXTVAL
初始化的新序列或对尚未在新会话中访问的现有序列调用它。例如:
=> CREATE SEQUENCE seq2;
CREATE SEQUENCE
=> SELECT currval('seq2');
ERROR 4700: Sequence seq2 has not been accessed in the session
表可以将任何列的默认值设置为命名序列。表创建者必须具有以下权限:SELECT(对于序列)、USAGE(对于其架构)。
在以下示例中,列 id
从命名序列 my_seq
获取其默认值:
=> CREATE TABLE customer(id INTEGER DEFAULT my_seq.NEXTVAL,
lname VARCHAR(25),
fname VARCHAR(25),
membership_card INTEGER
);
对于插入到表 customer
中的每一行,该序列调用 NEXTVAL
函数来设置 id
列的值。例如:
=> INSERT INTO customer VALUES (default, 'Carr', 'Mary', 87432);
=> INSERT INTO customer VALUES (default, 'Diem', 'Nga', 87433);
=> COMMIT;
对于每一行,插入操作都会对序列 my_seq
调用 NEXTVAL
,这会将该序列递增至 101 和 102,并将 id
列设置为这些值:
=> SELECT * FROM customer;
id | lname | fname | membership_card
-----+-------+-------+-----------------
101 | Carr | Mary | 87432
102 | Diem | Nga | 87433
(1 row)
当创建命名序列时,其 CACHE
参数决定了每个节点在会话期间所保留的序列值的数量。默认缓存值为 250K,因此每个节点在每个会话期间为每个序列保留 250,000 个值。此默认缓存大小为大规模插入或复制操作提供了一种有效的方式。
如果将序列缓存设置为较低的数字,则节点可能会更频繁地请求一组新的缓存值。提供新缓存时,Vertica 必须锁定编录。在 Vertica 释放锁之前,其他数据库活动(例如表插入)会被阻止,这将对整体性能产生不利影响。
当一个新会话启动时,节点缓存最初为空。默认情况下,启动程序节点会为群集中的所有节点请求和保留缓存。可以通过将配置参数 ClusterSequenceCacheMode
设置为 0 来更改此默认值,以便每个节点都请求其自己的缓存。
有关 Vertica 如何请求缓存并在群集中的所有节点之间分发缓存的信息,请参阅序列缓存。
Vertica 在所有节点之间分发会话。群集节点第一次对序列调用 NEXTVAL 函数以递增(或递减)其值时,该节点会请求其自己的序列值缓存。然后,该节点会为当前会话维护该缓存。当其他节点调用 NEXTVAL 时,它们也会创建和维护其自己的序列值缓存。
在会话期间,节点会以不同的频率独立调用 NEXTVAL。每个节点都使用其自己的缓存来填充序列。所有序列值都保证唯一,但是可能与另一个节点上执行的 NEXTVAL 语句出现乱序。因此,序列值通常不连续。
在所有情况下,每行仅递增序列一次。因此,如果多个列引用了同一序列,则 NEXTVAL 会将该行中的所有列设置为相同的值。这适用于联接表的行。
Vertica 按如下方式计算序列的当前值:
在每个语句结束时,会话中使用的所有序列的状态都会返回到启动程序节点。
启动程序节点计算每个序列在所有节点上的所有状态下的最大
CURRVAL
。
此最大值将用作后续语句中的 CURRVAL
,直到调用另一个 NEXTVAL。
在下列情况下,缓存中的序列值可能会丢失:
如果在调用 NEXTVAL 后某条语句失败了(因此耗用了缓存中的一个序列值),则值就会丢失。
如果连接断开了(如删除了会话),则缓存中尚未通过 NEXTVAL 返回的所有剩余值都会丢失。
一个或多个节点尚未用完其当前缓存分配时,启动程序节点便为每个节点分发新的缓存块。有关此场景的信息,请参阅序列缓存。
可以使用 ALTER SEQUENCE...RESTART 恢复丢失的序列值,这会在下一个会话中将序列重置为指定的值。
ALTER SEQUENCE
可以通过两种方式更改命名序列:
重置控制序列行为的参数,例如其起始值或最小值和最大值的范围。这些更改仅在您启动新数据库会话时生效。
重置序列名称、架构或所有权。这些更改会立即生效。
ALTER SEQUENCE
语句不能同时进行这两种类型的更改。
ALTER SEQUENCE
可以通过以下参数更改一个或多个序列属性:
这些更改仅在您启动新数据库会话时生效。例如,如果创建一个从 10 开始并按 1(默认值)递增的命名序列 my_sequence
,则序列每次调用 NEXTVAL
都会按 1 递增其值:
=> CREATE SEQUENCE my_sequence START 10;
=> SELECT NEXTVAL('my_sequence');
nextval
---------
10
(1 row)
=> SELECT NEXTVAL('my_sequence');
nextval
---------
11
(1 row)
以下 ALTER SEQUENCE
语句指定重新启动序列时从 50 开始:
=>ALTER SEQUENCE my_sequence RESTART WITH 50;
但是,此更改在当前会话中不起作用。对 NEXTVAL
的下一次调用会将序列递增至 12:
=> SELECT NEXTVAL('my_sequence');
NEXTVAL
---------
12
(1 row)
只有在启动新的数据库会话后,该序列才会在 50 处重新启动:
=> \q
$ vsql
Welcome to vsql, the Vertica Analytic Database interactive terminal.
=> SELECT NEXTVAL('my_sequence');
NEXTVAL
---------
50
(1 row)
可以使用 ALTER SEQUENCE
对命名序列进行以下更改:
重命名它。
将其移动到另一个架构。
重新分配所有权。
这些更改中的每个更改都需要使用单独的 ALTER SEQUENCE
语句。这些更改会立即生效。
例如,以下语句将序列从 my_seq
重命名为 serial
:
=> ALTER SEQUENCE s1.my_seq RENAME TO s1.serial;
以下语句将序列 s1.serial
移动到架构 s2
中:
=> ALTER SEQUENCE s1.my_seq SET SCHEMA TO s2;
以下语句将 s2.serial
的所有权重新分配给另一个用户:
=> ALTER SEQUENCE s2.serial OWNER TO bertie;
使用
DROP SEQUENCE
移除命名序列。例如:
=> DROP SEQUENCE my_sequence;
如果满足以下条件之一,则不能删除序列:
其他对象依赖该序列。 DROP SEQUENCE
不支持级联操作。
列的 DEFAULT
表达式引用该序列。在删除该序列之前,必须移除对它的所有列引用。
AUTO_INCREMENT 和 IDENTITY 约束是将列与序列相关联的同义词。当添加新行时,此序列会自动递增列值。
在表中定义 AUTO_INCREMENT 或 IDENTITY 列,如下所示:
CREATE TABLE table-name...
(column-name
{AUTO_INCREMENT | IDENTITY}
( [ cache-size | start, increment [, cache-size ] ] )
AUTO_INCREMENT/IDENTITY 序列由定义它们的表拥有,并且不存在于该表之外。与命名序列不同,您不能使用 ALTER SEQUENCE 管理 AUTO_INCREMENT/IDENTITY 序列。例如,您不能独立于其表更改 AUTO_INCREMENT/IDENTITY 序列的架构。如果将表移动到另一个架构,序列会自动随之移动。
可以通过调用 LAST_INSERT_ID 函数获取为 AUTO_INCREMENT/IDENTITY 序列生成的最后一个值。
以下限制适用于 AUTO_INCREMENT/IDENTITY 列:
一个表只能包含一个 AUTO_INCREMENT/IDENTITY 列。
即使未提交尝试向表中插入值的事务,AUTO_INCREMENT/IDENTITY 值也绝对不会回退。
不能更改 AUTO_INCREMENT/IDENTITY 列的值。
具有 AUTO_INCREMENT/IDENTITY 列的表还可以包含一个或多个设置为命名序列的列。
以下示例显示了如何使用 IDENTITY 列约束创建一个包含 ID 列的表。ID 列的初始值为 1。每次插入一行时,该列都将以 1 为增量递增。
创建名为 Premium_Customer
的表:
=> CREATE TABLE Premium_Customer(
ID IDENTITY(1,1),
lname VARCHAR(25),
fname VARCHAR(25),
store_membership_card INTEGER
);
=> INSERT INTO Premium_Customer (lname, fname, store_membership_card )
VALUES ('Gupta', 'Saleem', 475987);
IDENTITY 列的种子值为 1,它指定加载到表中的第一行的值,增量为 1,它指定添加到上一行的 IDENTITY 值的值。
确认添加的行并查看 ID 值:
=> SELECT * FROM Premium_Customer;
ID | lname | fname | store_membership_card
----+-------+--------+-----------------------
1 | Gupta | Saleem | 475987
(1 row)
增加一个行:
=> INSERT INTO Premium_Customer (lname, fname, store_membership_card)
VALUES ('Lee', 'Chen', 598742);
调用 Vertica 函数 LAST_INSERT_ID。该函数将返回值 2,因为您之前插入了一个新客户 (Chen Lee),并且每次插入一行时,此值都会递增:
=> SELECT LAST_INSERT_ID();
last_insert_id
----------------
2
(1 row)
查看 Premium_Customer
表中的所有 ID 值:
=> SELECT * FROM Premium_Customer;
ID | lname | fname | store_membership_card
----+-------+--------+-----------------------
1 | Gupta | Saleem | 475987
2 | Lee | Chen | 598742
(2 rows)
接下来的三个示例说明使用 IDENTITY 实参的三种有效方法。这些示例对 AUTO_INCREMENT 实参也有效。
第一个示例使用缓存 100,以及起始值 (1) 和增量值 (1) 的默认值:
=> CREATE TABLE t1(x IDENTITY(100), y INT);
第二个示例将起始值和增量值指定为 1,并将默认缓存值指定为 250,000:
=> CREATE TABLE t2(y IDENTITY(1,1), x INT);
第三个示例将起始值和增量值指定为 1,并将缓存值指定为 100:
=> CREATE TABLE t3(z IDENTITY(1,1,100), zx INT);
缓存对于所有序列类型都是类似的:命名序列、标识序列和自动递增序列。为了在群集中的节点之间为给定序列分配缓存,Vertica 使用以下过程。
默认情况下,当会话开始时,群集启动程序节点会为其自身和群集中的其他节点请求缓存。
启动程序节点会在分发执行计划时将缓存一并分发给其他节点。
由于启动程序节点会为所有节点请求缓存,因此只有启动程序会在请求缓存时锁定全局编录。
这种方法最适合处理大型 INSERT-SELECT 和 COPY 操作。下图显示了启动程序如何在三节点群集中为命名序列请求和分发缓存,其中将该序列的缓存设置为 250 K:
各节点在不同的时间用尽缓存。当执行相同的查询时,节点会根据需要单独请求其他缓存。
对于同一个会话中的新查询,如果启动程序使用其所有缓存执行上一个查询执行,则该启动程序可能具有空缓存。在这种情况下,启动程序会为所有节点请求缓存。
可以通过将配置参数 ClusterSequenceCacheMode
设置为 0(禁用)来更改节点获得序列缓存的方式。将此参数设置为 0 时,群集中的所有节点都会请求各自的缓存和编录锁。但是,一开始执行大型 INSERT-SELECT 和 COPY 操作时,如果所有节点的缓存为空,每个节点会同时请求缓存。这些多个请求会导致全局编录上同时出现多个锁定,从而对性能产生不利影响。因此,ClusterSequenceCacheMode
应保持设置为其默认值 1(启用)。
以下示例比较了 ClusterSequenceCacheMode
的不同设置如何影响 Vertica 管理序列缓存的方式。该示例假定一个三节点群集,每个节点 250 K 缓存(默认值),序列 ID 值递增量为 1。
MERGE
语句可以根据与源数据集联接的结果对目标表执行更新和插入操作。联接只能将源行与一个目标行相匹配;否则,Vertica 会返回错误。
MERGE
采用以下语法:
MERGE INTO target‑table USING source‑dataset ON join-condition
matching‑clause[ matching‑clause ]
合并操作至少包含三个组成部分:
要对其执行更新和插入操作的目标表。 MERGE
对目标表采用 X(互斥)锁,直到合并操作完成。
联接到另一个数据集,即以下数据集之一:表、视图或子查询结果集。
一个或两个匹配子句:
WHEN MATCHED THEN UPDATE SET
和
WHEN NOT MATCHED THEN INSERT
。
在此示例中,合并操作涉及两个表:
visits_daily
记录每日餐厅流量,并随着每次顾客到访而更新。此表中的数据每 24 小时刷新一次。
visits_history
存储顾客到访各个餐厅的历史记录(无限期累计)。
每天晚上,您都会将 visits_daily
的每日到访计数合并到 visits_history
。合并操作通过两种方式修改目标表:
更新现有顾客数据。
为首次到访的顾客插入新数据行。
一个 MERGE
语句将这两项操作作为单个 (upsert) 事务来执行。
源表和目标表 visits_daily
和 visits_history
定义如下:
CREATE TABLE public.visits_daily
(
customer_id int,
location_name varchar(20),
visit_time time(0) DEFAULT (now())::timetz(6)
);
CREATE TABLE public.visits_history
(
customer_id int,
location_name varchar(20),
visit_count int
);
表 visits_history
包含三个顾客行,他们分别到访了 Etoile 和 LaRosa 两家餐厅:
=> SELECT * FROM visits_history ORDER BY customer_id, location_name;
customer_id | location_name | visit_count
-------------+---------------+-------------
1001 | Etoile | 2
1002 | La Rosa | 4
1004 | Etoile | 1
(3 rows)
到营业结束时,表 visits_daily
包含三行餐厅到访数据:
=> SELECT * FROM visits_daily ORDER BY customer_id, location_name;
customer_id | location_name | visit_time
-------------+---------------+------------
1001 | Etoile | 18:19:29
1003 | Lux Cafe | 08:07:00
1004 | La Rosa | 11:49:20
(3 rows)
以下 MERGE
语句将 visits_daily
数据合并到 visits_history
中:
对于匹配的顾客,MERGE
会更新出现计数。
对于不匹配的顾客,MERGE
会插入新行。
=> MERGE INTO visits_history h USING visits_daily d
ON (h.customer_id=d.customer_id AND h.location_name=d.location_name)
WHEN MATCHED THEN UPDATE SET visit_count = h.visit_count + 1
WHEN NOT MATCHED THEN INSERT (customer_id, location_name, visit_count)
VALUES (d.customer_id, d.location_name, 1);
OUTPUT
--------
3
(1 row)
MERGE
返回已更新和插入的行数。在本例中,返回值指定三个更新和插入项:
顾客 1001
第三次到访 Etoile
新顾客 1003
首次到访新餐厅 Lux Cafe
客户 1004
首次到访 La Rosa
如果现在查询表 visits_history
,结果集会显示合并(更新和插入)的数据。更新行和新行高亮显示:
MERGE
操作将目标表联接到以下数据源之一:
另一个表
视图
子查询结果集
可以将一个表中的数据合并到另一个表中,如下所示:
MERGE INTO target‑table USING { source‑table | source‑view } join-condition
matching‑clause[ matching‑clause ]
如果指定视图,则 Vertica 会将视图名称扩展到其封装的查询,并将结果集用作合并源数据。
例如,VMart 表 public.product_dimension
包含当前和停产的产品。可以将所有停产的产品移动到单独的表 public.product_dimension_discontinued
中,如下所示:
=> CREATE TABLE public.product_dimension_discontinued (
product_key int,
product_version int,
sku_number char(32),
category_description char(32),
product_description varchar(128));
=> MERGE INTO product_dimension_discontinued tgt
USING product_dimension src ON tgt.product_key = src.product_key
AND tgt.product_version = src.product_version
WHEN NOT MATCHED AND src.discontinued_flag='1' THEN INSERT VALUES
(src.product_key,
src.product_version,
src.sku_number,
src.category_description,
src.product_description);
OUTPUT
--------
1186
(1 row)
源表 product_dimension
使用 product_key
和 product_version
两列来标识唯一产品。MERGE
语句在这些列上联接源表和目标表,以便返回不匹配行的单个实例。WHEN NOT MATCHED
子句包含一个筛选器 (src.discontinued_flag='1'
),它将结果集缩减为仅包含停产的产品。剩余的行将插入到目标表 product_dimension_discontinued
。
可以将子查询返回的结果集合并到表中,如下所示:
MERGE INTO target‑table USING (subquery) sq-alias join-condition
matching‑clause[ matching‑clause ]
例如,VMart 表 public.product_dimension
定义如下(DDL 截断):
CREATE TABLE public.product_dimension
(
product_key int NOT NULL,
product_version int NOT NULL,
product_description varchar(128),
sku_number char(32),
...
)
ALTER TABLE public.product_dimension
ADD CONSTRAINT C_PRIMARY PRIMARY KEY (product_key, product_version) DISABLED;
product_key
和 product_version
列构成表的主键。可以修改此表,使其包含连接这两列的值的单个列。此列可用于唯一标识每个产品,同时还保留 product_key
和 product_version
的原始值。
可以使用查询另外两列的 MERGE
语句填充新列:
=> ALTER TABLE public.product_dimension ADD COLUMN product_ID numeric(8,2);
ALTER TABLE
=> MERGE INTO product_dimension tgt
USING (SELECT (product_key||'.0'||product_version)::numeric(8,2) AS pid, sku_number
FROM product_dimension) src
ON tgt.product_key||'.0'||product_version::numeric=src.pid
WHEN MATCHED THEN UPDATE SET product_ID = src.pid;
OUTPUT
--------
60000
(1 row)
以下查询验证新列值是否对应于 product_key
和 product_version
中的值:
=> SELECT product_ID, product_key, product_version, product_description
FROM product_dimension
WHERE category_description = 'Medical'
AND product_description ILIKE '%diabetes%'
AND discontinued_flag = 1 ORDER BY product_ID;
product_ID | product_key | product_version | product_description
------------+-------------+-----------------+-----------------------------------------
5836.02 | 5836 | 2 | Brand #17487 diabetes blood testing kit
14320.02 | 14320 | 2 | Brand #43046 diabetes blood testing kit
18881.01 | 18881 | 1 | Brand #56743 diabetes blood testing kit
(3 rows)
MERGE
支持以下匹配子句的一个实例:
[WHEN MATCHED THEN UPDATE SET](#WHEN_MATCHED)
[WHEN NOT MATCHED THEN INSERT](#WHEN_NOT_MATCHED)
每个匹配子句都可以指定一个附加筛选器,如更新和插入筛选器中所述。
WHEN MATCHED THEN UPDATE SET
通常使用源表中的数据更新联接到源表的所有目标表行:
WHEN MATCHED [ AND update-filter ] THEN UPDATE
SET { target‑column = expression }[,...]
Vertica 只能对源表的联接列中的唯一值执行联接。如果源表的联接列包含多个匹配值,MERGE
语句将返回运行时错误。
WHEN NOT MATCHED THEN INSERT
WHEN NOT MATCHED THEN INSERT
会为从联接中排除的每个源表行向目标表中插入一个新行:
WHEN NOT MATCHED [ AND insert-filter ] THEN INSERT
[ ( column‑list ) ] VALUES ( values‑list )
column‑list 是目标表中一个或多个目标列的逗号分隔列表,按任意顺序列出。 MERGE
按相同的顺序将 column‑list 列映射到 values‑list 值,并且每个列-值对都必须兼容。如果省略 column‑list,Vertica 会根据表定义中的列顺序将 values‑list 值映射到列。
例如,给定以下源表和目标表定义:
CREATE TABLE t1 (a int, b int, c int);
CREATE TABLE t2 (x int, y int, z int);
以下 WHEN NOT MATCHED
子句在新插入的行中隐式设置目标表列 a
、b
和 c
的值:
MERGE INTO t1 USING t2 ON t1.a=t2.x
WHEN NOT MATCHED THEN INSERT VALUES (t2.x, t2.y, t2.z);
相反,以下 WHEN NOT MATCHED
子句从合并操作中排除列 t1.b
和 t2.y
。WHEN NOT MATCHED
子句显式将目标表和源表中的两组列进行配对: t1.a
对 t2.x
以及 t1.c
对 t2.z
。Vertica 将排除的列 t1.b
设置为 Null:
MERGE INTO t1 USING t2 ON t1.a=t2.x
WHEN NOT MATCHED THEN INSERT (a, c) VALUES (t2.x, t2.z);
MERGE
语句中的每个 WHEN MATCHED
和 WHEN NOT MATCHED
子句都可以选择分别指定更新筛选器和插入筛选器:
WHEN MATCHED AND update-filter THEN UPDATE ...
WHEN NOT MATCHED AND insert-filter THEN INSERT ...
Vertica 还支持用于指定更新和插入筛选器的 Oracle 语法:
WHEN MATCHED THEN UPDATE SET column-updates WHERE update-filter
WHEN NOT MATCHED THEN INSERT column-values WHERE insert-filter
每个筛选器都可以指定多个条件。Vertica 按如下方式处理筛选器:
更新筛选器应用于目标表中由 MERGE
联接返回的匹配行集。对于更新筛选器求值结果为 true 的每一行,Vertica 都会更新指定的列。
插入筛选器应用于从 MERGE
联接中排除的源表行集。对于插入筛选器求值结果为 true 的每一行,Vertica 都会向目标表中添加一个具有指定值的新行。
例如,给定表 t11
和 t22
中的以下数据:
=> SELECT * from t11 ORDER BY pk;
pk | col1 | col2 | SKIP_ME_FLAG
----+------+------+--------------
1 | 2 | 3 | t
2 | 3 | 4 | t
3 | 4 | 5 | f
4 | | 6 | f
5 | 6 | 7 | t
6 | | 8 | f
7 | 8 | | t
(7 rows)
=> SELECT * FROM t22 ORDER BY pk;
pk | col1 | col2
----+------+------
1 | 2 | 4
2 | 4 | 8
3 | 6 |
4 | 8 | 16
(4 rows)
可以使用以下 MERGE
语句将表 t11
中的数据合并到表 t22
中,其中包括更新和插入筛选器:
=> MERGE INTO t22 USING t11 ON ( t11.pk=t22.pk )
WHEN MATCHED
AND t11.SKIP_ME_FLAG=FALSE AND (
COALESCE (t22.col1<>t11.col1, (t22.col1 is null)<>(t11.col1 is null))
)
THEN UPDATE SET col1=t11.col1, col2=t11.col2
WHEN NOT MATCHED
AND t11.SKIP_ME_FLAG=FALSE
THEN INSERT (pk, col1, col2) VALUES (t11.pk, t11.col1, t11.col2);
OUTPUT
--------
3
(1 row)
=> SELECT * FROM t22 ORDER BY pk;
pk | col1 | col2
----+------+------
1 | 2 | 4
2 | 4 | 8
3 | 4 | 5
4 | | 6
6 | | 8
(5 rows)
Vertica 按如下方式使用更新和插入筛选器:
根据更新筛选器条件对所有匹配的行进行求值。Vertica 会更新以下两个条件的求值结果均为 true 的每一行:
源列 t11.SKIP_ME_FLAG
设置为 false。
COALESCE
函数的求值结果为 true。
根据插入筛选器,对源表中所有不匹配的行进行求值。对于列 t11.SKIP_ME_FLAG
设置为 false 的每一行,Vertica 都会在目标表中插入一个新行。
可以通过以下几种方式提高 MERGE
性能:
使用小于目标表的源表。
Vertica 查询优化器会自动选择最佳投影来实施合并操作。良好投影设计策略提供的投影可帮助查询优化器避免额外的排序和数据传输操作,并提高 MERGE
性能。
例如,以下 MERGE
语句片段分别在 tgt.a
和 src.b
列上联接源表和目标表 tgt
和 src
:
=> MERGE INTO tgt USING src ON tgt.a = src.b ...
如果 tgt
和 src
表的投影采用以下投影设计之一(其中输入通过投影 ORDER BY
子句进行预排序),则 Vertica 可以使用局部合并联接:
如果满足以下条件,Vertica 即准备了一个经过优化的查询计划:
MERGE
语句同时包含两个匹配子句
WHEN MATCHED THEN UPDATE SET
和
WHEN NOT MATCHED THEN INSERT
。如果 MERGE
语句仅包含一个匹配子句,那么它使用的是未经优化的查询计划。
MERGE
语句不包括更新和插入筛选器。
目标表联接列具有唯一键或主键约束。此要求不适用于源表联接列。
两个匹配子句指定目标表中的所有列。
两个匹配子句指定相同的源值。
有关评估
EXPLAIN
生成的查询计划的详细信息,请参阅 MERGE 路径。
后面的示例使用一个简单的架构来说明 Vertica 在哪些条件下会为 MERGE
准备优化查询计划,在哪些条件下不会准备该计划:
CREATE TABLE target(a INT PRIMARY KEY, b INT, c INT) ORDER BY b,a;
CREATE TABLE source(a INT, b INT, c INT) ORDER BY b,a;
INSERT INTO target VALUES(1,2,3);
INSERT INTO target VALUES(2,4,7);
INSERT INTO source VALUES(3,4,5);
INSERT INTO source VALUES(4,6,9);
COMMIT;
经过优化的 MERGE 语句
Vertica 可以为以下 MERGE
语句准备一个经过优化的查询计划,原因是:
目标表的联接列 t.a
具有主键约束。
目标表 (a,b,c)
中的所有列都包含在 UPDATE
和 INSERT
子句中。
UPDATE
和 INSERT
子句指定相同的源值:s.a
、s.b
和 s.c
。
MERGE INTO target t USING source s ON t.a = s.a
WHEN MATCHED THEN UPDATE SET a=s.a, b=s.b, c=s.c
WHEN NOT MATCHED THEN INSERT(a,b,c) VALUES(s.a, s.b, s.c);
OUTPUT
--------
2
(1 row)
输出值 2 表示成功,同时也说明了源中更新/插入到目标的行数。
未优化 MERGE 语句
在下一个示例中,MERGE
语句在未经优化的情况下运行,因为 UPDATE/INSERT
子句中的源值不相同。具体来说,UPDATE
子句包括列 s.a
和 s.c
的常数,而 INSERT
子句不包括:
MERGE INTO target t USING source s ON t.a = s.a
WHEN MATCHED THEN UPDATE SET a=s.a + 1, b=s.b, c=s.c - 1
WHEN NOT MATCHED THEN INSERT(a,b,c) VALUES(s.a, s.b, s.c);
为了使前面的 MERGE
语句符合优化条件,请重写该语句,以使 UPDATE
和 INSERT
子句中的源值相同:
MERGE INTO target t USING source s ON t.a = s.a
WHEN MATCHED THEN UPDATE SET a=s.a + 1, b=s.b, c=s.c -1
WHEN NOT MATCHED THEN INSERT(a,b,c) VALUES(s.a + 1, s.b, s.c - 1);
以下限制适用于使用
MERGE
更新和插入表数据。
如果在目标表中启用了主键、唯一键或检查约束以自动强制实施,Vertica 会在您加载新数据时强制实施这些约束。如果发生违规,Vertica 会回滚操作并返回错误。
合并操作中不能指定以下列,否则将返回错误:
Identity/auto-increment 列,或默认值设置为命名序列的列。
Flex 表中的 Vmap 列,例如 __raw__
。
复杂类型的列(ARRAY、SET、ROW)。
Vertica 提供了从表中移除数据的多种方法:
下表汇总了各种数据移除操作之间的差异。
下表可以帮助您确定最适合移除表数据的操作:
Vertica 已针对查询密集型工作负载进行了优化,因此,DELETE 和 UPDATE 查询可能无法达到与其他查询相同的性能水平。DELETE 和 UPDATE 操作必须更新所有投影,因此这些操作只能与最慢的投影一样快。
要提高 DELETE 和 UPDATE 查询的性能,请考虑以下问题:
如果投影包含查询谓词所需的所有列,则投影已针对 DELETE 和 UPDATE 操作进行了优化。通常,对经过优化的投影执行 DML 操作时,速度明显快于未经优化的投影。
例如,假设有以下表和投影:
=> CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER);
=> CREATE PROJECTION p1 (a, b, c) AS SELECT * FROM t ORDER BY a;
=> CREATE PROJECTION p2 (a, c) AS SELECT a, c FROM t ORDER BY c, a;
在以下查询中,p1
和 p2
都符合 DELETE 和 UPDATE 优化条件,因为列 a
可用:
=> DELETE from t WHERE a = 1;
在以下示例中,只有投影 p1
符合 DELETE 和 UPDATE 优化条件,因为 b 列在 p2
中不可用:
=> DELETE from t WHERE b = 1;
为了符合 DELETE 优化条件,在 DELETE 或 UPDATE 语句的 WHERE 子句中引用的所有目标表列都必须位于投影定义中。
例如,以下简单架构包含两个表和三个投影:
=> CREATE TABLE tb1 (a INT, b INT, c INT, d INT);
=> CREATE TABLE tb2 (g INT, h INT, i INT, j INT);
第一个投影引用 tb1
中的所有列并按列 a
进行排序:
=> CREATE PROJECTION tb1_p AS SELECT a, b, c, d FROM tb1 ORDER BY a;
伙伴实例投影引用 tb1
中的列 a
并按此列进行排序:
=> CREATE PROJECTION tb1_p_2 AS SELECT a FROM tb1 ORDER BY a;
以下投影引用 tb2
中的所有列并按列 i
进行排序:
=> CREATE PROJECTION tb2_p AS SELECT g, h, i, j FROM tb2 ORDER BY i;
考虑以下 DML 语句,其 WHERE
子句引用 tb1.a
。由于 tb1
的两个投影都包含列 a
,因此二者都符合经过优化的 DELETE 的条件:
=> DELETE FROM tb1 WHERE tb1.a IN (SELECT tb2.i FROM tb2);
在以下条件下,不支持经过优化的 DELETE 操作:
子查询引用目标表时存在复制的投影。例如不支持以下语法:
=> DELETE FROM tb1 WHERE tb1.a IN (SELECT e FROM tb2, tb2 WHERE tb2.e = tb1.e);
子查询不返回多个行。例如不支持以下语法:
=> DELETE FROM tb1 WHERE tb1.a = (SELECT k from tb2);
设计投影,使得经常使用的 DELETE 或 UPDATE 谓词列按照大型 DELETE 和 UPDATE 操作的所有投影的排序顺序显示。
例如,假设您对投影执行的大多数 DELETE 查询如下所示:
=> DELETE from t where time_key < '1-1-2007'
要优化 DELETE 操作,请使得 time_key
出现在所有投影的 ORDER BY 子句中。此架构设计可以提高 DELETE 操作的性能。
此外,将排序列添加到排序顺序中,以便每个排序键值组合都唯一标识一行或一小组行。有关详细信息,请参阅选择排序顺序:最佳实践。要分析投影是否存在排序顺序问题,请使用 EVALUATE_DELETE_PERFORMANCE 函数。
在 Vertica 中,删除操作不会从物理存储中移除行。 DELETE 会将行标记为已删除,UPDATE 也是如此,后者会合并删除和插入操作。在这两种情况下,Vertica 都会将丢弃的行保留为历史数据,在清除这些历史数据之前,仍然可通过历史查询访问这些数据。
保留历史数据的成本有双部分:
向已删除的行和删除标记分配磁盘空间。
典型(非历史)查询必须读取并跳过已删除的数据,这可能会影响性能。
清除操作将从物理存储中永久移除历史数据,并释放磁盘空间供重复使用。只有 Ancient History Mark (AHM) 之前的历史数据才符合清除条件。
可以通过两种方式清除数据:
在这两种情况下,Vertica 都会清除直到并包括 AHM 时期的所有历史数据,然后重置 AHM。有关 Vertica 如何使用时期的详细信息,请参阅时期。
清除数据的首选方法是建立一个策略来确定哪个已删除的数据符合清除条件。当 Tuple Mover 执行 合并操作时,会自动清除符合条件的数据。
Vertica 提供了两种方法用于确定已删除的数据何时符合清除条件:
指定保存删除数据的时间
指定保存的 时期数
指定保存删除数据的时间是确定可以清除哪些已删除数据的首选方法。默认情况下,仅当节点处于关闭状态时,Vertica 才会保存历史数据。
要更改保存已删除数据的指定时间,请使用 HistoryRetentionTime
配置参数:
=> ALTER DATABASE DEFAULT SET HistoryRetentionTime = {seconds | -1};
在上述语法中:
Seconds 是保存已删除数据的时长(单位为秒)。
-1 表示您不想使用 HistoryRetentionTime
配置参数确定哪些已删除数据符合清除条件。如果您更想使用其他方法 (HistoryRetentionEpochs
) 来确定可以清除哪些已删除数据,可使用此设置。
以下示例将历史时期保留级别设置为 240 秒:
=> ALTER DATABASE DEFAULT SET HistoryRetentionTime = 240;
除非您有理由限制时期数,否则 Vertica 建议您指定保存删除数据的时间。
要通过 HistoryRetentionEpochs
配置参数指定保存的历史时期数:
关闭 HistoryRetentionTime
配置参数:
=> ALTER DATABASE DEFAULT SET HistoryRetentionTime = -1;
通过 HistoryRetentionEpochs
配置参数设置历史时期保留级别:
=> ALTER DATABASE DEFAULT SET HistoryRetentionEpochs = {num_epochs | -1};
num_epochs 是要保存的历史时期数。
-1 表示您不想使用 HistoryRetentionEpochs
配置参数从时期映射中截取历史时期。默认情况下,HistoryRetentionEpochs
设置为 -1。
以下示例将要保存的历史时期数设置为 40:
=> ALTER DATABASE DEFAULT SET HistoryRetentionEpochs = 40;
将立即在数据库群集内的所有节点上实施修改。您无需重新启动数据库。
HistoryRetentionTime
和 HistoryRetentionEpochs
,则优先使用 HistoryRetentionTime
。
有关更多详细信息,请参阅时期管理参数。有关 Vertica 如何使用时期的信息,请参阅时期。
如果要保留所有历史数据,可按以下方式将历史时期保留参数的值设置为 -1:
=> ALTER DABABASE mydb SET HistoryRetentionTime = -1;
=> ALTER DATABASE DEFAULT SET HistoryRetentionEpochs = -1;
可以按如下方式手动清除已删除的数据:
设置清除已删除数据的截止日期。首先,调用以下函数之一以验证当前的 Ancient History Mark ( AHM):
GET_AHM_TIME
返回 AHM 的 TIMESTAMP 值。
GET_AHM_EPOCH
返回 AHM 所在的时期编号。
使用以下函数之一将 AHM 设置为所需的截止日期:
SET_AHM_TIME
将 AHM 设置为包含
启动程序节点上指定的 TIMESTAMP 值的
时期。
SET_AHM_EPOCH
将 AHM 设置为指定的时期。
MAKE_AHM_NOW
将 AHM 设置为最大允许值。这让您可以清除所有已删除的数据。
如果调用 SET_AHM_TIME
,请记住指定的时间戳将映射到默认具有三分钟粒度的时期。因此,如果将 AHM 时间指定为 2008-01-01 00:00:00.00
,则 Vertica 可能会清除 2008 年前三分钟的数据,或保留 2007 年最后三分钟的数据。
使用以下函数之一从所需的投影中清除已删除的数据:
PURGE
清除物理架构中的所有投影。
PURGE_TABLE
清除锚定到指定表的所有投影。
PURGE_PROJECTION
清除指定的投影。
PURGE_PARTITION
清除指定的分区。
Tuple Mover 执行
合并操作以清除数据。Vertica 会定期调用 Tuple Mover 以执行合并操作,如 Tuple Mover 参数所配置的那样。可以通过调用函数
DO_TM_TASK
来手动调用 Tuple Mover。
有关 Vertica 如何使用时期的详细信息,请参阅时期。
TRUNCATE TABLE 移除与目标表及其投影相关联的所有存储。Vertica 会保留表和投影定义。如果截断后的表包含过时投影,则当 TRUNCATE TABLE 返回时,这些投影将被清除并标记为最新。
TRUNCATE TABLE 在语句执行后提交整个事务,即使未能截断表也是如此。不能回退 TRUNCATE TABLE 语句。
使用 TRUNCATE TABLE 进行测试。可以使用它来移除表中的所有数据并向其中加载新数据,而无需重新创建表及其投影。
TRUNCATE TABLE 会对表采用 O(所有者)锁,直到截断过程完成。随后释放保存点。
如果操作无法在目标表上获取 O lock,Vertica 将尝试关闭该表上运行的任何内部 tuple mover 会话。如果成功,则可以继续操作。在用户会话中运行的显式 Tuple Mover 操作不会关闭。如果显式 Tuple Mover 操作在表上运行,则该操作仅在 Tuple Mover 操作完成后继续。
不能截断外部表。
=> INSERT INTO sample_table (a) VALUES (3);
=> SELECT * FROM sample_table;
a
---
3
(1 row)
=> TRUNCATE TABLE sample_table;
TRUNCATE TABLE
=> SELECT * FROM sample_table;
a
---
(0 rows)
可以通过重新构建表来大规模回收磁盘空间,如下所示:
创建一个与要重新构建的表具有相同(或相似)定义的表。
创建新表的投影。
使用
INSERT...SELECT
将目标表中的数据复制到新表中。
删除旧表及其投影。
使用旧表的名称,通过
ALTER TABLE...RENAME
重命名新表。
DROP TABLE
从数据库编录中删除表。如果有任何投影与该表相关联,则 DROP TABLE
会返回一条错误消息,除非它还包含 CASCADE
选项。有一个例外:该表仅有一个自动生成的超投影(自动投影)与之关联。
在以下示例中,DROP TABLE
尝试移除有多个投影与之关联的表。由于它省略了 CASCADE
选项,因此 Vertica 返回错误:
=> DROP TABLE d1;
NOTICE: Constraint - depends on Table d1
NOTICE: Projection d1p1 depends on Table d1
NOTICE: Projection d1p2 depends on Table d1
NOTICE: Projection d1p3 depends on Table d1
NOTICE: Projection f1d1p1 depends on Table d1
NOTICE: Projection f1d1p2 depends on Table d1
NOTICE: Projection f1d1p3 depends on Table d1
ERROR: DROP failed due to dependencies: Cannot drop Table d1 because other objects depend on it
HINT: Use DROP ... CASCADE to drop the dependent objects too.
=> DROP TABLE d1 CASCADE;
DROP TABLE
=> CREATE TABLE mytable (a INT, b VARCHAR(256));
CREATE TABLE
=> DROP TABLE IF EXISTS mytable;
DROP TABLE
=> DROP TABLE IF EXISTS mytable; -- Doesn't exist
NOTICE: Nothing was dropped
DROP TABLE
下一次尝试包含 CASCADE
选项,因此成功:
=> DROP TABLE d1 CASCADE;
DROP TABLE
=> CREATE TABLE mytable (a INT, b VARCHAR(256));
CREATE TABLE
=> DROP TABLE IF EXISTS mytable;
DROP TABLE
=> DROP TABLE IF EXISTS mytable; -- Doesn't exist
NOTICE: Nothing was dropped
DROP TABLE
在以下示例中,DROP TABLE
包含选项 IF EXISTS
。此选项指定当要删除的一个或多个表不存在时不报告错误。此子句在 SQL 脚本(例如在尝试重新创建表之前确保它已被删除)中十分有用。
=> DROP TABLE IF EXISTS mytable;
DROP TABLE
=> DROP TABLE IF EXISTS mytable; -- Table doesn't exist
NOTICE: Nothing was dropped
DROP TABLE
如果视图引用的表被删除,然后替换为同名的其他表,则该视图会继续正常运行,并使用新表的内容。新表必须具有相同的列定义。