【问题标题】:PostgreSQL AutoincrementPostgreSQL 自动增量
【发布时间】:2010-10-21 17:23:10
【问题描述】:

我正在从 MySQL 切换到 PostgreSQL,并且想知道如何执行自动增量值。我在 PostgreSQL 文档中看到了一个数据类型“serial”,但在使用它时出现语法错误(在 v8.0 中)。

【问题讨论】:

  • 如果您提供查询和遇到的错误 - 也许有人可以告诉您查询出了什么问题。
  • 我的第一个热门话题也是 Mich',因为这个问题获得了足够多的相关观点,为什么不投票。 PS如果你不知道怎么做,这不是微不足道的。
  • 如果您的客户端驱动程序是 Npgsql,SERIAL 是首选。提供程序在使用 SELECT currval(pg_get_serial_sequence('table', 'column')) 进行 INSERT 之后在内部选择新值。如果底层列不是序列类型(例如数字类型 + 显式序列),这将失败
  • 只是出于好奇...为什么有人必须从非常好的 MySQL 迁移到 PostgreSql?
  • ...这样更好。

标签: postgresql auto-increment


【解决方案1】:

是的,SERIAL 是等效的函数。

CREATE TABLE foo (
    id SERIAL,
    bar varchar
);

INSERT INTO foo (bar) VALUES ('blah');
INSERT INTO foo (bar) VALUES ('blah');

SELECT * FROM foo;

+----------+
| 1 | blah |
+----------+
| 2 | blah |
+----------+

SERIAL 只是一个围绕序列的创建表时间宏。您不能将 SERIAL 更改为现有列。

【讨论】:

  • 因为除非您有一个表 "Table""table",否则只需将其不加引号并将其规范化为 table。惯例是永远不要在 Pg 中使用引号。如果需要,您可以使用混合大小写的名称来显示外观,只是不需要它:CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar; 可以工作,SELECT * FROM foobar 也可以。
  • plus...大多数关系数据库实现的标准是没有引号。如果需要,它可以更简单地交换数据库实现。
  • @EvanCarroll 请尝试INSERT INTO user 不带引号。
  • 每个 postgres 文档,一致地引用或取消引用:postgresql.org/docs/current/interactive/…
  • 引用没有错。事实上,这里奇怪的是 Postgres,它对模式使用了无大小写约定。从任何其他数据库迁移都需要这个。
【解决方案2】:

您可以使用任何其他integer data type,例如smallint

例子:

CREATE SEQUENCE user_id_seq;
CREATE TABLE user (
    user_id smallint NOT NULL DEFAULT nextval('user_id_seq')
);
ALTER SEQUENCE user_id_seq OWNED BY user.user_id;

最好使用自己的数据类型,而不是用户serial data type

【讨论】:

  • 我想说这实际上是更好的答案,因为它允许我通过设置列默认值来修改我刚刚在 PostgreSQL 中创建的表(在阅读 CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html 之后)。但是,我不太清楚您为什么要更换所有者。
  • @JayC:来自documentation最后,序列被标记为列“拥有”,因此如果列或表被删除,它将被删除。
  • 为什么 postgres 社区不重新发明自动增量关键字?
  • 如果您只想要更小的数据类型,还有 smallserial。
  • 我一直不明白为什么有些开发人员会重新发明轮子或使事情复杂化。如果 Postgres 已经有了专门针对这个问题优化和创建的内部机制(Serial),为什么还要通过创建序列来让一切变得更加复杂呢?
【解决方案3】:

在所提问题的上下文中并回复@sereja1c 的评论,创建SERIAL 隐式创建序列,因此对于上面的示例-

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLE 将为串行列foo.id 隐式创建序列foo_id_seq。因此,SERIAL [4 Bytes] 易于使用,除非您的 id 需要特定的数据类型。

