为什么 Phoenix 的 Ecto 在上一个插入是错误时跳过 ID?



我是凤凰城的新手,当我注意到这件事时,我正在iex里玩。我试图插入一个记录,但它出现了验证错误。成功插入后,我发现ID不是下一个预期的ID,所以在我的数据库中,它只有2条ID为1和4的记录。

下面是一个日志,例如:

这是第一个错误,请注意ID为2。


iex(7)> Test.Projects.create_project(%{name: "Test", user: user})
[debug] QUERY ERROR db=3.6ms queue=6.4ms idle=1633.0ms
INSERT INTO "projects" ("name","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Test", ~N[2021-12-15 09:37:46], ~N[2021-12-15 09:37:46]]
** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "user_id" of relation "projects" violates not-null constraint
table: projects
column: user_id
Failing row contains (2, Test, null, 2021-12-15 09:37:46, 2021-12-15 09:37:46).
(ecto_sql 3.7.1) lib/ecto/adapters/sql.ex:760: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto 3.7.1) lib/ecto/repo/schema.ex:744: Ecto.Repo.Schema.apply/4
(ecto 3.7.1) lib/ecto/repo/schema.ex:367: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4

这是第二个错误,请注意ID为3。

iex(7)> user.id
1
iex(8)> Test.Projects.create_project(%{name: "Test", user_id: user.id})
[debug] QUERY ERROR db=2.1ms queue=2.1ms idle=1264.9ms
INSERT INTO "projects" ("name","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Test", ~N[2021-12-15 09:38:02], ~N[2021-12-15 09:38:02]]
** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "user_id" of relation "projects" violates not-null constraint
table: projects
column: user_id
Failing row contains (3, Test, null, 2021-12-15 09:38:02, 2021-12-15 09:38:02).
(ecto_sql 3.7.1) lib/ecto/adapters/sql.ex:760: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto 3.7.1) lib/ecto/repo/schema.ex:744: Ecto.Repo.Schema.apply/4
(ecto 3.7.1) lib/ecto/repo/schema.ex:367: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4

这是一个成功的插入,但请注意它的ID是4。我本以为是2。

iex(8)> recompile
Compiling 2 files (.ex)
:ok
iex(9)> Test.Projects.create_project(%{name: "Test", user_id: user.id})
[debug] QUERY OK db=9.7ms queue=5.4ms idle=1907.0ms
INSERT INTO "projects" ("name","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" ["Test", 1, ~N[2021-12-15 09:38:26], ~N[2021-12-15 09:38:26]]
{:ok,
%Test.Projects.Project{
__meta__: #Ecto.Schema.Metadata<:loaded, "projects">,
id: 4,
inserted_at: ~N[2021-12-15 09:38:26],
name: "Test",
updated_at: ~N[2021-12-15 09:38:26],
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}}

简而言之,这是数据库的预期行为,以避免";。。。为了避免阻塞并发事务">

如果你登录Postgres并在你的表上做d+ <table_name>,你应该会看到这样的东西:

my_db=# d+ accounts
Table "public.accounts"
Column    |              Type              | Collation | Nullable |               Default                | Storage  | Stats target | Description 
-------------+--------------------------------+-----------+----------+--------------------------------------+----------+--------------+-------------
id          | bigint                         |           | not null | nextval('accounts_id_seq'::regclass) | plain    |              | 
name        | character varying(255)         |           |          |                                      | extended |              | 
balance     | integer                        |           |          |                                      | plain    |              | 
inserted_at | timestamp(0) without time zone |           | not null |                                      | plain    |              | 
updated_at  | timestamp(0) without time zone |           | not null |                                      | plain    |              | 
Indexes:
"accounts_pkey" PRIMARY KEY, btree (id)
"accounts_name_index" UNIQUE, btree (name)
Access method: heap

id列被设置为作为序列的nextval('accounts_id_seq'::regclass)。在序列上的PG文档中,它表示即使事务失败,也不会回滚,就像在第一个实例中一样,nextval。因此,下一笔交易将接收序列中的下一个号码。

这类问题更多地是一个与Postgres相关的问题,而不是Phoenix/Elixir/Ecto,但我当然可以理解任何刚开始的人的困惑。

相关内容

最新更新