Knex: Impossible de créer plusieurs PK (mixin .increments avec .primary)

Créé le 18 juil. 2014  ·  31Commentaires  ·  Source: knex/knex

Je souhaite créer une table basée sur le vidage MySQL suivant:

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;

Mon knex ressemble à:

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

Cependant, il y a un problème avec les clés primaires. J'obtiens l'erreur suivante:

Error: Multiple primary key defined

Je suppose que la méthode increments crée déjà PK et la méthode primary crée une autre qui provoque une erreur.

Est-il possible de réaliser ce format de tableau?

bug

Commentaire le plus utile

Ou vous pouvez créer une colonne d'auto-incrémentation avec

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

(uniquement pour Postgresql)

Mais je pense que c'est beaucoup mieux table.increments() ne créera le PK que si je le spécifie manuellement avec table.increments().primary()

Tous les 31 commentaires

Non, mais ça devrait probablement l'être. Je vais chercher quelque chose pour ça.

Selon le 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';

Ainsi, .increments() crée PK. N'est-ce pas un comportement trop fort? Je pense que ce sera mieux s'il créera AUTO_INCREMENT colonne

int unsigned not null auto_increment

Et pour créer PK, nous devons appeler .primary() :

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

Cela permettra de créer de nombreux PK avec la colonne AUTO_INCREMENT .

En tant que workaroud, j'utilise en ce moment:

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

Et dans then callback:

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

J'ai un problème similaire - je veux utiliser .increments() sans que ce soit une clé primaire. Dans mon cas, c'est pour des raisons de performance. Je veux créer la table sans index (ce qui signifie pas de clé primaire), y insérer de nombreuses lignes, puis ajouter un index sur la colonne d'auto-incrémentation. C'est plus performant, et en fait recommandé par la documentation Postgres (voir http://www.postgresql.org/docs/9.4/static/populate.html#POPULATE-RM-INDEXES), mais semble actuellement impossible à faire avec knex.

Je semble également rencontrer un problème similaire. Je ne parviens pas à créer une table qui a:

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

Même problème ici .increments () essaie de définir une clé primaire, même si j'en spécifie une manuellement

Vous pouvez utiliser une requête brute pour ajouter des colonnes incrémentées sans sa clé primaire:

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

Ou vous pouvez créer une colonne d'auto-incrémentation avec

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

(uniquement pour Postgresql)

Mais je pense que c'est beaucoup mieux table.increments() ne créera le PK que si je le spécifie manuellement avec table.increments().primary()

Pourquoi ne pas partir
increments: 'serial'
au lieu de
increments: 'serial primary key'

Quel est le problème avec ça?

Je reçois toujours la même erreur.
IMHO .increments ne devrait pas créer PK.

Des nouvelles à ce sujet?

D'accord - bien qu'il soit certainement possible de contourner ce problème de différentes manières, il est plutôt étrange que l'API n'ait pas de support de premier ordre pour créer une colonne auto-incrémentée sans en faire une clé primaire.

Cela dit, j'imagine que cela n'a pas encore été corrigé, car corriger cela de la manière la plus sensée serait un changement radical, et il faudrait donc que cela fasse partie d'un changement majeur de version.

@zacronos
Le fait est que si nous utilisons .primary() sur une colonne lors de la création d'une table, le résultat serait une requête distincte pour créer la table, et une autre requête alter table pour définir les clés primaires.
Je pense que dans MySQL (ou peut-être dans d'autres SGBD aussi), vous ne pouvez pas avoir un champ auto_incremement sans être PK.
Ainsi, knex crée la table avec la clé primaire, et il ne peut pas seulement alter pour définir deux clés primaires depuis la table a déjà un.
Si nous pouvions résoudre ce problème, peut-être que .increments créera toujours PK mais autorisera également d'autres colonnes à être PK en une seule requête create table , donc nous n'aurions probablement pas besoin d'une modification majeure de la version.

Ou nous pouvons ajouter un autre type de colonne pour cela et laisser .increments tel quel pour ne pas rompre la compatibilité descendante.

@ amir-s: cela explique pourquoi knex se comporte comme il le fait - il tente apparemment d'avoir un comportement équivalent dans les différents SGBD pris en charge. Je n'aime pas particulièrement ça, mais au moins ça ne semble pas bizarre maintenant.

Le problème avec votre suggestion est que, par définition, vous ne pouvez pas avoir plusieurs clés primaires sur une table. (Vous pouvez avoir 1 PK composite sur plusieurs colonnes, mais c'est très différent d'avoir 2 PK à une seule colonne.)

Donc, si .increments crée un PK, alors il est impossible d'autoriser d'autres colonnes à être également marquées comme PK. Il serait possible de créer une option qui peut être passée à .increments pour désactiver le comportement de la clé primaire sur Postgres et tout autre SGBD prenant en charge ce comportement. C'est peut-être la meilleure solution, car elle ne rompt pas la compatibilité descendante et ne rompt pas non plus la compatibilité entre les SGBD. Je n'aime toujours pas cela ni changer le comportement par défaut sur Postgres pour que .increments n'en fasse pas aussi un PK, mais ce serait un PR plus facilement accepté.

Si quelqu'un veut soumettre un PR dans ce sens, je plaiderais en sa faveur. :-)

@zacronos Désolé, par "plusieurs PK", je voulais dire "un PK sur plusieurs colonnes".

En accord avec tous les commentaires ci-dessus. Ce comportement était inattendu lors de la tentative de création d'une clé primaire composite ainsi que d'une colonne à incrémentation automatique sans clé. Ce serait formidable de voir cela mis en œuvre.

Je rencontre également ce problème, mais dans le cas de l'utilisation de Postgres et de l'utilisation de champs série comme clés étrangères - l'implémentation knex d'un champ série Postgres est increments() et cela en fait des clés primaires par défaut. Je veux être capable de construire sqlite3 ainsi que postgres, donc utiliser une requête knex brute me limiterait à l'un ou l'autre. Des suggestions de contournement?

Ce fil n'a pas attiré beaucoup d'attention depuis un moment. Je voudrais réitérer ce que je pense être une solution idéale et rétrocompatible (qui peut donc faire partie de toute version mineure): ajouter une nouvelle option à .increments() , peut-être primaryKey: false , qui (pour les SGBD où cela est possible) désactive la partie clé primaire de sa fonctionnalité.

Ceci est entièrement rétrocompatible puisque .increments() conserve son comportement par défaut.

@zacronos J'ai parcouru rapidement le code. Ça a l'air trivial. Je vais lancer un PR
https://github.com/DeedMob/knex/tree/master/src

Même problème ici. Vous devez utiliser une requête brute pour éviter bigIncrements() d'essayer de créer une autre clé primaire.

Solution de contournement:

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

Si je comprends correctement l'enquête précédente sur ce sujet, alors knex force une clé primaire strictement parce que certaines bases de données (c'est-à-dire MySQL) ne peuvent pas créer de séries sans que ce soit une clé primaire? Si tel est le cas, je ne m'inquiéterais pas trop de la rétrocompatibilité en ce qui concerne Postgres.

Un utilisateur MySQL s'attendrait alors à ce que ce soit un primaire.
Un utilisateur Postgres ne le ferait pas. _ (Ce qui est évident ci-dessus) _

Pour cette raison, je pense qu'il est assez sûr de faire un ajustement dans Postgres pour ne pas forcer la clé primaire et de l'ajouter au journal des modifications. Pas comme un patch, mais pour la prochaine version majeure.

Pour cette raison, je pense qu'il est assez sûr de faire un ajustement dans Postgres pour ne pas forcer la clé primaire et l'ajouter au journal des modifications. Pas comme un patch, mais pour la prochaine version majeure.

Je connais des dizaines de personnes qui ont toujours écrit leurs migrations juste en écrivant seulement table.bigincrements('id') sans .primary() également pour postgresql, car cela a toujours fonctionné comme ça.

Je pensais qu'il y avait une option supplémentaire, qui devrait être utilisée pour dire que la clé primaire ne devrait pas être créée, mais semble ne pas être là. Au moins, je n'ai rien trouvé pour les implémentations postgres / mysql concernant le saut de la création de clé primaire.

Ce serait un changement majeur de rupture de compatibilité ascendante sur les migrations que je détesterais voir cela se produire. Si nous voulons continuer à patcher cette ancienne API de migration, nous devons d'abord ajouter la prise en charge de la gestion des versions pour empêcher tout le monde de réparer leurs anciennes migrations sur chaque version de knex (ces changements dans le code de migration sont vraiment difficiles à vérifier. tests automatiques donc c'est une partie très vulnérable dans le code des utilisateurs de knex).

