"дублированный ключ слияния обнаружен в соединении" в vertica - PullRequest
1 голос
/ 05 августа 2020

Я пытаюсь использовать MERGE для вставки агрегированных данных в коллекцию таблиц. Для каждой таблицы некоторые поля допускают значение NULL, а некоторые - нет. В fieldA и FieldB объявлено два первичных ключа. Когда я запускаю операторы MERGE, я получаю сообщение об ошибке «При соединении обнаружен повторяющийся ключ MERGE». Может ли кто-нибудь помочь мне решить эту проблему.

NB: когда я усекаю целевую таблицу и если какая-либо операция обновления выполняется в поле C или FieldD во временной таблице, а затем выполняется запрос слияния, он работает нормально . Но в следующий раз, когда произойдет какая-либо операция обновления в поле C или fieldD во временной таблице, а затем будет запущен запрос на слияние, вы получите эту ошибку.

Пример псевдосинтаксиса:

CREATE TABLE target (
fieldA varchar,
fieldB varchar,
fieldC varchar null,
fieldD int not null,
FieldE int,
Constraint target_pk primary key(fieldA, fieldB));

CREATE TABLE temp (
fieldA varchar,
fieldB varchar,
fieldC varchar null,
fieldD int not null,
fieldE int,
Constraint temp_pk primary key(fieldA, fieldB));

MERGE INTO target trg
USING temp tmp ON (
    trg.FieldA = tmp.FieldA
    AND trg.FieldB = tmp.FieldB)
   
WHEN MATCHED THEN UPDATE
    SET fieldC = tmp.fieldC ,
    fieldD= tmp.fieldD,
    fieldE=temp.fieldE

WHEN NOT MATCHED THEN INSERT (
    FieldA ,
    FieldB ,
    FieldC ,
    fieldD,
    fieldE)
VALUES (
    tmp.FieldA ,
    tmp.FieldB ,
    tmp.FieldC ,
    tmp.fieldD,
    tmp.fieldE);

1 Ответ

3 голосов
/ 05 августа 2020

Вначале целевая таблица пуста, и у нее даже нет проекции для хранения данных. При этом план доступа становится простой вставкой. Фактически, когда вы ОБЪЯСНИТЕ СЛИЯНИЕ с пустой целью и полным источником, вы получите пустой план объяснения.

-- here's a scenario - improved a bit - 
-- never, if you can avoid it, use a VARCHAR with no length info
DROP TABLE IF EXISTS target;
DROP TABLE IF EXISTS temp;
CREATE TABLE target (
fieldA VARCHAR(8) NOT NULL,
fieldB VARCHAR(8) NOT NULL,
fieldC VARCHAR(8) NULL,
fieldD INT        NOT NULL,
FieldE INT,
CONSTRAINT target_pk PRIMARY KEY(fieldA, fieldB)
);

CREATE TABLE temp LIKE target INCLUDING PROJECTIONS;

INSERT INTO temp
          SELECT '1','1','A',1,1
UNION ALL SELECT '1','1','A',1,1 -- note that this is a duplicate of the row above
UNION ALL SELECT '1','2','B',1,2
UNION ALL SELECT '1','3','C',1,3
UNION ALL SELECT '1','4','D',1,4
UNION ALL SELECT '1','5','E',1,5
;
COMMIT;

Объяснение с пустой таблицей:

EXPLAIN
MERGE INTO target trg
USING temp tmp 
 ON trg.FieldA = tmp.FieldA
AND trg.FieldB = tmp.FieldB
   
WHEN MATCHED THEN UPDATE SET 
    fieldC = tmp.fieldC ,
    fieldD = tmp.fieldD,
    fieldE = tmp.fieldE

WHEN NOT MATCHED THEN INSERT (
    FieldA ,
    FieldB ,
    FieldC ,
    fieldD,
    fieldE
) VALUES (
    tmp.FieldA ,
    tmp.FieldB ,
    tmp.FieldC ,
    tmp.fieldD,
    tmp.fieldE
);
-- out  Access Path:
-- out  
-- out  
-- out  ------------------------------
-- empty.

Теперь я выполните первое MERGE и COMMIT:

MERGE INTO target trg
USING temp tmp
 ON trg.FieldA = tmp.FieldA
AND trg.FieldB = tmp.FieldB
WHEN MATCHED THEN UPDATE SET
    fieldC = tmp.fieldC ,
    fieldD = tmp.fieldD,
    fieldE = tmp.fieldE
WHEN NOT MATCHED THEN INSERT (
    FieldA ,
    FieldB ,
    FieldC ,
    fieldD,
    fieldE
) VALUES (
    tmp.FieldA ,
    tmp.FieldB ,
    tmp.FieldC ,
    tmp.fieldD,
    tmp.fieldE
);

COMMIT;
-- out  OUTPUT 
-- out --------
-- out       6                                                              
-- out (1 row)
-- out 
-- out Time: First fetch (1 row): 142.089 ms. All rows formatted: 142.152 ms
-- out Time: First fetch (0 rows): 5.042 ms. All rows formatted: 5.059 ms

И я снова объясню MERGE:

-- out  Access Path:
-- out  +-DML MERGE [Cost: 0, Rows: 0]
-- out  |  Target Projection: dbadmin.target_super
-- out  |  Target Prep:
-- out  | +---> JOIN MERGEJOIN(inputs presorted) [RightOuter] [Cost: 7K, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
-- out  | |      Join Cond: (target.fieldA = VAL(2)) AND (target.fieldB = VAL(2))
-- out  | | +-- Outer -> STORAGE ACCESS for <No Alias> [Cost: 4K, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
-- out  | | |      Projection: dbadmin.target_super
-- out  | | |      Materialize: target.fieldA, target.fieldB, target.fieldC, target.fieldD, target.FieldE, target.epoch
-- out  | | |      Runtime Filters: (SIP1(MergeJoin): target.fieldA), (SIP2(MergeJoin): target.fieldB), (SIP3(MergeJoin): target.fieldA, target.fieldB)
-- out  | | +-- Inner -> SELECT [Cost: 3K, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
-- out  | | | +---> STORAGE ACCESS for tmp [Cost: 3K, Rows: 10K (NO STATISTICS)] (PATH ID: 4)
-- out  | | | |      Projection: dbadmin.temp_super
-- out  | | | |      Materialize: tmp.fieldA, tmp.fieldB, tmp.fieldC, tmp.fieldD, tmp.FieldE

Теперь вы видите, что есть операция DML MERGE, которая читает из JOIN MERGEJOIN операция.

При усечении target проекция остается, но пуста. Фактически вы получаете вторую версию EXPLAIN также тогда, когда целевая таблица усечена, но имеет проекцию.

Вы обнаруживаете особенность Vertica, потому что это платформа для больших данных, оптимизирован для производительности.

  1. Ограничение таблицы по умолчанию DISABLED, и если вы хотите, чтобы оно было включено, вы должны ALTER TABLE ENABLE CONSTRAINT ... - тем самым замедляя любые манипуляции с массовыми данными
  2. С отключенным ограничением первичного ключа вы можете вставлять дубликаты, но последующие JOIN с использованием столбцов первичного ключа целевой таблицы - таблицы OUTER в объяснении plan - попытается воспользоваться уникальностью при построении плана - и потерпит неудачу, если обнаружит повторяющиеся ключи. И это успешно с пустой внешней целевой таблицей.

Из-за этого происходит сбой вашего MERGE с полной целью, содержащей повторяющиеся ключи.

Вам нужна помощь в удалении дубликатов? Есть несколько сообщений с тегом [Vertica], посвященным этому ...

...