Перейти к основному содержимому

Изменение данных через загрузку

Таблицы с первичным ключом, предоставляемые Selena, позволяют вносить изменения в данные таблиц Selena путем выполнения заданий Stream Load, Broker Load или Routine Load. Эти изменения данных включают вставки, обновления и удаления. Однако таблицы с первичным ключом не поддерживают изменение данных с использованием Spark Load или INSERT.

Selena также поддерживает частичные обновления и условные обновления.

Вы можете загружать данные в таблицы Selena только как пользователь, имеющий привилегию INSERT на эти таблицы Selena. Если у вас нет привилегии INSERT, следуйте инструкциям в GRANT, чтобы предоставить привилегию INSERT пользователю, которого вы используете для подключения к вашему кластеру Selena. Синтаксис: GRANT INSERT ON TABLE <table_name> IN DATABASE <database_name> TO { ROLE <role_name> | USER <user_identity>}.

В этой теме используются данные CSV в качестве примера для описания того, как вносить изменения в данные таблицы Selena через загрузку. Поддерживаемые форматы файлов данных различаются в зависимости от выбранного метода загрузки.

ПРИМЕЧАНИЕ

Для данных CSV вы можете использовать строку UTF-8, такую как запятая (,), табуляция или вертикальная черта (|), длина которой не превышает 50 байт, в качестве разделителя текста.

Реализация

Таблицы с первичным ключом, предоставляемые Selena, поддерживают операции UPSERT и DELETE и не различают операции INSERT и UPDATE.

При создании задания загрузки Selena поддерживает добавление поля с именем __op в оператор или команду создания задания. Поле __op используется для указания типа операции, которую вы хотите выполнить.

ПРИМЕЧАНИЕ

При создании таблицы вам не нужно добавлять столбец с именем __op в эту таблицу.

Метод определения поля __op различается в зависимости от выбранного метода загрузки:

  • Если вы выбираете Stream Load, определите поле __op с помощью параметра columns.

  • Если вы выбираете Broker Load, определите поле __op с помощью предложения SET.

  • Если вы выбираете Routine Load, определите поле __op с помощью столбца COLUMNS.

Вы можете решить, добавлять ли поле __op, основываясь на изменениях данных, которые вы хотите внести. Если вы не добавляете поле __op, тип операции по умолчанию устанавливается как UPSERT. Основные сценарии изменения данных следующие:

  • Если файл данных, который вы хотите загрузить, включает только операции UPSERT, вам не нужно добавлять поле __op.

  • Если файл данных, который вы хотите загрузить, включает только операции DELETE, вы должны добавить поле __op и указать тип операции как DELETE.

  • Если файл данных, который вы хотите загрузить, включает как операции UPSERT, так и DELETE, вы должны добавить поле __op и убедиться, что файл данных содержит столбец, значения которого равны 0 или 1. Значение 0 указывает на операцию UPSERT, а значение 1 указывает на операцию DELETE.

Примечания по использованию

  • Убедитесь, что каждая строка в вашем файле данных имеет одинаковое количество столбцов.

  • Столбцы, которые участвуют в изменениях данных, должны включать столбец первичного ключа.

Основные операции

В этом разделе приведены примеры того, как вносить изменения в данные таблицы Selena через загрузку. Для подробного описания синтаксиса и параметров см. STREAM LOAD, BROKER LOAD и CREATE ROUTINE LOAD.

UPSERT

Если файл данных, который вы хотите загрузить, включает только операции UPSERT, вам не нужно добавлять поле __op.

ПРИМЕЧАНИЕ

Если вы добавляете поле __op:

  • Вы можете указать тип операции как UPSERT.

  • Вы можете оставить поле __op пустым, поскольку тип операции по умолчанию устанавливается как UPSERT.