Toute solution?

des nouvelles à ce sujet?

Ce fil n'a pas attiré beaucoup d'attention depuis un moment. Je voudrais réitérer ce que je pense être une solution idéale et rétrocompatible (qui peut donc faire partie de toute version mineure): ajouter une nouvelle option à .increments() , peut-être primaryKey: false , qui (pour les SGBD où cela est possible) désactive la partie clé primaire de sa fonctionnalité.

Ceci est entièrement rétrocompatible puisque .increments() conserve son comportement par défaut.

Pouvons-nous utiliser des incréments comme celui-ci? toute mise à jour?

Comment ça se passe ? c'est vraiment pénible, surtout dans MSSQL, la contrainte PK n'est pas nommée par défaut mais comme "PK__requests__DD771E3CC0CD0A5E" donc je ne peux même pas la laisser tomber et créer PK autre part.
J'ai essayé de spécifier le nom de la contrainte mais cela ne fonctionne pas pour les incréments ()

Toujours rien là-dessus?

J'aimerais beaucoup le paramètre facultatif dans .increments aussi! Les requêtes brutes pourraient évidemment fonctionner, mais c'est ennuyeux pour ceux qui exécutent plusieurs bases de données différentes (par exemple, mariadb pour la production, sqlite pour les tests). Des mises à jour à ce sujet? Il semble y avoir un PR déjà fait , mais il est devenu un peu silencieux là-bas?

J'appuie à nouveau cela, j'avais déjà besoin de 3 fois dans un seul projet.

Même problème ici, avoir un paramètre supplémentaire pour désactiver les coutures PK raisonnable.

Similaire à # 2896, vous pouvez utiliser pour MySQL:

t.primary(["cmdId", "deviceId"]);
t.specificType("cmdId", "int(10) unsigned AUTO_INCREMENT").notNullable();
t.string("deviceId", 16).notNullable().references("second_table.deviceId");
Cette page vous a été utile?
0 / 5 - 0 notes