Knex: 无法创建多个PK(与.primary混合在一起的.increments)

创建于 2014-07-18  ·  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()仅当我手动指定table.increments().primary()时才创建PK

所有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。 这不是太强硬的行为吗? 我认为,如果创建不带PK的AUTO_INCREMENT列会更好,所以:

int unsigned not null auto_increment

要创建PK,我们必须调用.primary()

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

它将允许使用AUTO_INCREMENT列创建许多PK。

作为工作专家,我现在使用:

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.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()仅当我手动指定table.increments().primary()时才创建PK

为什么不离开
increments: 'serial'
代替
increments: 'serial primary key'

有什么问题吗?

我仍然遇到相同的错误。
恕我直言.increments不应创建PK。

关于这个问题有什么消息吗?

同意-尽管当然可以以多种方式解决此问题,但怪异的是,该API没有一流的支持,无法在不将其设为主键的情况下创建自动增量列。

话虽如此,但我想这还没有得到解决,因为以最明智的方式纠正此问题将是一项重大突破,因此,它必须成为主要版本升级的一部分。

@zacronos
问题是如果我们在创建表时在列上使用.primary() ,结果将是创建表的单独查询,以及另一个用于定义主键的alter table查询。
我认为在MySQL(或其他DBMS)中,如果没有PK,就不能有auto_incremement字段。
因此,knex用主键创建表,然后不能仅仅用alter来定义两个主键,因为表已经有一个。
如果我们可以解决此问题,也许.increments仍然可以创建PK,但也可以在一个create table查询中同时允许其他列也为PK,因此我们可能不需要主要版本。

或者,我们可以为此添加另一列类型,并保留.increments不变,以免破坏向后兼容性。

@ amir-s:这很好地解释了为什么knex会如此表现-显然,它试图在不同的受支持的DBMS之间具有相同的行为。 我不是特别喜欢,但是至少现在看起来并不奇怪。

您的建议的问题在于,根据定义,您在一个表上不能有多个主键。 (您可以在多个列上具有1个复合PK,但这与具有2个单列PK完全不同。)

因此,如果.increments创建了PK,则不可能将其他列也标记为PK。 可以创建一个可以传递给.increments的选项,以禁用Postgres和支持该行为的任何其他DBMS上的主键行为。 也许这是最好的解决方案,因为它不会破坏向后兼容性,也不会破坏跨DBMS兼容性。 我仍然不喜欢这样做,也没有更改Postgres的默认行为,因此.increments也不会使其成为PK,但这将是更容易接受的PR。

如果有人想按照这些原则提交PR,我会赞成它。 :-)

@zacronos对不起,通过“多个PK”,我的意思是“多个列上有一个PK”。

同意以上所有评论。 尝试创建复合主键和非键自动增量列时,此行为是意外的。 看到这一实现真是太好了。

我也遇到了这个问题,但是在使用Postgres并将串行字段用作外键的情况下- Postgres串行字段的knex实现是increments()这默认使它们成为主键。 我希望能够构建sqlite3以及postgres,因此使用原始knex查询会将我限制为or。 有任何解决方法的建议吗?

该线程已经有一段时间没有引起太多关注了。 我想重申一下我认为是理想的,向后兼容的解决方案(因此可以成为任何次要版本的一部分):在.increments()添加一个新选项,也许是primaryKey: false ,这(对于可能的DBMS)禁用其功能的主键部分。

这是完全向后兼容的,因为.increments()保留其默认行为。

@zacronos快速浏览代码。 看起来微不足道。 会把公关放在一起
https://github.com/DeedMob/knex/tree/master/src

同样的问题在这里。 必须使用原始查询来避免bigIncrements()尝试创建另一个主键。

解决方法:

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

如果我正确理解了先前关于该主题的调查,那么严格地knex会强制使用主键,因为某些DB(例如MySQL)无法在没有主键的情况下创建序列吗? 如果是这种情况,那么就Postgres而言,我不必担心向后兼容性。

然后,MySQL用户会期望它是主要的。
Postgres用户不会。 _(以上显而易见)_

因此,我认为在Postgres中进行调整以_not_ force主键是很安全的,并将其添加到变更日志中。 不是作为补丁,而是用于下一个主要版本。

因此,我认为在Postgres中进行调整以不强制使用主键并将其添加到变更日志中是非常安全的。 不是作为补丁,而是用于下一个主要版本。

我知道数十个人总是只为Postgresql编写table.bigincrements('id')而没有.primary()来编写迁移,因为它一直都这样工作。

我虽然有一个额外的选项,但该选项应用于指示不应创建主键,但看起来好像不存在。 至少对于postgres / mysql实现,我没有找到有关跳过主键创建的任何信息。

这将是主要的向后兼容性,打破了迁移的变化,我不希望看到这种情况的发生。 如果我们想继续修补此旧的迁移API,我们首先需要为其添加版本控制支持,以防止所有人在每个knex版本上修复其旧的迁移(迁移代码中的这些更改实际上很难验证其与自动测试,因此它是knex用户代码中非常脆弱的部分)。

有什么办法吗?

有什么消息吗?

该线程已经有一段时间没有引起太多关注了。 我想重申一下我认为是理想的,向后兼容的解决方案(因此可以成为任何次要版本的一部分):在.increments()添加一个新选项,也许是primaryKey: false ,这(对于可能的DBMS)禁用其功能的主键部分。

这是完全向后兼容的,因为.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 等级

相关问题

koskimas picture koskimas  ·  3评论

saurabhghewari picture saurabhghewari  ·  3评论

marianomerlo picture marianomerlo  ·  3评论

mattgrande picture mattgrande  ·  3评论

sandrocsimas picture sandrocsimas  ·  3评论