我有写日志的表历史记录。
当我要插入日志时,出现错误:
23505:重复的密钥违反了唯一约束!
这是我的职能
public static void InsertLog(JobLogs log)
{
using (NpgsqlConnection con = new NpgsqlConnection(AppForms.connectionString))
{
string insert = "Insert into jat.history Values (@jobid, @year, @number, @datetime, @operator, @activity, @desc, nextval('jat.history_h_id_seq'))";
using (NpgsqlCommand cmd = new NpgsqlCommand(insert, con))
{
//MessageBox.Show(insert);
con.Open();
cmd.Parameters.AddWithValue("@jobid", log.JobId);
cmd.Parameters.AddWithValue("@year", log.jobMark.Year);
cmd.Parameters.AddWithValue("@number", log.jobMark.JobNumber);
cmd.Parameters.AddWithValue("@datetime", log.Time);
cmd.Parameters.AddWithValue("@operator", log.Operator.GetFullName());
cmd.Parameters.AddWithValue("@activity", log.Activity);
cmd.Parameters.AddWithValue("@desc", DBNull.Value);
cmd.ExecuteNonQuery();
}
}
}
表历史记录只有一个主键,它是-> h_id。当我将插入查询更改为此:
string insert = "Insert into jat.history Values(@jobid, @year, @number, @datetime, @operator, @activity, @desc, (select max(h_id) + 1 from jat.history))";
它工作正常,但是我听说我应该在多用户应用程序中使用序列,那么我的第一个函数和第一个查询出了什么问题?
编辑:
当我按照建议运行这两个查询时,得到以下结果:
" (SELECT last_value FROM jat.history_h_id_seq)"; -- result:282
"SELECT (SELECT MAX(h_id) FROM jat.history)"; -- result:290
答案 0 :(得分:0)
在表定义中,h_id
字段为h_id serial NOT NULL
。 serial
位表示已为此字段创建了一个序列,因此每次您在该表中插入新记录时,默认情况下,h_id
字段将为nextval('jat.history_h_id_seq')
的值。
像这样设置字段后,在大多数情况下,您永远不要在INSERT命令中手动指定该字段和值,因为这意味着nextval('jat.history_h_id_seq')
不会被调用和递增,并且可能导致您遇到的那种问题。
例如,让我们创建一个新表:CREATE TABLE t (id SERIAL PRIMARY KEY, txt TEXT);
。名为schema_name.t_id_seq
的序列将随表格一起自动创建。我还把主键放在了id
上,因为它也是您表的PK。
现在正确插入新记录:INSERT INTO t (txt) VALUES ('hello');
将创建一个包含值id: 1, txt: 'hello'
的记录。我根本没有指定id
,但是它会自动获得值1。如果我以相同的方式插入另一条记录,则新记录的id
将是2。
相反,我会错误地插入新记录:INSERT INTO t (id, txt) VALUES (2, 'bye');
。效果很好,但是我手动设置了id
的值,这意味着未使用该序列,因此其“下一个值”仍为2。现在,如果我正确插入了另一条记录:INSERT INTO t (txt) VALUES ('test');
,它将失败,并出现一个唯一的违规错误,因为它正试图将序列的nextval
值(2)用作id
,但是由于我手动指定了该字段,所以存在该ID的记录。
无论如何,这就是您的数据问题。根据您为从表中获取MAX(h_id)
和从序列中获取last_value
的值而执行的查询,表中的ID比序列中的ID高,而表中的ID最高应该与序列的last_value
相同(或低于序列号)。
然后要解决此问题,请先将序列更新为表中的最大ID:SELECT SETVAL('jat.history_h_id_seq', (SELECT MAX(h_id) FROM jat.history));
这会将您序列的当前值更新为表中的最高ID,因此下次使用该序列时,下一个ID(最后/当前+ 1)将高于表中的任何值,因此不会副本。
第二部分是确保您不要在任何插入语句中指定h_id
。只需让它自动从序列中获取下一个值即可。否则,您将再次遇到此问题。