Seedを実行した後にシーケンスを更新する方法(duplicate key valueエラー)

Seedでidを指定してINSERTしたあとにORMでINSERTを行うとduplicate key valueのエラーが出ることがあります。
[参考記事] PostgreSQLのERROR: duplicate key value

ERROR:  duplicate key value violates unique constraint "hoge_pkey"
DETAIL:  Key (id)=(10) already exists.

PostgreSQLでは、プライマリキーの自動採番(AUTO INCREMENT)はシーケンス(sequence)によって管理されています。
[参考記事] 自動採番をするシーケンス(sequence)とは

idを指定してINSERTした場合は、シーケンスは更新されません。
またプライマリキーとシーケンス(sequence)は自動的に同期されないため、次にidを指定せずINSERTを行うとプライマリキーの重複が起こり上記のエラーが発生します。

マスターデータなどプライマリキーのidを指定してINSERTするときなどに発生します。

プライマリキーとシーケンス(sequence)を同期させるには、次のようなSQLを実行する必要があります。
[参考記事] プライマリキーとシーケンスを同期させるには

SELECT SETVAL ('【シーケンス名】', (SELECT MAX(【プライマリキー名】) FROM 【テーブル名】));
例
SELECT SETVAL ('users_id_seq', (SELECT MAX(id) FROM users));

SeedでINSERTしたあとにシーケンスを同期させるSQLを実行するには次のようにします。
[参考記事] SQLクエリーを直接実行する方法

<?php
declare(strict_types=1);

use Cake\Datasource\ConnectionManager;
use Migrations\AbstractSeed;

/**
 * Users seed.
 */
class UsersSeed extends AbstractSeed
{
    /**
     * Run Method.
     *
     * @return void
     */
    public function run()
    {
        $data = [
            [
                'id'    => 1,
                'email' => 'test@example.com',
                'name'  => 'テスト太郎',
            ],
            [
                'id'    => 2,
                'email' => 'hoge@example.com',
                'name'  => 'ほげほげ',
            ],
        ];

        $table = $this->table('users');
        $table->insert($data)->save();

        // Postgresではシーケンスを更新する
        $connection = ConnectionManager::get('default');
        if (strpos(strtolower($connection->config()['driver']), 'postgres') !== false) {
            $connection->execute("SELECT SETVAL ('users_id_seq', (SELECT MAX(id) FROM users));")->fetchAll('assoc');
        }
    }
}

他のRDBMSでも使用する可能性がなければ下記のコードのみで問題はありません。

$connection = ConnectionManager::get('default');
$connection->execute("SELECT SETVAL ('users_id_seq', (SELECT MAX(id) FROM users));")->fetchAll('assoc');

MySQLなどにはシーケンスはないため、下記のコードでPostgresSQLのみで実行するように制限をしています。

if (strpos(strtolower($connection->config()['driver']), 'postgres') !== false) {

}

スポンサーリンク

関連記事

スポンサーリンク

rsh リモート・マシンのコマンドを実行

ホームページ製作・web系アプリ系の製作案件募集中です。

上に戻る