Knex: Es können nicht mehrere PKs erstellt werden (Inkremente mit .primary mischen)

Erstellt am 18. Juli 2014  ·  31Kommentare  ·  Quelle: knex/knex

Ich möchte eine Tabelle erstellen, die auf dem folgenden MySQL-Dump basiert:

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;

Mein Knex sieht aus wie:

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

Es gibt jedoch ein Problem mit Primärschlüsseln. Ich erhalte folgenden Fehler:

Error: Multiple primary key defined

Ich gehe davon aus, dass die increments -Methode bereits PK erstellt und die primary -Methode eine weitere Methode erstellt, die einen Fehler verursacht.

Ist es möglich, dieses Tabellenformat zu erreichen?

bug

Hilfreichster Kommentar

Oder Sie können eine Spalte zum automatischen Inkrementieren mit erstellen

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

(nur für Postgresql)

Aber ich denke, es ist viel besser, wenn table.increments() die PK nur erstellt, wenn ich dies manuell mit table.increments().primary() spezifiziere

Alle 31 Kommentare

Nein, aber es sollte wahrscheinlich sein. Ich werde etwas dafür untersuchen.

Laut 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() erstellt also PK. Ist es nicht zu starkes Verhalten? Ich denke, es wird besser sein, wenn es eine AUTO_INCREMENT -Spalte ohne PK erstellt, also:

int unsigned not null auto_increment

Und um PK zu erstellen, müssen wir .primary() aufrufen:

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

Es wird möglich sein, viele PKs mit der Spalte AUTO_INCREMENT zu erstellen.

Als Workaroud benutze ich gerade:

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

Und in then Rückruf:

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

Ich habe ein ähnliches Problem - ich möchte .increments() ohne dass es sich um einen Primärschlüssel handelt. In meinem Fall aus Leistungsgründen. Ich möchte die Tabelle ohne Indizes erstellen (dh ohne Primärschlüssel), viele Zeilen einfügen und dann einen Index für die Autoincrement-Spalte hinzufügen. Dies ist leistungsfähiger und wird in der Postgres-Dokumentation empfohlen (siehe http://www.postgresql.org/docs/9.4/static/populate.html#POPULATE-RM-INDEXES), scheint jedoch derzeit unmöglich zu sein knex.

Ich scheine auch auf ein ähnliches Problem zu stoßen. Ich kann keine Tabelle erstellen, die Folgendes enthält:

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

Das gleiche Problem hier .increments () versucht, einen Primärschlüssel zu definieren, auch wenn ich einen manuell spezifiziere

Sie können eine Rohabfrage verwenden, um Inkrementspalten ohne ihren Primärschlüssel hinzuzufügen:

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

Oder Sie können eine Spalte zum automatischen Inkrementieren mit erstellen

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

(nur für Postgresql)

Aber ich denke, es ist viel besser, wenn table.increments() die PK nur erstellt, wenn ich dies manuell mit table.increments().primary() spezifiziere

Warum nicht gehen?
increments: 'serial'
Anstatt von
increments: 'serial primary key'

Was ist das Problem damit?

Ich bekomme immer noch den gleichen Fehler.
IMHO .increments sollte keine PK erstellen.

Gibt es Neuigkeiten zu diesem Thema?

Einverstanden - Obwohl es sicherlich möglich ist, dieses Problem auf verschiedene Weise zu umgehen, ist es ziemlich bizarr, dass die API keine erstklassige Unterstützung für das Erstellen einer Spalte mit automatischer Inkrementierung bietet, ohne sie zu einem Primärschlüssel zu machen.

Trotzdem stelle ich mir vor, dass dies noch nicht behoben wurde, da eine sinnvolle Korrektur eine bahnbrechende Änderung wäre und daher Teil einer größeren Version sein müsste.

@ Zacronos
Die Sache ist, wenn wir beim Erstellen einer Tabelle .primary() für eine Spalte verwenden, wäre das Ergebnis eine separate Abfrage zum Erstellen der Tabelle und eine weitere alter table Abfrage zum Definieren der Primärschlüssel.
Ich denke, in MySQL (oder vielleicht auch in anderen DBMS) kann man kein auto_incremement -Feld haben, ohne PK zu sein.
Knex erstellt also die Tabelle mit dem Primärschlüssel, und dann kann es nicht nur alter , um zwei Primärschlüssel zu definieren, da die Tabelle bereits einen hat.
Wenn wir dies beheben könnten, würde .increments möglicherweise immer noch PK erstellen, aber andere Spalten können ebenfalls PK sein, alles in einer create table -Abfrage, sodass wir wahrscheinlich keinen größeren Versionsschub benötigen würden.

Oder wir können einen anderen Spaltentyp hinzufügen und .increments belassen, um die Abwärtskompatibilität nicht zu beeinträchtigen.

@ amir-s: Nun, das erklärt, warum sich knex so verhält - es wird anscheinend versucht, ein Verhalten zu erreichen, das für die verschiedenen unterstützten DBMS gleich ist. Ich mag das nicht besonders, aber zumindest scheint es jetzt nicht bizarr.

Das Problem mit Ihrem Vorschlag ist, dass Sie per Definition nicht mehrere Primärschlüssel in einer Tabelle haben können. (Sie können 1 zusammengesetzte PK in mehreren Spalten haben, dies unterscheidet sich jedoch stark von 2 einspaltigen PKs.)

Wenn also .increments eine PK erstellt, können andere Spalten nicht als PK markiert werden. Es wäre möglich, eine Option zu erstellen, die an .increments , um das Primärschlüsselverhalten in Postgres und jedem anderen DBMS zu deaktivieren, das dieses Verhalten unterstützt. Vielleicht ist dies die beste Lösung, da dadurch die Abwärtskompatibilität und die DBMS-übergreifende Kompatibilität nicht beeinträchtigt werden. Ich mag das immer noch nicht und ändere das Standardverhalten auf Postgres so, dass .increments es nicht auch zu einer PK macht, aber es wäre eine leichter akzeptierte PR.

Wenn jemand eine PR in dieser Richtung einreichen möchte, würde ich dafür sprechen. :-)

