Knex: No se pueden crear múltiples PK (mezclando .increments con .primary)

Creado en 18 jul. 2014  ·  31Comentarios  ·  Fuente: knex/knex

Quiero crear una tabla basada en el siguiente volcado de 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;

Mi knex se ve así:

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');
});

Sin embargo, existe un problema con las claves primarias. Me sale el siguiente error:

Error: Multiple primary key defined

Supongo que el método increments ya crea PK y el método primary crea otro que causa un error.

¿Es posible lograr este formato de tabla?

bug

Comentario más útil

O puede crear la columna de autoincremento con

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

(solo para Postgresql)

Pero creo que es mucho mejor table.increments() creará el PK solo si lo especifico manualmente con table.increments().primary()

Todos 31 comentarios

No, pero probablemente debería serlo. Buscaré algo para esto.

Según 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';

Entonces, .increments() crea PK. ¿No es un comportamiento demasiado fuerte? Creo que será mejor si crea una columna AUTO_INCREMENT sin PK, entonces:

int unsigned not null auto_increment

Y para crear PK, tenemos que llamar a .primary() :

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

Permitirá crear muchos PK con la columna AUTO_INCREMENT .

Como workaroud que uso ahora mismo:

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

Y en then devolución

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

Tengo un problema similar: quiero usar .increments() sin que sea una clave principal. En mi caso es por motivos de rendimiento. Quiero crear la tabla sin índices (lo que significa que no hay clave principal), insertar muchas filas en ella y luego agregar un índice en la columna de autoincremento. Esto es más eficaz y, de hecho, lo recomienda la documentación de Postgres (consulte http://www.postgresql.org/docs/9.4/static/populate.html#POPULATE-RM-INDEXES), pero actualmente parece imposible de hacer con knex.

También parece que me encuentro con un problema similar. No puedo crear una tabla que tenga:

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");
});

El mismo problema aquí .increments () intenta definir una clave principal, incluso si especifico una manualmente

Puede usar una consulta sin procesar para agregar columnas de incrementos sin su clave principal:

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');
    }),
  ]);
};

O puede crear la columna de autoincremento con

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

(solo para Postgresql)

Pero creo que es mucho mejor table.increments() creará el PK solo si lo especifico manualmente con table.increments().primary()

Porque no te vas
increments: 'serial'
en vez de
increments: 'serial primary key'

Cual es el problema con eso?

Sigo recibiendo el mismo error.
En mi humilde opinión, .increments no debería crear PK.

¿Alguna novedad sobre este asunto?

De acuerdo, aunque ciertamente es posible solucionar este problema de varias maneras, es bastante extraño que la API no tenga un soporte de primera clase para crear una columna de incremento automático sin convertirla en una clave principal.

Dicho esto, imagino que esto aún no se ha solucionado porque corregirlo de la manera más sensata sería un cambio importante, por lo que tendría que ser parte de un aumento de la versión principal.

@zacronos
El caso es que si usamos .primary() en una columna al crear una tabla, el resultado sería una consulta separada para crear la tabla y otra consulta alter table para definir las claves primarias.
Creo que en MySQL (o quizás también en otros DBMS), no puedes tener un campo auto_incremement sin ser PK.
Entonces, knex crea la tabla con la clave primaria, y luego no puede simplemente alter definir dos claves primarias ya que la tabla ya tiene una.
Si pudiéramos solucionar esto, tal vez .increments aún cree PK pero permita que otras columnas también sean PK en una consulta create table , por lo que probablemente no necesitaríamos un aumento de versión principal.

O podemos agregar otro tipo de columna para esto y dejar .increments como está para no romper la compatibilidad con versiones anteriores.

@ amir-s: bueno, eso explica por qué knex se comporta como lo hace; aparentemente, está intentando tener un comportamiento equivalente en los diferentes DBMS compatibles. Eso no me gusta particularmente, pero al menos ahora no parece extraño.

El problema con su sugerencia es que, por definición, no puede tener varias claves primarias en una mesa. (Puede tener 1 PK compuesto en varias columnas, pero eso es muy diferente de tener 2 PK de una sola columna).

Entonces, si .increments crea un PK, entonces es imposible permitir que otras columnas también se marquen como PK. Sería posible crear una opción que se pueda pasar a .increments para deshabilitar el comportamiento de la clave principal en Postgres y cualquier otro DBMS que admita dicho comportamiento. Quizás esa sea la mejor solución, ya que no rompe la compatibilidad con versiones anteriores y tampoco rompe la compatibilidad entre DBMS. Todavía no me gusta eso, además de cambiar el comportamiento predeterminado en Postgres para que .increments no lo convierta en un PK, pero sería un PR más fácilmente aceptado.

Si alguien quiere presentar un PR en ese sentido, yo argumentaría a su favor. :-)

@zacronos Lo siento, por "múltiples PK" me refiero a "un PK en múltiples columnas".

De acuerdo con todos los comentarios anteriores. Este comportamiento fue inesperado al intentar crear una clave primaria compuesta, así como una columna de incremento automático sin clave. Sería genial ver esto implementado.

