SQL Server 和 MongoDB 中引用操作的特殊規則
如果您正在使用引用操作,則某些資料庫有特定的要求需要考慮。
-
如果關係鏈導致迴圈或多個級聯路徑,Microsoft SQL Server 不允許對外部索引鍵進行級聯引用操作。如果外部索引鍵上的引用操作設定為
NO ACTION之外的值(如果 Prisma ORM 管理引用完整性,則為NoAction),伺服器將在執行 SQL 時檢查迴圈或多個級聯路徑並返回錯誤。 -
對於 MongoDB,在 Prisma ORM 中使用引用操作要求:對於任何包含自引用關係或三個模型之間存在迴圈的資料模型,您必須將引用操作設定為
NoAction,以防止引用操作模擬無限迴圈。請注意,預設情況下,MongoDB 使用relationMode = "prisma"模式,這意味著 Prisma ORM 管理引用完整性。
給定 SQL
CREATE TABLE [dbo].[Employee] (
[id] INT NOT NULL IDENTITY(1,1),
[managerId] INT,
CONSTRAINT [PK__Employee__id] PRIMARY KEY ([id])
);
ALTER TABLE [dbo].[Employee]
ADD CONSTRAINT [FK__Employee__managerId]
FOREIGN KEY ([managerId]) REFERENCES [dbo].[Employee]([id])
ON DELETE CASCADE ON UPDATE CASCADE;
當 SQL 執行時,資料庫將丟擲以下錯誤
Introducing FOREIGN KEY constraint 'FK__Employee__managerId' on table 'Employee' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
在更復雜的資料模型中,查詢級聯路徑可能會變得複雜。因此,在 Prisma ORM 中,資料模型會在生成任何用於遷移的 SQL 之前進行驗證,並突出顯示作為路徑一部分的關係。這使得查詢和打破這些操作鏈變得容易得多。
自引用關係(SQL Server 和 MongoDB)
以下模型描述了一個自引用關係,其中 Employee 可以擁有經理和被管理人員,引用同一模型中的條目。
model Employee {
id Int @id @default(autoincrement())
manager Employee? @relation(name: "management", fields: [managerId], references: [id])
managees Employee[] @relation(name: "management")
managerId Int?
}
這將導致以下錯誤
Error parsing attribute "@relation": A self-relation must have `onDelete` and `onUpdate` referential actions set to `NoAction` in one of the @relation attributes. (Implicit default `onDelete`: `SetNull`, and `onUpdate`: `Cascade`)
如果不定義任何操作,Prisma ORM 將根據底層標量欄位是可選還是必填來使用以下預設值。
| 條款 | 所有標量欄位都是可選的 | 至少一個標量欄位是必填的 |
|---|---|---|
onDelete | SetNull | NoAction |
onUpdate | Cascade | Cascade |
由於上述關係中 onUpdate 的預設引用操作是 Cascade,而 onDelete 的預設引用操作是 SetNull,這會建立一個迴圈,解決方案是明確將 onUpdate 和 onDelete 的值設定為 NoAction。
model Employee {
id Int @id @default(autoincrement())
manager Employee @relation(name: "management", fields: [managerId], references: [id])
manager Employee @relation(name: "management", fields: [managerId], references: [id], onDelete: NoAction, onUpdate: NoAction)
managees Employee[] @relation(name: "management")
managerId Int
}
三個表之間的迴圈關係(SQL Server 和 MongoDB)
以下模型描述了 Chicken、Egg 和 Fox 之間的迴圈關係,其中每個模型都引用另一個模型。
model Chicken {
id Int @id @default(autoincrement())
egg Egg @relation(fields: [eggId], references: [id])
eggId Int
predators Fox[]
}
model Egg {
id Int @id @default(autoincrement())
predator Fox @relation(fields: [predatorId], references: [id])
predatorId Int
parents Chicken[]
}
model Fox {
id Int @id @default(autoincrement())
meal Chicken @relation(fields: [mealId], references: [id])
mealId Int
foodStore Egg[]
}
這將在構成迴圈的每個關係欄位中導致三個驗證錯誤。
第一個錯誤在 Chicken 模型中 egg 關係欄位中
Error parsing attribute "@relation": Reference causes a cycle. One of the @relation attributes in this cycle must have `onDelete` and `onUpdate` referential actions set to `NoAction`. Cycle path: Chicken.egg → Egg.predator → Fox.meal. (Implicit default `onUpdate`: `Cascade`)
第二個錯誤在 Egg 模型中 predator 關係欄位中
Error parsing attribute "@relation": Reference causes a cycle. One of the @relation attributes in this cycle must have `onDelete` and `onUpdate` referential actions set to `NoAction`. Cycle path: Egg.predator → Fox.meal → Chicken.egg. (Implicit default `onUpdate`: `Cascade`)
第三個錯誤在 Fox 模型中 meal 關係欄位中
Error parsing attribute "@relation": Reference causes a cycle. One of the @relation attributes in this cycle must have `onDelete` and `onUpdate` referential actions set to `NoAction`. Cycle path: Fox.meal → Chicken.egg → Egg.predator. (Implicit default `onUpdate`: `Cascade`)
由於關係欄位是必填的,onDelete 的預設引用操作是 NoAction,但 onUpdate 的預設引用操作是 Cascade,這導致了一個引用操作迴圈。解決方案是將任意一個關係中的 onUpdate 值設定為 NoAction。
model Chicken {
id Int @id @default(autoincrement())
egg Egg @relation(fields: [eggId], references: [id])
egg Egg @relation(fields: [eggId], references: [id], onUpdate: NoAction)
eggId Int
predators Fox[]
}
或
model Egg {
id Int @id @default(autoincrement())
predator Fox @relation(fields: [predatorId], references: [id])
predator Fox @relation(fields: [predatorId], references: [id], onUpdate: NoAction)
predatorId Int
parents Chicken[]
}
或
model Fox {
id Int @id @default(autoincrement())
meal Chicken @relation(fields: [mealId], references: [id])
meal Chicken @relation(fields: [mealId], references: [id], onUpdate: NoAction)
mealId Int
foodStore Egg[]
}
兩個模型之間的多重級聯路徑(僅限 SQL Server)
此資料模型描述了相同模型之間的兩條不同路徑,並且這兩個關係都會觸發級聯引用操作。
model User {
id Int @id @default(autoincrement())
comments Comment[]
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
authorId Int
author User @relation(fields: [authorId], references: [id])
comments Comment[]
}
model Comment {
id Int @id @default(autoincrement())
writtenById Int
postId Int
writtenBy User @relation(fields: [writtenById], references: [id])
post Post @relation(fields: [postId], references: [id])
}
此資料模型的問題在於,存在從 Comment 到 User 的兩條路徑,並且這兩個關係中的預設 onUpdate 操作都是 Cascade。這會導致兩個驗證錯誤
第一個錯誤在 writtenBy 關係中
Error parsing attribute "@relation": When any of the records in model `User` is updated or deleted, the referential actions on the relations cascade to model `Comment` through multiple paths. Please break one of these paths by setting the `onUpdate` and `onDelete` to `NoAction`. (Implicit default `onUpdate`: `Cascade`)
第二個錯誤在 post 關係中
Error parsing attribute "@relation": When any of the records in model `User` is updated or deleted, the referential actions on the relations cascade to model `Comment` through multiple paths. Please break one of these paths by setting the `onUpdate` and `onDelete` to `NoAction`. (Implicit default `onUpdate`: `Cascade`)
此錯誤意味著,透過更新 User 模型中記錄的主鍵,更新將透過 writtenBy 關係在 Comment 和 User 之間級聯一次,然後由於 Post 與 Comment 模型相關,再透過 post 關係從 Post 模型級聯一次。
修復方法是將 writtenBy 或 post 關係欄位中的 onUpdate 引用操作設定為 NoAction,或者透過更改 Post 模型中 author 關係的操作來修復。
model Comment {
id Int @id @default(autoincrement())
writtenById Int
postId Int
writtenBy User @relation(fields: [writtenById], references: [id])
writtenBy User @relation(fields: [writtenById], references: [id], onUpdate: NoAction)
post Post @relation(fields: [postId], references: [id])
}
或
model Comment {
id Int @id @default(autoincrement())
writtenById Int
postId Int
writtenBy User @relation(fields: [writtenById], references: [id])
post Post @relation(fields: [postId], references: [id])
post Post @relation(fields: [postId], references: [id], onUpdate: NoAction)
}
或
model Post {
id Int @id @default(autoincrement())
authorId Int
author User @relation(fields: [authorId], references: [id])
author User @relation(fields: [authorId], references: [id], onUpdate: NoAction)
comments Comment[]
}