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

i2byvkas  于 2024-01-07  发布在  PostgreSQL
关注(0)|答案(1)|浏览(172)

我的数据库遇到过这样的问题:

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

字符串
一般来说,我所拥有的数据库可以用下面的概念来描述:有不同的公司,公司用户属于公司,公司(他们的用户)可以创建不同的自定义角色,这些角色可以分配给不同的公司用户。
下面是DataGrip生成的这个数据库描述的一部分(我将跳过表的一些不重要的字段):
x1c 0d1x的数据
下面是我编写的模型,用于使用这些外键生成这个数据库:
表格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,或者可能我在某个地方搞砸了数据库外键...
提前感谢大家的帮助!我真的很感激!
编辑日期:
表的索引/约束:

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;


| 重新命名|医务室|独立的|不成簇的|无效的|pg_获取_索引定义|pg_获取_约束定义|控制型|可供的|延期的|不重复的|关系表空间|
| --|--|--|--|--|--|--|--|--|--|--|--|
| 用户_角色_主键|真正|真正|虚假|真正|CREATE UNIQUE INDEX用户角色主键ON用户角色USING b树(标识)|主键(id)|p|虚假|虚假|虚假| 0 |
| 用户角色公司标识角色标识关键字|虚假|真正|虚假|真正|CREATE UNIQUE INDEX用户角色公司标识角色标识关键字ON用户角色USING b树(公司标识,角色标识)|UNIQUE(公司标识,职责标识)|u|虚假|虚假|虚假| 0 |
| 用户角色公司用户标识关键字|虚假|真正|虚假|真正|CREATE UNIQUE INDEX用户角色公司用户标识关键字ON用户角色USING b树(公司用户标识)|UNIQUE(公司_用户_标识)|u|虚假|虚假|虚假| 0 |

b5lpy0ml

b5lpy0ml1#

DataGrip图上的图标表示users_roles的所有3个外键列都有索引。
其中一个必须是你的意外和不需要的unique constraint "users_roles_company_user_id_key"(每个unique constraint builds and uses a unique index自PostgreSQL 16)背后的唯一索引,它是默认@BelongsToMany()行为的结果:
BelongsToMany关联在直通模型的外键上创建唯一键。
您可以通过覆盖unique来禁用它:

@BelongsToMany(() => Role,{through:{model: 'UserRole',
                                    unique: false, //----here
                                   },
                          }
              )

字符串
虽然这里的情况不是这样,但如果直接将外键列定义为unique,也可能会出现此错误:

@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;
  //...
}


也可以在你的模型中设置一个@Index,或者define and sync it separately,甚至完全在其他地方设置一个directly on db--如果你这样设置unique: true,你也会得到这个错误。索引本身绝对是有帮助的,它只是不应该被设置为唯一的,除非有明显的必要;你需要找到它,并使它成为一个常规的,非唯一的索引。
在一个失败的迁移场景中,当你试图改变模型时,出现了一些错误,你没有完全解决这个问题,你也可以有一个来自改变之前的“流浪”唯一索引/约束-在这种情况下,你的CI/CD日志应该告诉你细节。
您可以check your constraints/indexes with DataGrip-在那里,取消勾选 unique 复选框可能就足够了。(在users_roles之前添加模式名称)。您可能会看到users_roles_company_user_id_key被定义为unique-作为临时解决方案,您可以复制该定义,drop index并再次运行该定义,在尝试填充表之前,只删除unique部分。
表上的索引/约束:

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;

相关问题