También me encuentro con este problema, pero en el caso de usar Postgres y usar campos de serie como claves externas, la implementación de knex de un campo de serie Postgres es increments() y esto las convierte en claves primarias por defecto. Quiero poder construir sqlite3 y postgres, por lo que usar una consulta knex sin procesar me limitaría a o. ¿Alguna sugerencia de solución?

Este hilo no ha recibido mucha atención en un tiempo. Me gustaría reiterar lo que creo que es una solución ideal, compatible con versiones anteriores (que, por lo tanto, puede ser parte de cualquier versión menor): agregue una nueva opción a .increments() , tal vez primaryKey: false , que (para DBMS donde esto sea posible) deshabilita la parte de clave primaria de su funcionalidad.

Esto es totalmente compatible con versiones anteriores ya que .increments() conserva su comportamiento predeterminado.

@zacronos Echó un vistazo rápido al código. Parece trivial. Voy a armar un PR
https://github.com/DeedMob/knex/tree/master/src

El mismo problema aquí. Debe usar una consulta sin procesar para evitar que bigIncrements() intente crear otra clave principal.

Solución alterna:

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

Si entiendo correctamente la investigación anterior sobre este tema, entonces presumiblemente knex fuerza una clave primaria estrictamente porque algunas bases de datos (es decir, MySQL) no pueden crear publicaciones seriadas sin que sea una clave primaria. Si ese es el caso, no me preocuparía demasiado por la compatibilidad con versiones anteriores cuando se trata de Postgres.

Un usuario de MySQL esperaría entonces que fuera un primario.
Un usuario de Postgres no lo haría. _ (Que es evidente arriba) _

Por esta razón, creo que es bastante seguro hacer un ajuste en Postgres para _no_ forzar la clave principal y agregarla al registro de cambios. No como parche, sino para la próxima versión principal.

Por esta razón, creo que es bastante seguro hacer un ajuste en Postgres para no forzar la clave principal y agregarla al registro de cambios. No como parche, sino para la próxima versión principal.

Conozco a decenas de personas que siempre han escrito sus migraciones simplemente escribiendo solo table.bigincrements('id') sin .primary() también para postgresql, porque siempre ha funcionado así.

Pensé que había una opción adicional, que debería usarse para indicar que no se debe crear la clave principal, pero parece que no está allí. Al menos no encontré nada para las implementaciones de postgres / mysql sobre omitir la creación de la clave principal.

Este sería un importante cambio de ruptura de compatibilidad con versiones anteriores en las migraciones que no me gustaría que sucediera. Si queremos seguir parcheando esta antigua API de migración, primero debemos agregar soporte de versiones para evitar que todas las personas arreglen sus antiguas migraciones en cada versión de knex (es realmente difícil verificar que esos cambios en el código de migración funcionan exactamente igual pruebas automáticas por lo que es una parte muy vulnerable en el código de los usuarios de knex).

¿Alguna solución?

¿Alguna noticia sobre esto?

Este hilo no ha recibido mucha atención en un tiempo. Me gustaría reiterar lo que creo que es una solución ideal, compatible con versiones anteriores (que, por lo tanto, puede ser parte de cualquier versión menor): agregue una nueva opción a .increments() , tal vez primaryKey: false , que (para DBMS donde esto sea posible) deshabilita la parte de clave primaria de su funcionalidad.

Esto es totalmente compatible con versiones anteriores ya que .increments() conserva su comportamiento predeterminado.

¿Podemos usar incrementos como este? ¿cualquier actualización?

Como va eso ? es realmente una molestia, especialmente en MSSQL, la restricción de PK no se nombra por defecto, sino como "PK__requests__DD771E3CC0CD0A5E", así que ni siquiera puedo dejarlo y crear PK en otra parte.
He cambiado para especificar el nombre de la restricción, pero no funciona para incrementos ()

¿Aún no tienes nada de eso?

¡También me gustaría mucho el parámetro opcional en .increments! Las consultas sin procesar obviamente podrían funcionar, pero es molesto para aquellos que ejecutan múltiples bases de datos diferentes (por ejemplo, mariadb para producción, sqlite para pruebas). ¿Alguna actualización sobre esto? Parece que ya se hizo un

De nuevo lo secundo, ya necesitaba esa 3 vez en un solo proyecto.

El mismo problema aquí, tener un parámetro adicional para deshabilitar las costuras PK razonable.

Similar al # 2896 que puede usar para MySQL:

t.primary(["cmdId", "deviceId"]);
t.specificType("cmdId", "int(10) unsigned AUTO_INCREMENT").notNullable();
t.string("deviceId", 16).notNullable().references("second_table.deviceId");
¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

saurabhghewari picture saurabhghewari  ·  3Comentarios

hyperh picture hyperh  ·  3Comentarios

marianomerlo picture marianomerlo  ·  3Comentarios

koskimas picture koskimas  ·  3Comentarios

sandrocsimas picture sandrocsimas  ·  3Comentarios