重复密钥违反唯一约束

时间:2018-12-01 23:16:45

标签: c# database postgresql sequence primary-key

我有写日志的表历史记录。

当我要插入日志时,出现错误:

  

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

1 个答案:

答案 0 :(得分:0)

在表定义中,h_id字段为h_id serial NOT NULLserial位表示已为此字段创建了一个序列,因此每次您在该表中插入新记录时,默认情况下,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。只需让它自动从序列中获取下一个值即可。否则,您将再次遇到此问题。