Примеры данных

  1. Подготовьте файл данных.

    a. Создайте CSV-файл с именем example1.csv в вашей локальной файловой системе. Файл состоит из трех столбцов, которые представляют ID пользователя, имя пользователя и оценку пользователя по порядку.

    101,Lily,100
    102,Rose,100

    b. Опубликуйте данные example1.csv в topic1 вашего кластера Kafka.

  2. Подготовьте таблицу Selena.

    a. Создайте таблицу с первичным ключом с именем table1 в вашей базе данных Selena test_db. Таблица состоит из трех столбцов: id, name и score, где id является первичным ключом.

    CREATE TABLE `table1`
    (
    `id` int(11) NOT NULL COMMENT "user ID",
    `name` varchar(65533) NOT NULL COMMENT "user name",
    `score` int(11) NOT NULL COMMENT "user score"
    )
    ENGINE=OLAP
    PRIMARY KEY(`id`)
    DISTRIBUTED BY HASH(`id`);

    ПРИМЕЧАНИЕ

    Начиная с версии 1.5.0, Selena может автоматически устанавливать количество корзин (BUCKETS) при создании таблицы или добавлении раздела. Вам больше не нужно вручную устанавливать количество корзин. Для получения подробной информации см. установка количества корзин.

    b. Вставьте запись в table1.

    INSERT INTO table1 VALUES
    (101, 'Lily',80);

Загрузка данных

Выполните задание загрузки для обновления записи с id равным 101 из example1.csv в table1 и вставки записи с id равным 102 из example1.csv в table1.

  • Выполните задание Stream Load.

    • Если вы не хотите включать поле __op, выполните следующую команду:

      curl --location-trusted -u <username>:<password> \
      -H "Expect:100-continue" \
      -H "label:label1" \
      -H "column_separator:," \
      -T example1.csv -XPUT \
      http://<fe_host>:<fe_http_port>/api/test_db/table1/_stream_load
    • Если вы хотите включить поле __op, выполните следующую команду:

      curl --location-trusted -u <username>:<password> \
      -H "Expect:100-continue" \
      -H "label:label2" \
      -H "column_separator:," \
      -H "columns:__op ='upsert'" \
      -T example1.csv -XPUT \
      http://<fe_host>:<fe_http_port>/api/test_db/table1/_stream_load
  • Выполните задание Broker Load.

    • Если вы не хотите включать поле __op, выполните следующую команду:

      LOAD LABEL test_db.label1
      (
      data infile("hdfs://<hdfs_host>:<hdfs_port>/example1.csv")
      into table table1
      columns terminated by ","
      format as "csv"
      )
      WITH BROKER;
    • Если вы хотите включить поле __op, выполните следующую команду:

      LOAD LABEL test_db.label2
      (
      data infile("hdfs://<hdfs_host>:<hdfs_port>/example1.csv")
      into table table1
      columns terminated by ","
      format as "csv"
      set (__op = 'upsert')
      )
      WITH BROKER;
  • Выполните задание Routine Load.

    • Если вы не хотите включать поле __op, выполните следующую команду:

      CREATE ROUTINE LOAD test_db.table1 ON table1
      COLUMNS TERMINATED BY ",",
      COLUMNS (id, name, score)
      PROPERTIES
      (
      "desired_concurrent_number" = "3",
      "max_batch_interval" = "20",
      "max_batch_rows"= "250000",
      "max_error_number" = "1000"
      )
      FROM KAFKA
      (
      "kafka_broker_list" ="<kafka_broker_host>:<kafka_broker_port>",
      "kafka_topic" = "test1",
      "property.kafka_default_offsets" ="OFFSET_BEGINNING"
      );
    • Если вы хотите включить поле __op, выполните следующую команду:

      CREATE ROUTINE LOAD test_db.table1 ON table1
      COLUMNS TERMINATED BY ",",
      COLUMNS (id, name, score, __op ='upsert')
      PROPERTIES
      (
      "desired_concurrent_number" = "3",
      "max_batch_interval" = "20",
      "max_batch_rows"= "250000",
      "max_error_number" = "1000"
      )
      FROM KAFKA
      (
      "kafka_broker_list" ="<kafka_broker_host>:<kafka_broker_port>",
      "kafka_topic" = "test1",
      "property.kafka_default_offsets" ="OFFSET_BEGINNING"
      );

Запрос данных

После завершения загрузки запросите данные table1, чтобы убедиться, что загрузка прошла успешно:

SELECT * FROM table1;
+------+------+-------+
| id | name | score |
+------+------+-------+
| 101 | Lily | 100 |
| 102 | Rose | 100 |
+------+------+-------+
2 rows in set (0.02 sec)

Как показано в предыдущем результате запроса, запись с id равным 101 из example1.csv была обновлена в table1, а запись с id равным 102 из example1.csv была вставлена в table1.

DELETE