@zacronos Sorry, mit "mehrere PKs" meinte ich "eine PK auf mehreren Spalten".

In Übereinstimmung mit allen obigen Kommentaren. Dieses Verhalten war unerwartet, als versucht wurde, einen zusammengesetzten Primärschlüssel sowie eine automatisch inkrementierende Spalte ohne Schlüssel zu erstellen. Es wäre toll, wenn dies umgesetzt würde.

Ich stoße auch auf dieses Problem, aber im Fall der Verwendung von Postgres und der Verwendung von seriellen Feldern als Fremdschlüssel beträgt die Knex-Implementierung eines seriellen increments() Postgres -Felds increments() und dies macht sie standardmäßig zu Primärschlüsseln. Ich möchte in der Lage sein, sowohl sqlite3 als auch postgres zu erstellen. Die Verwendung einer rohen knex-Abfrage würde mich also auf entweder oder beschränken. Vorschläge zur Problemumgehung?

Dieser Thread hat seit einiger Zeit nicht mehr viel Aufmerksamkeit erhalten. Ich möchte noch einmal wiederholen, was meiner Meinung nach eine ideale, abwärtskompatible Lösung ist (die daher Teil jeder Nebenversion sein kann): Fügen Sie .increments() eine neue Option hinzu, vielleicht primaryKey: false (für DBMS, wo dies möglich ist) Deaktiviert den Primärschlüsselteil seiner Funktionalität.

Dies ist vollständig abwärtskompatibel, da .increments() sein Standardverhalten beibehält.

@zacronos Habe einen kurzen Blick durch den Code
https://github.com/DeedMob/knex/tree/master/src

Gleiches Problem hier. Sie müssen eine Rohabfrage verwenden, um bigIncrements() zu vermeiden und zu versuchen, einen anderen Primärschlüssel zu erstellen.

Problemumgehung:

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

Wenn ich die vorherige Untersuchung zu diesem Thema richtig verstehe, erzwingt knex einen Primärschlüssel, weil einige DBs (dh MySQL) keine Serien erstellen können, ohne dass es sich um einen Primärschlüssel handelt? Wenn dies der Fall ist, würde ich mir keine allzu großen Sorgen um die Abwärtskompatibilität machen, wenn es um Postgres geht.

