Пакет · cdo

Вставка записей

Как вставить одну строку, эффективно вставить тысячи и прочитать id, который сгенерировала база — плюс два правила, определяющие, что именно записывается.

Вставка одной строки

insert() принимает имя таблицы и массив (или объект) пар «колонка → значение». Он возвращает сгенерированный первичный ключ.

php
$id = $cdo->insert('users', [
  'name'  => 'Alice',
  'email' => 'alice@example.com',
]);

Возвращаемый id — это значение первого ключа переданной сущности, полученное через RETURNING на PostgreSQL/MariaDB или lastInsertId() на MySQL, SQLite или Oracle. См. Определение драйвера — почему существуют оба пути.

Первичный ключ — это первый ключ

insert() выводит возвращаемую колонку из array_key_first() вашей сущности — до отбрасывания NULL. Ставьте колонку первичного ключа первой (даже как 'id' => null), чтобы вернулось правильное значение. Колонка авто-id со значением null/отсутствующая всё равно вернёт сгенерированный id через драйвер.

Два правила о том, что записывается

  1. Значения null выбрасываются. Любая колонка со значением null удаляется из INSERT, поэтому она принимает значение по умолчанию из базы или автогенерируемое.
  2. Объекты «разворачиваются». Сущность-объект преобразуется через get_object_vars(), поэтому её публичные свойства становятся колонками.
php
$user = new stdClass();
$user->name  = 'Bob';
$user->email = 'bob@example.com';
$user->note  = null;   // выброшено — колонка сохраняет значение по умолчанию

$id = $cdo->insert('users', $user);

Пакетная вставка

insertBatch() записывает много строк с гораздо меньшим числом обращений к базе. Он автоматически разбивает вход на чанки и выдаёт один многострочный INSERT на каждый чанк.

php
$users = [
  ['name' => 'Alice', 'email' => 'alice@example.com'],
  ['name' => 'Bob',   'email' => 'bob@example.com'],
  // ... тысячи ещё
];

$inserted = $cdo->insertBatch('users', $users);              // размер чанка по умолчанию 1000
$inserted = $cdo->insertBatch('users', $users, chunkSize: 500);

insertBatch() возвращает int — общее число вставленных строк. Это массовая запись, а не получение id. Пустой вход — это no-op (ничего не делает) и возвращает 0.

Вторым аргументом принимается любой iterable, поэтому держать строки в массиве необязательно. Генератор отдаёт их по одной, и в памяти живёт только текущий чанк — так грузят файлы, которые целиком в память не помещаются:

php
$inserted = $cdo->insertBatch('users', (function () use ($csv) {
  while (($line = fgetcsv($csv)) !== false) {
      yield ['name' => $line[0], 'email' => $line[1]];
  }
})());

Подробности — в Пакетах и чанках.

Строки группируются по форме

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

Выбор размера чанка

chunkSize ограничивает, сколько строк попадает в один запрос, что удерживает вас под лимитами базы на число связанных параметров и размер пакета. Меньшие чанки используют меньше памяти на запрос; большие означают меньше обращений к базе. Значение по умолчанию 1000 — безопасная отправная точка; уменьшайте его, если строки очень широкие (много колонок).

Обработка дубликатов ключей

Обычный insert в строку, нарушающую уникальное ограничение, бросает CDOException. Если вам нужно вставить-или-обновить (или вставить-или-игнорировать), используйте upsert / upsertBatch.

Связанное