我的数据库遇到了这个问题:
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
,或者也许我在某个地方搞砸了数据库外键...
提前感谢大家的帮助!我真的很感激这个!
你的模型不应该导致这种结果,但数据库的行为就好像
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;