【讨论】:

    【解决方案4】:

    如果你想在已经存在的表中添加序列到id,你可以使用:

    CREATE SEQUENCE user_id_seq;
    ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');
    

    【讨论】:

    • 什么是序列? AUTO_INCREMENT 在哪里?
    • @Green:AUTO_INCREMENT 不是 SQL 标准的一部分,它是 MySQL 特有的。序列是在 PostgreSQL 中做类似工作的东西。
    • 如果你使用'id SERIAL',它会自动在PostgreSQL中创建一个序列。该序列的名称将是 __seq
    • 你不用ALTER COLUMN user_id吗?
    • 我试过这个方法,但我得到一个错误:ERROR: syntax error at or near "DEFAULT"有什么建议吗?
    【解决方案5】:

    你要注意不要直接插入到你的SERIAL或sequence字段中,否则当sequence达到插入值时你的写入会失败:

    -- Table: "test"
    
    -- DROP TABLE test;
    
    CREATE TABLE test
    (
      "ID" SERIAL,
      "Rank" integer NOT NULL,
      "GermanHeadword" "text" [] NOT NULL,
      "PartOfSpeech" "text" NOT NULL,
      "ExampleSentence" "text" NOT NULL,
      "EnglishGloss" "text"[] NOT NULL,
      CONSTRAINT "PKey" PRIMARY KEY ("ID", "Rank")
    )
    WITH (
      OIDS=FALSE
    );
    -- ALTER TABLE test OWNER TO postgres;
     INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
               VALUES (1, '{"der", "die", "das", "den", "dem", "des"}', 'art', 'Der Mann küsst die Frau und das Kind schaut zu', '{"the", "of the" }');
    
    
     INSERT INTO test("ID", "Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
               VALUES (2, 1, '{"der", "die", "das"}', 'pron', 'Das ist mein Fahrrad', '{"that", "those"}');
    
     INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
               VALUES (1, '{"der", "die", "das"}', 'pron', 'Die Frau, die nebenen wohnt, heißt Renate', '{"that", "who"}');
    
    SELECT * from test; 
    

    【讨论】:

      【解决方案6】:

      虽然看起来序列与 MySQL auto_increment 等价,但还是有一些细微但重要的区别:

      1。失败的查询增加序列/序列

      串行列在查询失败时递增。这会导致失败查询产生碎片,而不仅仅是行删除。例如,在您的 PostgreSQL 数据库上运行以下查询:

      CREATE TABLE table1 (
        uid serial NOT NULL PRIMARY KEY,
        col_b integer NOT NULL,
        CHECK (col_b>=0)
      );
      
      INSERT INTO table1 (col_b) VALUES(1);
      INSERT INTO table1 (col_b) VALUES(-1);
      INSERT INTO table1 (col_b) VALUES(2);
      
      SELECT * FROM table1;
      

      你应该得到以下输出:

       uid | col_b 
      -----+-------
         1 |     1
         3 |     2
      (2 rows)
      

      注意 uid 如何从 1 变为 3 而不是 1 变为 2。

      如果您要手动创建自己的序列,仍然会发生这种情况:

      CREATE SEQUENCE table1_seq;
      CREATE TABLE table1 (
          col_a smallint NOT NULL DEFAULT nextval('table1_seq'),
          col_b integer NOT NULL,
          CHECK (col_b>=0)
      );
      ALTER SEQUENCE table1_seq OWNED BY table1.col_a;
      

      如果您想测试 MySQL 的不同之处,请在 MySQL 数据库上运行以下命令:

      CREATE TABLE table1 (
        uid int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
        col_b int unsigned NOT NULL
      );
      
      INSERT INTO table1 (col_b) VALUES(1);
      INSERT INTO table1 (col_b) VALUES(-1);
      INSERT INTO table1 (col_b) VALUES(2);
      

      您应该得到以下无碎片

      +-----+-------+
      | uid | col_b |
      +-----+-------+
      |   1 |     1 |
      |   2 |     2 |
      +-----+-------+
      2 rows in set (0.00 sec)
      

      2。手动设置序列列值可能会导致以后的查询失败。

      @trev 在之前的回答中指出了这一点。

      为了模拟这种情况,手动将 uid 设置为 4,这将在稍后“冲突”。

      INSERT INTO table1 (uid, col_b) VALUES(5, 5);
      

      表格数据:

       uid | col_b 
      -----+-------
         1 |     1
         3 |     2
         5 |     5
      (3 rows)
      

      运行另一个插入:

      INSERT INTO table1 (col_b) VALUES(6);
      

      表格数据:

       uid | col_b 
      -----+-------
         1 |     1
         3 |     2
         5 |     5
         4 |     6
      

      现在如果你运行另一个插入:

      INSERT INTO table1 (col_b) VALUES(7);
      

      它将失败并显示以下错误消息:

      错误:重复键值违反唯一约束“table1_pkey” 详细信息:键 (uid)=(5) 已存在。

      相比之下,MySQL 会优雅地处理这个问题,如下所示:

      INSERT INTO table1 (uid, col_b) VALUES(4, 4);
      

      现在插入另一行而不设置uid

      INSERT INTO table1 (col_b) VALUES(3);
      

      查询没有失败,uid只是跳转到5:

      +-----+-------+
      | uid | col_b |
      +-----+-------+
      |   1 |     1 |
      |   2 |     2 |
      |   4 |     4 |
      |   5 |     3 |
      +-----+-------+
      

      测试是在 MySQL 5.6.33、Linux (x86_64) 和 PostgreSQL 9.4.9 上进行的

      【讨论】:

      • 您正在进行比较,但我在这里看不到任何解决方案!这是一个答案吗?
      • @Anwar 它只是扩展了各种答案,说明答案是使用序列/序列。这提供了一些需要考虑的重要背景。
      • 使用 MSSQL 和 Mysql 已有 10 年,第 2 点简直荒谬至极。用户的随机手动插入可能会完全破坏应用程序
      • @dsturbid 代码错误或数据质量问题有时可能会合理地要求在生产环境中手动插入。这可能足以让一些应用程序开发人员避免使用此功能。
      • “失败的查询增加序列/序列” - 我对两个 psql 实例的测试表明这也适用于失败的事务。如果你begin 有两个事务,INSERT 的第一个请求第一个 id。如果第二个事务提交但第一个事务回滚,则表中将跳过第一个 id。
      【解决方案7】:

      从 Postgres 10 开始,还支持 SQL 标准定义的标识列:

      create table foo 
      (
        id integer generated always as identity
      );
      

      创建一个不能被覆盖的标识列,除非明确要求。以下插入将失败,列定义为generated always

      insert into foo (id) 
      values (1);
      

      然而,这可以被否决:

      insert into foo (id) overriding system value 
      values (1);
      

      当使用选项 generated by default 时,这与现有的 serial 实现基本相同:

      create table foo 
      (
        id integer generated by default as identity
      );
      

      当手动提供值时,也需要手动调整底层序列 - 与 serial 列相同。


      默认情况下,标识列不是主键(就像serial 列一样)。如果应该是一个,则需要手动定义主键约束。

      【讨论】:

      【解决方案8】:

      抱歉,重提一个老问题,但这是 Google 上出现的第一个 Stack Overflow 问题/答案。

      这篇文章(首先出现在 Google 上)讨论了使用更新的 PostgreSQL 10 语法: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

      恰好是:

      CREATE TABLE test_new (
          id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
      );
      

      希望有帮助:)

      【讨论】:

      • 这确实是 PostgreSQL 10 的方式,它与 DB2 或 Oracle 等其他数据库软件的语法相同。
      • @adriaan 实际上GENERATED … AS IDENTITY 命令是标准SQL。首先在SQL:2003中添加,然后在SQL:2008中阐明。请参阅功能 # T174 & F386 & T178。
      【解决方案9】:

      这种方法肯定行得通,希望对你有帮助:

      CREATE TABLE fruits(
         id SERIAL PRIMARY KEY,
         name VARCHAR NOT NULL
      );
      
      INSERT INTO fruits(id,name) VALUES(DEFAULT,'apple');
      
      or
      
      INSERT INTO fruits VALUES(DEFAULT,'apple');
      

      您可以在下一个链接中查看详细信息: http://www.postgresqltutorial.com/postgresql-serial/

      【讨论】:

        【解决方案10】:

        自 PostgreSQL 10 起

        CREATE TABLE test_new (
            id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
            payload text
        );
        

        【讨论】:

          【解决方案11】:

          创建序列。

          CREATE SEQUENCE user_role_id_seq
            INCREMENT 1
            MINVALUE 1
            MAXVALUE 9223372036854775807
            START 3
            CACHE 1;
          ALTER TABLE user_role_id_seq
            OWNER TO postgres;
          

          改变表

          ALTER TABLE user_roles ALTER COLUMN user_role_id SET DEFAULT nextval('user_role_id_seq'::regclass);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-08-19
            • 2013-03-04
            • 2015-09-03
            • 1970-01-01
            • 1970-01-01
            • 2011-08-14
            • 2012-11-22
            相关资源
            最近更新 更多