NestJS / Sequelize 重复键值违反唯一约束错误

问题描述 投票:0回答:1

我的数据库遇到了这个问题:

2023-12-15 12:31:31.913 UTC [95] ERROR:  duplicate key value violates unique constraint "users_roles_company_user_id_key"

一般来说,我的数据库可以使用以下概念来描述:有不同的公司,公司用户属于公司,公司(他们的用户)可以创建不同的自定义角色,并且这些角色可以分配给不同的公司用户.

这是DataGrip生成的数据库描述的一部分(我将跳过表中一些不重要的字段):

这是我编写的模型,以便使用这些外键生成此数据库:

users_roles

@Table({ tableName: 'users_roles' })
export class UserRole extends Model<UserRole> {
  @PrimaryKey
  @Default(DataType.UUIDV4)
  @Column(DataType.UUID)
  id: string;

  @ForeignKey(() => CompanyUser)
  @Column({ type: DataType.UUID, allowNull: false, field: 'company_user_id', unique: false })
  companyUserId: string;

  @ForeignKey(() => Company)
  @Column({ type: DataType.UUID, allowNull: false, field: 'company_id', unique: false })
  companyId: string;

  @ForeignKey(() => Role)
  @Column({ type: DataType.UUID, allowNull: false, field: 'role_id', unique: false })
  roleId: string;
}

companies

@Table({ tableName: 'companies' })
export class Company extends Model<Company, CompanyCreationAttributes> {
  @PrimaryKey
  @Default(DataType.UUIDV4)
  @Column(DataType.UUID)
  id: string;

  @BelongsTo(() => User)
  user: User;

  @HasMany(() => CompanyUser)
  companyUsers: Array<CompanyUser>;

  @BelongsToMany(() => Role, () => UserRole)
  roles: Array<Role>;
}

roles

@Table({ tableName: 'roles' })
export class Role extends Model<Role> {
  @PrimaryKey
  @Default(DataType.UUIDV4)
  @Column(DataType.UUID)
  id: string;

  @BelongsToMany(() => CompanyUser, () => UserRole)
  companyUsers: Array<CompanyUser>;

  @BelongsToMany(() => Company, () => UserRole)
  companies: Array<Company>;
}

company_user

@Table({ tableName: 'company_users' })
export class CompanyUser extends Model<
  CompanyUser,
  CompanyUserCreationAttributes
> {
  @PrimaryKey
  @Default(DataType.UUIDV4)
  @Column(DataType.UUID)
  id: string;

  @ForeignKey(() => User)
  @Column({ type: DataType.UUID, allowNull: false, field: 'user_id' })
  userId: string;

  @ForeignKey(() => Company)
  @Column({ type: DataType.UUID, allowNull: false, field: 'company_id' })
  companyId: string;

  @BelongsTo(() => Company)
  company: Company;

  @BelongsToMany(() => Role, () => UserRole)
  roles: Array<Role>;
}

老实说,可能我只是盲目的,无法发现错误,因为出于某种原因,在

users_roles
中,我可以有2条具有相同
company_id
的记录,但不是
company_user_id
,或者也许我在某个地方搞砸了数据库外键...

提前感谢大家的帮助!我真的很感激这个!

sql database postgresql sequelize.js nestjs
1个回答
0
投票

你的模型不应该导致这种结果,但数据库的行为就好像

unique: true
上有一个
UserRole.companyUserId
一样,这就像让任何公司只拥有一名担任任何给定角色的员工一样有意义:一名过度劳累的看门人,只有一名律师(不一定是坏事),只有一名 IT 人员(常见但不受欢迎)。

@Table({ tableName: 'users_roles' })
export class UserRole extends Model<UserRole> {
  //...
  @ForeignKey(() => CompanyUser)
  @Column({ type: DataType.UUID, 
            allowNull: false, 
            field: 'company_user_id', 
            unique: false //-----------here
          })
  companyUserId: string;
  //...
}

DataGrip 图上的 图标 表示所有 3 个外键都有一个索引。您的模型中没有

@Index
,因此它们可能已被 单独定义和同步 甚至直接 并且有人错误地认为 unique: true
 是个好主意。索引本身肯定是有帮助的,只是它不应该是唯一的;您需要找到它并使其成为常规的非唯一索引。

如果您最近更改了模型,请检查 CI/CD 日志以查看一切是否正常。您可能只想检查直接在数据库上存在哪些约束。唯一约束使用唯一索引,因此您可以使用 DataGrip

检查它 - 在那里,取消选中 unique 复选框可能就足够了。在 psql \d users_roles 中会列出这些,相当于运行这些查询(在

users_roles
之前添加模式名称)。
表上的索引/约束:

SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, i.indisvalid, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true), pg_catalog.pg_get_constraintdef(con.oid, true), contype, condeferrable, condeferred, i.indisreplident, c2.reltablespace FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x')) WHERE c.oid = 'users_roles'::regclass::oid AND c.oid = i.indrelid AND i.indexrelid = c2.oid ORDER BY i.indisprimary DESC, c2.relname;

从此表指向的外键

SELECT true as sametable, conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef, conrelid::pg_catalog.regclass AS ontable FROM pg_catalog.pg_constraint r WHERE r.conrelid = 'users_roles'::regclass::oid AND r.contype = 'f' AND conparentid = 0 ORDER BY conname

外键指向此表

SELECT conname, conrelid::pg_catalog.regclass AS ontable, pg_catalog.pg_get_constraintdef(oid, true) AS condef FROM pg_catalog.pg_constraint c WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('users_roles'::regclass) UNION ALL VALUES ('users_roles'::regclass)) AND contype = 'f' AND conparentid = 0 ORDER BY conname;

© www.soinside.com 2019 - 2024. All rights reserved.