我正在使用 CTE 检查数据库行,通过过滤删除行,最后将行插入数据库。
下面是插入行的 go 函数:
func (vm VoteModel) Insert(vote *Vote) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// begin atomic transaction
tx, err := vm.DB.BeginTx(ctx, nil)
if err != nil {
return err
}
// Use a CTE to check if the poll_choice_id belongs to the poll_id and to delete any existing vote
insertVoteQuery := `
WITH valid_choice AS (
SELECT 1
FROM poll_choices
WHERE id = $1 AND poll_id = $2
LIMIT 1
),
delete_existing_vote AS (
DELETE FROM votes
WHERE user_id = $3 AND poll_id = $2
RETURNING 1
)
INSERT INTO votes (user_id, poll_id, poll_choice_id)
SELECT $3, $2, $1
FROM valid_choice
RETURNING id, added_at, version
`
insertArgs := []interface{}{vote.PollChoiceID, vote.PollID, vote.UserID}
err = tx.QueryRowContext(ctx, insertVoteQuery, insertArgs...).Scan(
&vote.ID,
&vote.AddedAt,
&vote.Version,
)
if err != nil {
if rbErr := tx.Rollback(); rbErr != nil {
return rbErr
}
switch {
case errors.Is(err, sql.ErrNoRows):
return ErrRecordNotFound
default:
return err
}
}
// commit
err = tx.Commit()
if err != nil {
if rbErr := tx.Rollback(); rbErr != nil {
return rbErr
}
return err
}
return nil
}
假设我有这些行的投票表:
id | user_id | poll_id | poll_choice_id | added_at | version
----+---------+---------+----------------+---------------------------+---------
10 | 11 | 12 | 5 | 2024-05-21 13:39:58+05:30 | 1
16 | 3 | 18 | 34 | 2024-05-22 21:57:44+05:30 | 1 <-- existing user vote
24 | 9 | 18 | 35 | 2024-05-24 10:54:47+05:30 | 1
(3 rows)
当我尝试对一项民意调查进行投票时,我已经使用另一个民意调查选项进行了投票,例如,我的用户 ID 是
3
:
{
"poll_id": 18,
"poll_choice_id": 31
}
我收到错误:
votes_user_id_poll_id_key :“违反唯一约束”
但我想我删除了所有用户并使用模型 go
Insert()
函数在这一行中进行投票:
delete_existing_vote AS(从投票中删除 user_id = $3 且 poll_id = $2 返回 1 )
我错过了什么?我可以分离查询事务并进行额外的数据库调用,但我想最大程度地减少数据库调用的数量。
答案来自手册:
WITH中的子语句是并发执行的 以及主要查询。因此,在使用数据修改时 WITH 中的语句,指定的实际更新顺序 发生的事情是不可预测的 [...] 尝试在单个语句中更新同一行两次是不行的 支持的。仅发生其中一项修改,但并非如此 很容易(有时不可能)可靠地预测是哪一个。这 也适用于删除已在同一行中更新的行 声明:仅执行更新。因此你应该 通常避免尝试在单个行中修改同一行两次 陈述。特别是避免编写可能会导致以下情况的WITH子语句: 影响主语句或同级语句更改的相同行 子陈述。此类声明的影响不会 可以预见的。