Если файл данных, который вы хотите загрузить, включает только операции DELETE, вы должны добавить поле __op и указать тип операции как DELETE.

Примеры данных

  1. Подготовьте файл данных.

    a. Создайте CSV-файл с именем example2.csv в вашей локальной файловой системе. Файл состоит из трех столбцов, которые представляют ID пользователя, имя пользователя и оценку пользователя по порядку.

    101,Jack,100

    b. Опубликуйте данные example2.csv в topic2 вашего кластера Kafka.

  2. Подготовьте таблицу Selena.

    a. Создайте таблицу с первичным ключом с именем table2 в вашей таблице Selena test_db. Таблица состоит из трех столбцов: id, name и score, где id является первичным ключом.

    CREATE TABLE `table2`
    (
    `id` int(11) NOT NULL COMMENT "user ID",
    `name` varchar(65533) NOT NULL COMMENT "user name",
    `score` int(11) NOT NULL COMMENT "user score"
    )
    ENGINE=OLAP
    PRIMARY KEY(`id`)
    DISTRIBUTED BY HASH(`id`);

    ПРИМЕЧАНИЕ

    Начиная с версии 1.5.0, Selena может автоматически устанавливать количество корзин (BUCKETS) при создании таблицы или добавлении раздела. Вам больше не нужно вручную устанавливать количество корзин. Для получения подробной информации см. установка количества корзин.

    b. Вставьте две записи в table2.

    INSERT INTO table2 VALUES
    (101, 'Jack', 100),
    (102, 'Bob', 90);

Загрузка данных

Выполните задание загрузки для удаления записи с id равным 101 из example2.csv из table2.

  • Выполните задание Stream Load.

    curl --location-trusted -u <username>:<password> \
    -H "Expect:100-continue" \
    -H "label:label3" \
    -H "column_separator:," \
    -H "columns:__op='delete'" \
    -T example2.csv -XPUT \
    http://<fe_host>:<fe_http_port>/api/test_db/table2/_stream_load
  • Выполните задание Broker Load.

    LOAD LABEL test_db.label3
    (
    data infile("hdfs://<hdfs_host>:<hdfs_port>/example2.csv")
    into table table2
    columns terminated by ","
    format as "csv"
    set (__op = 'delete')
    )
    WITH BROKER;
  • Выполните задание Routine Load.

    CREATE ROUTINE LOAD test_db.table2 ON table2
    COLUMNS(id, name, score, __op = 'delete')
    PROPERTIES
    (
    "desired_concurrent_number" = "3",
    "max_batch_interval" = "20",
    "max_batch_rows"= "250000",
    "max_error_number" = "1000"
    )
    FROM KAFKA
    (
    "kafka_broker_list" ="<kafka_broker_host>:<kafka_broker_port>",
    "kafka_topic" = "test2",
    "property.kafka_default_offsets" ="OFFSET_BEGINNING"
    );

Запрос данных

После завершения загрузки запросите данные table2, чтобы убедиться, что загрузка прошла успешно:

SELECT * FROM table2;
+------+------+-------+
| id | name | score |
+------+------+-------+
| 102 | Bob | 90 |
+------+------+-------+
1 row in set (0.00 sec)

Как показано в предыдущем результате запроса, запись с id равным 101 из example2.csv была удалена из table2.

UPSERT и DELETE

Если файл данных, который вы хотите загрузить, включает как операции UPSERT, так и DELETE, вы должны добавить поле __op и убедиться, что файл данных содержит столбец, значения которого равны 0 или 1. Значение 0 указывает на операцию UPSERT, а значение 1 указывает на операцию DELETE.

Примеры данных

  1. Подготовьте файл данных.

    a. Создайте CSV-файл с именем example3.csv в вашей локальной файловой системе. Файл состоит из четырех столбцов, которые представляют ID пользователя, имя пользователя, оценку пользователя и тип операции по порядку.

    101,Tom,100,1
    102,Sam,70,0
    103,Stan,80,0

    b. Опубликуйте данные example3.csv в topic3 вашего кластера Kafka.

  2. Подготовьте таблицу Selena.

    a. Создайте таблицу с первичным ключом с именем table3 в вашей базе данных Selena test_db. Таблица состоит из трех столбцов: id, name и score, где id является первичным ключом.

    CREATE TABLE `table3`
    (
    `id` int(11) NOT NULL COMMENT "user ID",
    `name` varchar(65533) NOT NULL COMMENT "user name",
    `score` int(11) NOT NULL COMMENT "user score"
    )
    ENGINE=OLAP
    PRIMARY KEY(`id`)
    DISTRIBUTED BY HASH(`id`);

    ПРИМЕЧАНИЕ

    Начиная с версии 1.5.0, Selena может автоматически устанавливать количество корзин (BUCKETS) при создании таблицы или добавлении раздела. Вам больше не нужно вручную устанавливать количество корзин. Для получения подробной информации см. установка количества корзин.

    b. Вставьте две записи в table3.

    INSERT INTO table3 VALUES
    (101, 'Tom', 100),
    (102, 'Sam', 90);