Ein MySQL-Benutzer würde dann erwarten, dass es sich um eine primäre handelt.
Ein Postgres-Benutzer würde dies nicht tun. _ (Was oben ersichtlich ist) _

Aus diesem Grund denke ich, dass es ziemlich sicher ist, in Postgres eine Anpassung vorzunehmen, um den Primärschlüssel nicht zu erzwingen und ihn dem Änderungsprotokoll hinzuzufügen. Nicht als Patch, sondern für die nächste Hauptversion.

Aus diesem Grund halte ich es für ziemlich sicher, eine Anpassung in Postgres vorzunehmen, um den Primärschlüssel nicht zu erzwingen, und ihn dem Änderungsprotokoll hinzuzufügen. Nicht als Patch, sondern für die nächste Hauptversion.

Ich kenne Dutzende von Leuten, die ihre Migrationen immer nur geschrieben haben, indem sie nur table.bigincrements('id') ohne .primary() auch für postgresql, weil es immer so funktioniert hat.

Ich dachte, es gab eine zusätzliche Option, mit der angegeben werden sollte, dass der Primärschlüssel nicht erstellt werden sollte, aber anscheinend nicht vorhanden ist. Zumindest habe ich für Postgres / MySQL-Implementierungen nichts zum Überspringen der Primärschlüsselerstellung gefunden.

Dies wäre eine wichtige Änderung der Abwärtskompatibilität bei Migrationen, die ich nicht gerne sehen würde. Wenn wir diese alte Migrations-API weiterhin patchen möchten, müssen wir zuerst die Versionsunterstützung hinzufügen, um zu verhindern, dass alle Benutzer ihre alten Migrationen auf jeder knex-Version korrigieren (diese Änderungen im Migrationscode sind wirklich schwer zu überprüfen, ob sie genau gleich funktionieren automatische Tests, so dass es ein sehr anfälliger Teil des Benutzercodes von knex ist).

Irgendeine Lösung?

Gibt es Neuigkeiten zu diesem Thema?

Dieser Thread hat seit einiger Zeit nicht mehr viel Aufmerksamkeit erhalten. Ich möchte noch einmal wiederholen, was meiner Meinung nach eine ideale, abwärtskompatible Lösung ist (die daher Teil jeder Nebenversion sein kann): Fügen Sie .increments() eine neue Option hinzu, vielleicht primaryKey: false (für DBMS, wo dies möglich ist) Deaktiviert den Primärschlüsselteil seiner Funktionalität.

Dies ist vollständig abwärtskompatibel, da .increments() sein Standardverhalten beibehält.

Können wir solche Inkremente verwenden? irgendein Update?

Wie geht das Es ist wirklich ein Schmerz, besonders in MSSQL. Die PK-Einschränkung wird nicht standardmäßig benannt, sondern wie "PK__requests__DD771E3CC0CD0A5E", sodass ich sie nicht einmal löschen und PK anderswo erstellen kann.
Ich habe versucht, den Namen für die Einschränkung anzugeben, aber es funktioniert nicht für Inkremente ()

Immer noch nichts dazu?

Möchte auch den optionalen Parameter in .increments sehr! Rohe Abfragen könnten natürlich funktionieren, aber es ist ärgerlich für diejenigen, die mehrere verschiedene DBs ausführen (z. B. Mariadb für die Produktion, SQLite zum Testen). Irgendwelche Updates dazu? Es scheint bereits eine

Ich stimme dem noch einmal zu, ich brauchte das 3 Mal in einem einzelnen Projekt.

Gleiches Problem hier, mit einem zusätzlichen Parameter zum Deaktivieren von PK-Nähten sinnvoll.

Ähnlich wie bei # 2896 können Sie für MySQL verwenden:

t.primary(["cmdId", "deviceId"]);
t.specificType("cmdId", "int(10) unsigned AUTO_INCREMENT").notNullable();
t.string("deviceId", 16).notNullable().references("second_table.deviceId");
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

mattgrande picture mattgrande  ·  3Kommentare

aj0strow picture aj0strow  ·  3Kommentare

fsebbah picture fsebbah  ·  3Kommentare

tjwebb picture tjwebb  ·  3Kommentare

nklhrstv picture nklhrstv  ·  3Kommentare