PostgreSQLエラー「ERROR: 重複キーが一意性制約"テーブル名_pkey"に違反しています」の原因と解決方法
PostgreSQLのデータベース・テーブルにレコードをinsertをする際、「ERROR: 重複キーが一意性制約"テーブル名_pkey"に違反しています DETAIL: キー (テーブル_カラム名)=(10) はすでに存在します」というようなエラーメッセージが表示された場合、主キー(プライマリキー)の値を一意にしないといけないにもかかわらず、今まさにinsertしようとしているレコードの主キーの値が、テーブル内の他のレコード内に既に存在することが原因だ。レコードの特定のカラムの値が一意でないといけない制約をテーブルに設けているにもかかわらず、その制約に違反するようなことをしていることから発生しているエラーであるため、このエラーを解決するには一意となるように(他のレコードの値と同じ値にならないように)別の値に変更してinsertすればよい。
もし、主キーの値をシーケンスで自動的に割り振っている場合、このエラーの原因はシーケンスの設定にあるとも言えるため、シーケンスに問題がある可能性を疑う場合は、このエラーを解決するにはシーケンスに誤りがないかどうかを確認し、シーケンスに誤りがあった場合はシーケンスを再設定するとよい。
前提条件
今回問題となったテーブルには100レコード存在し、各レコードの「テーブル_カラム名」の値は1から100までの間の数値であり、この数値が重複するレコードは1つもない前提とする。
テーブルの列・型・修飾語の情報を確認
psqlでデータベースに接続後、「\d テーブル名」でテーブルの情報を確認する。
データベース名=> \d テーブル名 テーブル "public.テーブル名" 列 | 型 | 修飾語 ----------------------+-----------------------------+---------------------------------------------------------------- テーブル_カラム名 | integer | not null default nextval('テーブル名_カラム名_seq'::regclass) テーブル_カラム名2 | timestamp without time zone | テーブル_カラム名3 | text |
今回問題となっているテーブル列「テーブル_カラム名」の「型」が「integer」であり、「修飾語」が「not null default nextval」となっていることから、シーケンスを使用していること、シーケンス名が「テーブル名_カラム名_seq」であることが分かる。
現在のシーケンス値を確認する
現在のシーケンス値は「select last_value from シーケンス名;」を実行することで確認できる。
データベース名=> select last_value from テーブル名_カラム名_seq; last_value ------------ 11 (1 行)
上述の「前提条件」で考えると、現在のシーケンスが11になっているのはおかしい。本来は100になっていないといけない。
シーケンス値を再設定
シーケンス値を再設定することで問題が解決するため、再設定する。
シーケンス値は「select setval('シーケンス名', 数値, true);」で再設定可能。
trueを指定した場合、指定した数値が現在のシーケンス値となり、nextvalは「数値+1」の値となる。
falseを指定した場合、指定した数値が現在のシーケンス値となり、nextvalは「数値」の値となる。
指定した数値で再設定する方法、シーケンス値を利用するテーブルカラムの最大値をselect文で取得して再設定する方法の2つを以下に記載する。シーケンス値を指定した数値で再設定する方法
「select setval('テーブル名_カラム名_seq', 100, true);」とすることで、100が現在のシーケンス値となり、nextvalは101となる。
データベース名=> select setval('テーブル名_カラム名_seq', 100, true); setval -------- 100 (1 行)
シーケンス値を利用するテーブルカラムの最大値をselect文で取得して再設定する方法
「select setval('テーブル名_カラム名_seq', ( select max(テーブル名_カラム名) from テーブル名), true);」とすることで、「テーブル名_カラム名」の最大値(=100)が現在のシーケンス値となり、nextvalは101となる。
データベース名=> select setval('テーブル名_カラム名_seq', ( select max(テーブル名_カラム名) from テーブル名), true); setval -------- 100 (1 行)