Загрузка данных

Выполните задание загрузки для удаления записи с id равным 101 из example3.csv из table3, обновления записи с id равным 102 из example3.csv в table3 и вставки записи с id равным 103 из example3.csv в table3.

  • Выполните задание Stream Load:

    curl --location-trusted -u <username>:<password> \
    -H "Expect:100-continue" \
    -H "label:label4" \
    -H "column_separator:," \
    -H "columns: id, name, score, temp, __op = temp" \
    -T example3.csv -XPUT \
    http://<fe_host>:<fe_http_port>/api/test_db/table3/_stream_load

    ПРИМЕЧАНИЕ

    В предыдущем примере четвертый столбец, который представляет тип операции в example3.csv, временно назван как temp, а поле __op сопоставляется со столбцом temp с помощью параметра columns. Таким образом, Selena может решить, выполнять ли операцию UPSERT или DELETE в зависимости от того, является ли значение в четвертом столбце example3.csv равным 0 или 1.

  • Выполните задание Broker Load:

    LOAD LABEL test_db.label4
    (
    data infile("hdfs://<hdfs_host>:<hdfs_port>/example1.csv")
    into table table1
    columns terminated by ","
    format as "csv"
    (id, name, score, temp)
    set (__op=temp)
    )
    WITH BROKER;
  • Выполните задание Routine Load:

    CREATE ROUTINE LOAD test_db.table3 ON table3
    COLUMNS(id, name, score, temp, __op = temp)
    PROPERTIES
    (
    "desired_concurrent_number" = "3",
    "max_batch_interval" = "20",
    "max_batch_rows"= "250000",
    "max_error_number" = "1000"
    )
    FROM KAFKA
    (
    "kafka_broker_list" = "<kafka_broker_host>:<kafka_broker_port>",
    "kafka_topic" = "test3",
    "property.kafka_default_offsets" = "OFFSET_BEGINNING"
    );

Запрос данных

После завершения загрузки запросите данные table3, чтобы убедиться, что загрузка прошла успешно:

SELECT * FROM table3;
+------+------+-------+
| id | name | score |
+------+------+-------+
| 102 | Sam | 70 |
| 103 | Stan | 80 |
+------+------+-------+
2 rows in set (0.01 sec)

Как показано в предыдущем результате запроса, запись с id равным 101 из example3.csv была удалена из table3, запись с id равным 102 из example3.csv была обновлена в table3, а запись с id равным 103 из example3.csv была вставлена в table3.

Частичные обновления

Таблицы с первичным ключом также поддерживают частичные обновления и предоставляют два режима частичных обновлений, строковый режим и столбцовый режим, для различных сценариев обновления данных. Эти два режима частичных обновлений могут минимизировать накладные расходы частичных обновлений как можно больше, гарантируя при этом производительность запросов и обеспечивая обновления в реальном времени. Строковый режим больше подходит для сценариев обновления в реальном времени, включающих много столбцов и небольшие пакеты. Столбцовый режим подходит для сценариев пакетной обработки обновлений, включающих несколько столбцов и большое количество строк.

УВЕДОМЛЕНИЕ

При выполнении частичного обновления, если строка, которую нужно обновить, не существует, Selena вставляет новую строку и заполняет значения по умолчанию в полях, которые остаются пустыми, поскольку в них не вставляются обновления данных.

В этом разделе используется CSV в качестве примера для описания того, как выполнять частичные обновления.

Примеры данных

  1. Подготовьте файл данных.

    a. Создайте CSV-файл с именем example4.csv в вашей локальной файловой системе. Файл состоит из двух столбцов, которые представляют ID пользователя и имя пользователя по порядку.

    101,Lily
    102,Rose
    103,Alice

    b. Опубликуйте данные example4.csv в topic4 вашего кластера Kafka.

  2. Подготовьте таблицу Selena.

    a. Создайте таблицу с первичным ключом с именем table4 в вашей базе данных Selena test_db. Таблица состоит из трех столбцов: id, name и score, где id является первичным ключом.

    CREATE TABLE `table4`
    (
    `id` int(11) NOT NULL COMMENT "user ID",
    `name` varchar(65533) NOT NULL COMMENT "user name",
    `score` int(11) NOT NULL COMMENT "user score"
    )
    ENGINE=OLAP
    PRIMARY KEY(`id`)
    DISTRIBUTED BY HASH(`id`);

    ПРИМЕЧАНИЕ

    Начиная с версии 1.5.0, Selena может автоматически устанавливать количество корзин (BUCKETS) при создании таблицы или добавлении раздела. Вам больше не нужно вручную устанавливать количество корзин. Для получения подробной информации см. установка количества корзин.

    b. Вставьте запись в table4.

    INSERT INTO table4 VALUES
    (101, 'Tom',80);

Загрузка данных

Выполните загрузку для обновления данных в двух столбцах example4.csv в столбцы id и name таблицы table4.

  • Выполните задание Stream Load:

    curl --location-trusted -u <username>:<password> \
    -H "Expect:100-continue" \
    -H "label:label7" -H "column_separator:," \
    -H "partial_update:true" \
    -H "columns:id,name" \
    -T example4.csv -XPUT \
    http://<fe_host>:<fe_http_port>/api/test_db/table4/_stream_load

    ПРИМЕЧАНИЕ

    Если вы выбираете Stream Load, вы должны установить параметр partial_update в true, чтобы включить функцию частичного обновления. По умолчанию используются частичные обновления в строковом режиме. Если вам нужно выполнить частичные обновления в столбцовом режиме, вам нужно установить partial_update_mode в column. Кроме того, вы должны использовать параметр columns для указания столбцов, которые вы хотите обновить.

  • Выполните задание Broker Load:

    LOAD LABEL test_db.table4
    (
    data infile("hdfs://<hdfs_host>:<hdfs_port>/example4.csv")
    into table table4
    format as "csv"
    (id, name)
    )
    WITH BROKER
    PROPERTIES
    (
    "partial_update" = "true"
    );

    ПРИМЕЧАНИЕ

    Если вы выбираете Broker Load, вы должны установить параметр partial_update в true, чтобы включить функцию частичного обновления. По умолчанию используются частичные обновления в строковом режиме. Если вам нужно выполнить частичные обновления в столбцовом режиме, вам нужно установить partial_update_mode в column. Кроме того, вы должны использовать параметр column_list для указания столбцов, которые вы хотите обновить.

  • Выполните задание Routine Load:

    CREATE ROUTINE LOAD test_db.table4 on table4
    COLUMNS (id, name),
    COLUMNS TERMINATED BY ','
    PROPERTIES
    (
    "partial_update" = "true"
    )
    FROM KAFKA
    (
    "kafka_broker_list" ="<kafka_broker_host>:<kafka_broker_port>",
    "kafka_topic" = "test4",
    "property.kafka_default_offsets" ="OFFSET_BEGINNING"
    );

    ПРИМЕЧАНИЕ

    • Если вы выбираете Routine Load, вы должны установить параметр partial_update в true, чтобы включить функцию частичного обновления. Кроме того, вы должны использовать параметр COLUMNS для указания столбцов, которые вы хотите обновить.
    • Routine Load поддерживает только частичные обновления в строковых режимах и не поддерживает частичные обновления в столбцовом режиме.

Запрос данных

После завершения загрузки запросите данные table4, чтобы убедиться, что загрузка прошла успешно:

SELECT * FROM table4;
+------+-------+-------+
| id | name | score |
+------+-------+-------+
| 102 | Rose | 0 |
| 101 | Lily | 80 |
| 103 | Alice | 0 |
+------+-------+-------+
3 rows in set (0.01 sec)

Как показано в предыдущем результате запроса, запись с id равным 101 из example4.csv была обновлена в table4, а записи с id равными 102 и 103 из example4.csv были вставлены в table4.

Условные обновления

Начиная с версии 1.5.0 Selena, таблицы с первичным ключом поддерживают условные обновления. Вы можете указать столбец, не являющийся первичным ключом, в качестве условия для определения того, могут ли обновления вступить в силу. Таким образом, обновление из исходной записи в целевую запись вступает в силу только тогда, когда исходная запись данных имеет большее или равное значение, чем целевая запись данных в указанном столбце.

Функция условного обновления предназначена для решения проблемы беспорядка данных. Если исходные данные неупорядочены, вы можете использовать эту функцию, чтобы гарантировать, что новые данные не будут перезаписаны старыми данными.

УВЕДОМЛЕНИЕ

  • Вы не можете указать разные столбцы в качестве условий обновления для одного и того же пакета данных.
  • Операции DELETE не поддерживают условные обновления.
  • В версиях ранее v3.1.3 частичные обновления и условные обновления нельзя использовать одновременно. Начиная с версии 1.5.0, Selena поддерживает использование частичных обновлений с условными обновлениями.

Примеры данных

  1. Подготовьте файл данных.

    a. Создайте CSV-файл с именем example5.csv в вашей локальной файловой системе. Файл состоит из трех столбцов, которые представляют ID пользователя, версию и оценку пользователя по порядку.

    101,1,100
    102,3,100

    b. Опубликуйте данные example5.csv в topic5 вашего кластера Kafka.

  2. Подготовьте таблицу Selena.

    a. Создайте таблицу с первичным ключом с именем table5 в вашей базе данных Selena test_db. Таблица состоит из трех столбцов: id, version и score, где id является первичным ключом.

    CREATE TABLE `table5`
    (
    `id` int(11) NOT NULL COMMENT "user ID",
    `version` int NOT NULL COMMENT "version",
    `score` int(11) NOT NULL COMMENT "user score"
    )
    ENGINE=OLAP
    PRIMARY KEY(`id`) DISTRIBUTED BY HASH(`id`);

    ПРИМЕЧАНИЕ

    Начиная с версии 1.5.0, Selena может автоматически устанавливать количество корзин (BUCKETS) при создании таблицы или добавлении раздела. Вам больше не нужно вручную устанавливать количество корзин. Для получения подробной информации см. установка количества корзин.

    b. Вставьте запись в table5.

    INSERT INTO table5 VALUES
    (101, 2, 80),
    (102, 2, 90);

Загрузка данных

Выполните загрузку для обновления записей со значениями id равными 101 и 102 соответственно из example5.csv в table5 и укажите, что обновления вступают в силу только тогда, когда значение version в каждой из двух записей больше или равно их текущим значениям version.

  • Выполните задание Stream Load:

    curl --location-trusted -u <username>:<password> \
    -H "Expect:100-continue" \
    -H "label:label10" \
    -H "column_separator:," \
    -H "merge_condition:version" \
    -T example5.csv -XPUT \
    http://<fe_host>:<fe_http_port>/api/test_db/table5/_stream_load
  • Выполните задание Routine Load:

    CREATE ROUTINE LOAD test_db.table5 on table5
    COLUMNS (id, version, score),
    COLUMNS TERMINATED BY ','
    PROPERTIES
    (
    "merge_condition" = "version"
    )
    FROM KAFKA
    (
    "kafka_broker_list" ="<kafka_broker_host>:<kafka_broker_port>",
    "kafka_topic" = "topic5",
    "property.kafka_default_offsets" ="OFFSET_BEGINNING"
    );
  • Выполните задание Broker Load:

    LOAD LABEL test_db.table5
    ( DATA INFILE ("s3://xxx.csv")
    INTO TABLE table5 COLUMNS TERMINATED BY "," FORMAT AS "CSV"
    )
    WITH BROKER
    PROPERTIES
    (
    "merge_condition" = "version"
    );

Запрос данных

После завершения загрузки запросите данные table5, чтобы убедиться, что загрузка прошла успешно:

SELECT * FROM table5;
+------+------+-------+
| id | version | score |
+------+------+-------+
| 101 | 2 | 80 |
| 102 | 3 | 100 |
+------+------+-------+
2 rows in set (0.02 sec)

Как показано в предыдущем результате запроса, запись с id равным 101 из example5.csv не была обновлена в table5, а запись с id равным 102 из example5.csv была вставлена в table5.