这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

合并表数据

MERGE 语句可以根据与源数据集联接的结果对目标表执行更新插入操作。联接只能将源行与一个目标行相匹配;否则,Vertica 会返回错误。

MERGE 采用以下语法:

MERGE INTO target‑table USING source‑dataset ON  join-condition
matching‑clause[ matching‑clause ]

合并操作至少包含三个组成部分:

1 - 基本 MERGE 示例

在此示例中,合并操作涉及两个表:

  • visits_daily 记录每日餐厅流量,并随着每次顾客到访而更新。此表中的数据每 24 小时刷新一次。

  • visits_history 存储顾客到访各个餐厅的历史记录(无限期累计)。

每天晚上,您都会将 visits_daily 的每日到访计数合并到 visits_history。合并操作通过两种方式修改目标表:

  • 更新现有顾客数据。

  • 为首次到访的顾客插入新数据行。

一个 MERGE 语句将这两项操作作为单个 (upsert) 事务来执行。

源表和目标表

源表和目标表 visits_dailyvisits_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,结果集会显示合并(更新和插入)的数据。更新行和新行高亮显示:

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_keyproduct_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_keyproduct_version 列构成表的主键。可以修改此表,使其包含连接这两列的值的单个列。此列可用于唯一标识每个产品,同时还保留 product_keyproduct_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_keyproduct_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 子句在新插入的行中隐式设置目标表列 abc 的值:

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.bt2.yWHEN NOT MATCHED 子句显式将目标表和源表中的两组列进行配对: t1.at2.x 以及 t1.ct2.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 MATCHEDWHEN 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 都会在目标表中插入一个新行。

5 - MERGE 优化

可以通过以下几种方式提高 MERGE 性能:

MERGE 操作的投影

Vertica 查询优化器会自动选择最佳投影来实施合并操作。良好投影设计策略提供的投影可帮助查询优化器避免额外的排序和数据传输操作,并提高 MERGE 性能。

例如,以下 MERGE 语句片段分别在 tgt.asrc.b 列上联接源表和目标表 tgt src

=> MERGE INTO tgt USING src ON tgt.a = src.b ...

如果 tgtsrc 表的投影采用以下投影设计之一(其中输入通过投影 ORDER BY 子句进行预排序),则 Vertica 可以使用局部合并联接:

  • 复制的投影按以下列进行排序:

    • tgt 的列 a

    • src 的列 b

  • 分段投影在以下列以相同方式进行分段

    • tgt 的列 a

    • src 的列 b

    • 相应的分段列

优化 MERGE 查询计划

如果满足以下条件,Vertica 即准备了一个经过优化的查询计划:

  • MERGE 语句同时包含两个匹配子句 WHEN MATCHED THEN UPDATE SETWHEN 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) 中的所有列都包含在 UPDATEINSERT 子句中。

  • UPDATEINSERT 子句指定相同的源值:s.as.bs.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.as.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 语句符合优化条件,请重写该语句,以使 UPDATEINSERT 子句中的源值相同:


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 会回滚操作并返回错误。

禁止合并的列

合并操作中不能指定以下列,否则将返回错误: