Knex: Невозможно создать несколько PK (приращение миксина с .primary)

Созданный на 18 июл. 2014  ·  31Комментарии  ·  Источник: knex/knex

Я хочу создать таблицу на основе следующего дампа MySQL:

CREATE TABLE `my_table` (
  `cmdId` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `deviceId` char(16) NOT NULL,
  `fnNumber` int(10) unsigned DEFAULT NULL,
  `chNumber` int(10) unsigned DEFAULT NULL,
  `cmd` varchar(50) DEFAULT NULL,
  `cmdDate` datetime DEFAULT NULL,
  `delivered` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`cmdId`,`deviceId`),
  KEY `deviceId` (`deviceId`),
  CONSTRAINT `tblcommands_ibfk_1` FOREIGN KEY (`deviceId`) REFERENCES `second_table` (`deviceId`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Мой кнекс выглядит так:

knex.schema.createTable('my_table', function(t) {
    t.primary(['cmdId', 'deviceId']);
    t.increments('cmdId');
    t.string('deviceId', 16).notNullable().references('second_table.deviceId');
    t.integer('fnNumber').unsigned();
    t.integer('chNumber').unsigned();
    t.string('cmd96', 50);
    t.dateTime('cmdDate');
    t.boolean('delivered');
});

Однако есть проблема с первичными ключами. Я получаю следующую ошибку:

Error: Multiple primary key defined

Я предполагаю, что метод increments уже создает PK, а метод primary создает еще один, который вызывает ошибку.

Можно ли добиться такого формата таблицы?

Самый полезный комментарий

Или вы можете создать столбец с автоинкрементом с помощью

table.specificType('myid','serial');

(только для Postgresql)

Но я думаю, что намного лучше table.increments() создаст PK, только если я вручную укажу это с помощью table.increments().primary()

Все 31 Комментарий

Нет, но, наверное, так и должно быть. Я поищу кое-что для этого.

Согласно https://github.com/tgriesser/knex/blob/master/lib/dialects/mysql/schema/column.js#L30

ColumnCompiler_MySQL.prototype.increments = 'int unsigned not null auto_increment primary key';

Итак, .increments() создает PK. Разве это не слишком сильное поведение? Думаю, будет лучше, если он создаст столбец AUTO_INCREMENT без PK, поэтому:

int unsigned not null auto_increment

И чтобы создать ПК, мы должны вызвать .primary() :

table.increments('id').primary();

Это позволит создать множество ПК со столбцом AUTO_INCREMENT .

В качестве обходного пути сейчас использую:

table.primary(['cmdId', 'deviceId']);
table.integer('cmdId').notNullable();
table.string('deviceId', 16).notNullable().references('second_table.deviceId');

И в then :

knex.schema.raw('ALTER TABLE my_table MODIFY cmdId INT UNSIGNED AUTO_INCREMENT');

У меня аналогичная проблема - я хочу использовать .increments() не являясь первичным ключом. В моем случае это из соображений производительности. Я хочу создать таблицу без индексов (что означает отсутствие первичного ключа), вставить в нее много строк, а затем добавить индекс в столбец автоинкремента. Это более производительно и фактически рекомендуется в документации Postgres (см. Http://www.postgresql.org/docs/9.4/static/populate.html#POPULATE-RM-INDEXES), но в настоящее время кажется невозможным сделать с knex.

Я тоже, кажется, сталкиваюсь с подобной проблемой. Я не могу создать таблицу, в которой есть:

knex.schema.createTable("numeric_table", function (table) {
    table.integer("integer_key").primary;

   // Both increments & bigIncrements tries to create primary key.
    table.increments("increment_key");
    table.bigIncrements("big_increment_key");
});

Та же проблема здесь .increments () пытается определить первичный ключ, даже если я вручную укажу его

Вы можете использовать необработанный запрос для добавления столбцов приращения без его первичного ключа:

exports.up = function (knex) {
  return Promise.all([
    knex.raw('alter table "my_table" add column "my_seq" bigserial'),
  ]);
};

exports.down = (knex) => {
  return Promise.all([
    knex.schema.table('my_table', (t) => {
      t.dropColumn('my_seq');
    }),
  ]);
};

Или вы можете создать столбец с автоинкрементом с помощью

table.specificType('myid','serial');

(только для Postgresql)

Но я думаю, что намного лучше table.increments() создаст PK, только если я вручную укажу это с помощью table.increments().primary()

Почему бы не уйти
increments: 'serial'
вместо того
increments: 'serial primary key'

Что с этим не так?

Я все еще получаю ту же ошибку.
IMHO .increments не должен создавать PK.

Есть новости по этому поводу?

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

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

@zacronos
Дело в том, что если мы используем .primary() в столбцах при создании таблицы, результатом будет отдельный запрос для создания таблицы и еще один запрос alter table для определения первичных ключей.
Я думаю, что в MySQL (или, возможно, в других СУБД) вы не можете иметь поле auto_incremement без PK.
Итак, knex создает таблицу с первичным ключом, а затем он не может просто alter определить два первичных ключа, поскольку в таблице уже есть один.
Если бы мы могли это исправить, возможно, .increments прежнему создает PK, но позволяет другим столбцам также быть PK в одном запросе create table , поэтому нам, вероятно, не понадобится повышение основной версии.

Или мы можем добавить для этого другой тип столбца и оставить .increments как есть, чтобы не нарушать обратную совместимость.

@ amir-s: что ж, это объясняет, почему knex ведет себя именно так - очевидно, он пытается иметь поведение, эквивалентное для различных поддерживаемых СУБД. Мне это не особенно нравится, но, по крайней мере, сейчас это не кажется странным.

Проблема с вашим предложением заключается в том, что по определению у вас не может быть нескольких первичных ключей в таблице. (У вас может быть 1 составной ПК для нескольких столбцов, но это сильно отличается от наличия 2 ПК с одним столбцом.)

Таким образом, если .increments создает PK, тогда невозможно разрешить другим столбцам также помечаться как PK. Можно было бы создать параметр, который можно передать в .increments чтобы отключить поведение первичного ключа в Postgres и любой другой СУБД, поддерживающей такое поведение. Возможно, это лучшее решение, поскольку оно не нарушает обратную совместимость и не нарушает совместимость между СУБД. Мне все еще не нравится это, а также изменение поведения по умолчанию в Postgres, так что .increments также не делает его PK, но это было бы более приемлемым PR.

Если кто-то хочет представить PR в этом направлении, я буду возражать в его пользу. :-)

@zacronos Извините, под «несколькими ПК» я имел в виду «один ПК на нескольких столбцах».

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

Я тоже сталкиваюсь с этой проблемой, но в случае использования Postgres и использования последовательных полей в качестве внешних ключей - реализация knex для последовательного поля Postgres - это increments() и это делает их первичными ключами по умолчанию. Я хочу иметь возможность создавать sqlite3, а также postgres, поэтому использование необработанного запроса knex ограничило бы меня одним или. Есть предложения по обходному пути?

Эта ветка давно не привлекала особого внимания. Я хотел бы повторить то, что я считаю идеальным, обратно совместимым решением (которое, таким образом, может быть частью любого второстепенного выпуска): добавить новую опцию в .increments() , возможно, primaryKey: false , что (для СУБД, где это возможно) отключает основную ключевую часть своей функциональности.

Это полностью обратно совместимо, поскольку .increments() сохраняет поведение по умолчанию.

@zacronos Бегло просмотрел код. Выглядит банально. Собираю PR
https://github.com/DeedMob/knex/tree/master/src

Здесь та же проблема. Придется использовать необработанный запрос, чтобы избежать попытки bigIncrements() создать другой первичный ключ.

Обходной путь:

table.bigincrements();
table.biginteger('other').notNullable();
table.dropPrimary();
table.primary(['id', 'other']);

Если я правильно понимаю предыдущее расследование по этой теме, то при нажатии knex принудительно вводит первичный ключ строго потому, что некоторые БД (например, MySQL) не могут создавать сериалы, не являясь первичным ключом? Если это так, я бы не стал слишком беспокоиться об обратной совместимости, когда дело доходит до Postgres.

Тогда пользователь MySQL будет ожидать, что он будет основным.
Пользователь Postgres этого не сделает. _ (Что очевидно выше) _

По этой причине я думаю, что довольно безопасно внести в Postgres изменения в _not_ force primary key и добавить его в журнал изменений. Не как патч, а для следующей основной версии.

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

Я знаю десятки людей, которые всегда писали свои миграции просто путем написания только table.bigincrements('id') без .primary() также для postgresql, потому что он всегда так работал.

Я, хотя есть дополнительная опция, которая должна использоваться, чтобы сказать, что первичный ключ не должен создаваться, но похоже, что его там нет. По крайней мере, я не нашел ничего для реализации postgres / mysql о пропуске создания первичного ключа.

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

Любое решение?

Есть новости по этому поводу?

Эта ветка давно не привлекала особого внимания. Я хотел бы повторить то, что я считаю идеальным, обратно совместимым решением (которое, таким образом, может быть частью любого второстепенного выпуска): добавить новую опцию в .increments() , возможно, primaryKey: false , что (для СУБД, где это возможно) отключает основную ключевую часть своей функциональности.

Это полностью обратно совместимо, поскольку .increments() сохраняет поведение по умолчанию.

Можем ли мы использовать такие приращения? какое-нибудь обновление?

Как дела? это действительно неприятно, особенно в MSSQL, ограничение PK по умолчанию не называется, а называется «PK__requests__DD771E3CC0CD0A5E», поэтому я даже не могу его отбросить и создать PK где-нибудь еще.
Я правильно указал имя для ограничения, но оно не работает для увеличения ()

По-прежнему ничего по этому поводу?

Очень бы хотелось, чтобы в .increments был необязательный параметр! Необработанные запросы, очевидно, могут работать, но это раздражает тех, кто использует несколько разных БД (например, mariadb для производства, sqlite для тестирования). Есть обновления по этому поводу? Вроде пиар уже сделали , а там молчали?

Я снова повторяю это, мне уже нужно было это 3 раза в одном проекте.

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

Аналогично # 2896 вы можете использовать для MySQL:

t.primary(["cmdId", "deviceId"]);
t.specificType("cmdId", "int(10) unsigned AUTO_INCREMENT").notNullable();
t.string("deviceId", 16).notNullable().references("second_table.deviceId");
Была ли эта страница полезной?
0 / 5 - 0 рейтинги