这是本节的多页打印视图。
点击此处打印.
返回本页常规视图.
合并表数据
MERGE
语句可以根据与源数据集联接的结果对目标表执行更新和插入操作。联接只能将源行与一个目标行相匹配;否则,Vertica 会返回错误。
MERGE
采用以下语法:
MERGE INTO target‑table USING source‑dataset ON join-condition
matching‑clause[ matching‑clause ]
合并操作至少包含三个组成部分:
1 - 基本 MERGE 示例
在此示例中,合并操作涉及两个表:
每天晚上,您都会将 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
返回已更新和插入的行数。在本例中,返回值指定三个更新和插入项:
如果现在查询表 visits_history
,结果集会显示合并(更新和插入)的数据。更新行和新行高亮显示:
2 - MERGE 源选项
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)
3 - MERGE 匹配子句
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);
4 - 更新和插入筛选器
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 按如下方式处理筛选器:
例如,给定表 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 按如下方式使用更新和插入筛选器:
5 - MERGE 优化
可以通过以下几种方式提高 MERGE
性能:
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 可以使用局部合并联接:
-
复制的投影按以下列进行排序:
-
分段投影在以下列以相同方式进行分段:
-
表 tgt
的列 a
-
表 src
的列 b
-
相应的分段列
优化 MERGE 查询计划
如果满足以下条件,Vertica 即准备了一个经过优化的查询计划:
有关评估
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);
6 - MERGE 限制
以下限制适用于使用
MERGE
更新和插入表数据。
如果在目标表中启用了主键、唯一键或检查约束以自动强制实施,Vertica 会在您加载新数据时强制实施这些约束。如果发生违规,Vertica 会回滚操作并返回错误。
当心
如果使用相同的目标和源表多次运行 MERGE,则每次迭代都有可能将重复值引入目标列并返回错误。
禁止合并的列
合并操作中不能指定以下列,否则将返回错误: