Click here to view and discuss this page in DocCommentXchange. In the future, you will be sent there automatically.

SQL Anywhere 11.0.1 (中文) » SQL Anywhere 服务器 - SQL 的用法 » 远程数据和批量操作 » 导入和导出数据 » 导入数据

 

使用 MERGE 语句导入数据

使用 MERGE 语句可执行更新操作和更新大量表数据。合并数据时,可以指定源数据中的行与目标数据中的行匹配或不匹配时要采取的操作。

定义合并行为

为了便于说明,以下是 MERGE 语句语法的简化版本。有关 MERGE 语句的完整语法,请参见MERGE 语句

MERGE INTO target-object
USING source-object
ON merge-search-condition
{ WHEN MATCHED | WHEN NOT MATCHED } [...]

数据库执行合并操作时,该语句将根据 ON 子句中包含的定义比较 source-objecttarget-object 中的行以找到匹配或不匹配的行。如果 target-table 中至少存在一行使 merge-search-condition 的值为 true,则将 source-object 中的行视为匹配。

source-object 可以是基表、视图、实例化视图、派生表或过程的结果。target-object 可以是上述对象中除实例化视图和过程以外的任意一种。有关这些对象类型的限制的更多信息,请参见MERGE 语句

ANSI SQL/2003 标准不允许在合并操作中 target-object 中的行被 source-object 中的多个行更新。

一旦 source-object 中的行被视为匹配或不匹配,将针对相应的匹配或非匹配 WHEN 子句(WHEN MATCHED 或 WHEN NOT MATCHED)进行评估。WHEN MATCHED 子句定义对 target-object 中的行执行的操作(例如 WHEN MATCHED ...UPDATE 指定更新 target-object 中的行)。WHEN NOT MATCHED 子句定义使用 source-object 的非匹配行对 target-object 执行的操作。

可指定不受限制的 WHEN 子句,按照指定的顺序对其进行处理。还可以在 WHEN 子句内使用 AND 子句指定对行的子集的操作。例如,以下 WHEN 子句根据用于匹配行的 Quantity 列的值来定义要执行的不同操作:

WHEN MATCHED AND myTargetTable.Quantity<=500 THEN SKIP 
WHEN MATCHED AND myTargetTable.Quantity>500 THEN UPDATE SET myTargetTable.Quantity=500
合并操作中的分支

通过操作对匹配行和非匹配行的分组称作分支,每一组称为一个分支分支相当于一个 WHEN MATCHED 或 WHEN NOT MATCHED 子句。例如,一个分支可能包含必须被插入的 source-object 中的非匹配行集。只有在所有分支活动完成后(已计算 source-object 中的所有行),才能开始执行分支操作。数据库服务器根据指定 WHEN 子句的顺序执行分支的操作。

分支中一旦有 source-object 中的非匹配行或者 source-objecttarget-object 中的匹配行对,就不会对后续分支进行评估。这样指定 WHEN 子句的顺序就非常重要。

将忽略 source-object 中被视为匹配或非匹配但不属于任何分支的行(也就是说,它不满足任何 WHEN 子句)。当 WHEN 子句包含 AND 子句且行不满足任何 AND 子句的条件时,可能发生这种情况。在这种情况下,由于没有对其定义任何操作,因此忽略该行。

在事务日志中,修改数据的操作记录为各个 INSERT、UPDATE 和 DELETE 语句。

在目标表中定义的触发器

合并操作期间执行每个 INSERT、UPDATE 和 DELETE 语句时触发器正常触发。例如,处理为其定义 UPDATE 操作的分支时,数据库服务器将:

  1. 触发所有 BEFORE UPDATE 触发器

  2. 触发任何行级 UPDATE 触发器时,对行的候选集执行 UPDATE 语句

  3. 触发 AFTER UPDATE 触发器

如果影响了将要在另一分支中更新的行,则合并操作期间 target-table 中的触发器会导致冲突。例如,假定在行 A 中执行了一项操作,从而触发了删除行 B 的触发器。但行 B 有一个为其定义的、尚未执行的操作。当无法对行执行操作时,合并操作失败,回退所有更改并返回错误。

将用多个触发器操作定义的触发器视为对同一主体将每个触发器操作都指定一次(也就是说,等效于定义单独的触发器,每个触发器具备一个触发器操作)。

有关快速实例化视图的注意事项

如果 MERGE 语句更新大量的行,可能会影响数据库服务器性能。如果要更新大量的行,请在对表执行 MERGE 语句之前首先考虑截断相关快速实例化视图中的数据。执行 MERGE 语句之后,请执行 REFRESH MATERIALIZED VIEW 语句。请参见REFRESH MATERIALIZED VIEW 语句TRUNCATE 语句

有关文本索引的注意事项

如果 MERGE 语句更新大量的行,可能会影响数据库服务器性能。在对表执行 MERGE 语句之前,请考虑删除相关的文本索引。执行了 MERGE 语句后,重新创建文本索引。请参见DROP TEXT INDEX 语句CREATE TEXT INDEX 语句

示例 1

假定您拥有销售夹克衫和毛衣的小型业务。夹克衫的原料价格上升了 5%,您想要调整价格与其匹配。使用以下 CREATE TABLE 语句,创建名为 myProducts 的小表以保存所销售的夹克衫和毛衣的当前价格信息。随后的 INSERT 语句给 myProducts 填充数据。

CREATE TABLE myProducts (
   product_id    NUMERIC(10),
   product_name  CHAR(20),
   product_size  CHAR(20),
   product_price NUMERIC(14,2));
INSERT INTO myProducts VALUES (1, 'Jacket', 'Small', 29.99);
INSERT INTO myProducts VALUES (2, 'Jacket', 'Medium', 29.99);
INSERT INTO myProducts VALUES (3, 'Jacket', 'Large', 39.99);
INSERT INTO myProducts VALUES (4, 'Sweater', 'Small', 18.99);
INSERT INTO myProducts VALUES (5, 'Sweater', 'Medium', 18.99);
INSERT INTO myProducts VALUES (6, 'Sweater', 'Large', 19.99);
SELECT * FROM myProducts;
product_id product_name product_size product_price
1 Jacket Small 29.99
2 Jacket Medium 29.99
3 Jacket Large 39.99
4 Sweater Small 18.99
5 Sweater Medium 18.99
6 Sweater Large 19.99

现在,使用以下语句创建另一名为 myPrices 的表以保存夹克衫的价格更改信息。结束时添加 SELECT 语句,以便在执行合并操作之前可以看到 myPrices 表的内容。

CREATE TABLE myPrices (
   product_id    NUMERIC(10), 
   product_name  CHAR(20), 
   product_size  CHAR(20), 
   product_price NUMERIC(14,2), 
   new_price     NUMERIC(14,2));
INSERT INTO myPrices (product_id) VALUES (1);
INSERT INTO myPrices (product_id) VALUES (2);
INSERT INTO myPrices (product_id) VALUES (3);
INSERT INTO myPrices (product_id) VALUES (4);
INSERT INTO myPrices (product_id) VALUES (5);
INSERT INTO myPrices (product_id) VALUES (6);
COMMIT;
SELECT * FROM myPrices;
product_id product_name product_size product_price new_price
1 (NULL) (NULL) (NULL) (NULL)
2 (NULL) (NULL) (NULL) (NULL)
3 (NULL) (NULL) (NULL) (NULL)
4 (NULL) (NULL) (NULL) (NULL)
5 (NULL) (NULL) (NULL) (NULL)
6 (NULL) (NULL) (NULL) (NULL)

使用以下 MERGE 语句将数据从 myProducts 表合并到 myPrices 表。请注意,source-object 是派生表,它经过过滤仅包含 product_name 为 Jacket 的行。另请注意,如果 target-objectsource-object 的 product_id 列中的值匹配,则 ON 子句指定其中的行匹配。

MERGE INTO myPrices p
USING ( SELECT 
     product_id, 
     product_name, 
     product_size, 
     product_price
  FROM myProducts
  WHERE product_name='Jacket') pp
ON (p.product_id = pp.product_id)
WHEN MATCHED THEN
  UPDATE SET 
    p.product_id=pp.product_id,
    p.product_name=pp.product_name,
    p.product_size=pp.product_size,
    p.product_price=pp.product_price,
    p.new_price=pp.product_price * 1.05;
SELECT * FROM myPrices;
product_id product_name product_size product_price new_price
1 Jacket Small 29.99 31.49
2 Jacket Medium 29.99 31.49
3 Jacket Large 39.99 41.99
4 (NULL) (NULL) (NULL) (NULL)
5 (NULL) (NULL) (NULL) (NULL)
6 (NULL) (NULL) (NULL) (NULL)

product_id 4、5 和 6 的列值都保持 NULL,因为这些产品与 myProducts 表(其产品为 (product_name='Jacket'))中的任何行都不匹配。

示例 2

以下示例使用 myTargetTable 的主键值来匹配行,以此来合并 mySourceTable 表和 myTargetTable 表中的行。如果 mySourceTable 中行的值与 myTargetTable 的主键列值相同,则将该行视为匹配行。

MERGE INTO myTargetTable
   USING mySourceTable ON PRIMARY KEY
   WHEN NOT MATCHED THEN INSERT
   WHEN MATCHED THEN UPDATE;

WHEN NOT MATCHED THEN INSERT 子句指定,在 mySourceTable 中找到的、但在 myTargetTable 中找不到的行必须添加到 myTargetTable。WHEN MATCHED THEN UPDATE 子句指定,将 myTargetTable 的匹配行更新为 mySourceTable 中的值。

下面的语法与以上语法等效。它假定 myTargetTable 具有列(I1、I2、...In),并且主键定义在列(I1、I2)上。此表包含三列:N + 1

MERGE INTO myTargetTable ( I1, I2, .. ., In )
   USING mySourceTable ON myTargetTable.I1 = mySourceTable.U1
      AND myTargetTable.I2 = mySourceTable.U2
   WHEN NOT MATCHED 
      THEN INSERT ( I1, I2, .. In ) 
         VALUES ( mySourceTable.U1, mySourceTable.U2, ..., mySourceTable.Un )
   WHEN MATCHED 
      THEN UPDATE SET
      myTargetTable.I1 = mySourceTable.U1,
      myTargetTable.I2 = mySourceTable.U2,
      ...
      myTargetTable.In = mySourceTable.Un;
使用 RAISERROR 操作

为匹配或非匹配动作指定的操作之一是 RAISERROR。如果满足 WHEN 子句的条件,RAISERROR 允许合并操作失败。

指定 RAISERROR 时,缺省条件下数据库服务器返回 SQLSTATE 23510 和 SQLCODE -1254。另外,在 RAISERROR 关键字之后,可通过指定 error_number 参数自定义返回的 SQLCODE。请参见MERGE 语句

指定自定义 SQLCODE 对于以后尝试确定导致错误产生的特定情况会有益处。

自定义 SQLCODE 必须是大于 17000 的正整数,并且可以指定为数字或变量。

以下语句简单说明了定制自定义 SQLCODE 如何影响返回内容:

按如下所示创建表 targetTable:

CREATE TABLE targetTable( c1 int );
INSERT INTO targetTable VALUES( 1 );
COMMIT;

以下语句返回错误 SQLSTATE = '23510' 和 SQLCODE = -1254:

MERGE INTO targetTable
   USING (SELECT 1 c1 ) AS sourceData
   ON targetTable.c1 = sourceData.c1
   WHEN MATCHED THEN RAISERROR;
SELECT sqlstate, sqlcode;

以下语句返回错误 SQLSTATE = '23510' 和 SQLCODE = -17001:

MERGE INTO targetTable
   USING (SELECT 1 c1 ) AS sourceData
   ON targetTable.c1 = sourceData.c1
   WHEN MATCHED THEN RAISERROR 17001
   WHEN NOT MATCHED THEN RAISERROR 17002;
SELECT sqlstate, sqlcode;

以下语句返回错误 SQLSTATE = '23510' 和 SQLCODE = -17002:

MERGE INTO targetTable
   USING (SELECT 2 c1 ) AS sourceData
   ON targetTable.c1 = sourceData.c1
   WHEN MATCHED THEN RAISERROR 17001
   WHEN NOT MATCHED THEN RAISERROR 17002;
SELECT sqlstate, sqlcode;