13

我最近将我的 rails 应用程序迁移到 PostgreSQL,以便利用全文搜索。

由于迁移恰逢迁移到新的虚拟主机,迁移的步骤是:

  1. 使用适当的 database.yml 文件在新服务器上部署应用程序和 db:create/db:schema:load
  2. mysqldump 仅来自现有 MySQL 生产数据库的数据
  3. 将数据导入 PostgreSQL 数据库

应用程序运行成功,但在尝试向数据库添加新内容时出现问题。例如,当我运行 rake 任务来更新我的 twitter 提要时:

PG::Error: ERROR:  duplicate key value violates unique constraint "twitter_feeds_pkey" DETAIL:  Key (id)=(3) already exists.

这也适用于所有其他模型,创建新文章、用户等。在开发中,我可以看到发布插入语句 n+1 次将成功保存记录而不会出错。

我的问题是:如何告诉 PostgreSQL 从现有数据开始按顺序添加索引?

我已经阅读了该REINDEX页面,但不认为这真的是我正在寻找的操作。

4

4 回答 4

31

在 Rails 中,您可以使用命令

ActiveRecord::Base.connection.reset_pk_sequence!('users')

使 User 表的主键索引再次同步。

于 2014-06-02T16:11:37.367 回答
21

如果架构包含序列或序列列,您应该将它们重置为相应列中出现的最大值。(通常你不应该从文件中导入连续剧,而是让它们自由自增。)

对于所有导入的表,您应该识别序列字段并在它们上运行以下代码。(用你的模式名代替“sch”,用你的表名代替“mytable”,用你的id列名代替“id”)

WITH mx AS ( SELECT MAX(id) AS id FROM sch.mytable)
SELECT setval('sch.mytable_id_seq', mx.id) AS curseq
FROM mx
        ;
于 2012-12-02T14:46:40.797 回答
5

您可以自动化 wildplasser 的解决方案,以便所有序列与其关联列的当前最大值同步:

do
$block$
declare 
  r        record;
  stmt     text;
  max_id   integer;
begin
  for r in (
              select *
              from (
                select table_schema, 
                       table_name, 
                       column_name, 
                       pg_get_serial_sequence(table_schema||'.'||table_name, column_name) as col_sequence
                from information_schema.columns
                where table_schema not in ('pg_catalog', 'information_schema')
              ) t
              where col_sequence is not null
        ) 
  loop
    stmt := 'select coalesce(max('||r.column_name||'), 0) + 1 from '||r.table_schema||'.'||r.table_name;
    execute stmt into max_id;
    raise notice 'Next ID for %.%.% is %', r.table_schema, r.table_name, r.column_name, max_id;
    perform setval(r.col_sequence, max_id); 
  end loop;
end;
$block$

请注意,这仅在列已定义为serialbigserial已成为序列的“所有者”时才有效。

于 2012-12-02T15:35:11.700 回答
0

另一种方法是从列中删除主键(id)(或不转储 id)。所以你的数据看起来像

INSERT INTO book (name, price) VALUES ('Alchemist' , 10);

代替

INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10);

这样,您不必在加载初始数据后重置主键

于 2014-05-23T17:18:10.327 回答