【问题标题】:nextval(seq_name) not fetching correct value from DBnextval(seq_name) 没有从数据库中获取正确的值
【发布时间】:2017-01-02 10:55:10
【问题描述】:

我有一个带有 sqlalchemy 的烧瓶,连接到 postgres 数据库。所有组件都使用功能齐全的读取。我有一个简单的模型:

class School(db.Model):
    __tablename__ = 'schools'
    id = db.Column(db.Integer, db.Sequence('schools_id_seq'), primary_key=True)
    name = db.Column(db.String(80))
    active = db.Column(db.Boolean)
    created = db.Column(db.DateTime)
    updated = db.Column(db.DateTime)

    def __init__(self, name, active, created, updated):
        self.name = name
        self.active = active
        self.created = created
        self.updated = updated

正在处理 postgres 表:

CREATE SEQUENCE schools_id_seq;

CREATE TABLE schools(
  id  int   PRIMARY KEY NOT NULL DEFAULT nextval('schools_id_seq'),
  name  varchar(80) NOT NULL,
  active boolean DEFAULT TRUE,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

ALTER SEQUENCE schools_id_seq OWNED BY schools.id;

当我从 psql 对该表进行插入操作时,一切都很好:

cake=# select nextval('schools_id_seq');
 nextval 
---------
      65
(1 row)

cake=# INSERT INTO schools (id, name, active, created, updated) VALUES (nextval('schools_id_seq'),'Test', True, current_timestamp, current_timestamp);
INSERT 0 1

导致:

66 | Test                           |          0 | t      | 2016-08-25 14:12:24.928456 | 2016-08-25 14:12:24.928456

但是当我从烧瓶中尝试相同的插入时,堆栈跟踪抱怨重复的id,但它使用nextval 来获取该值:

sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) 重复键值违反了唯一约束“schools_pkey” 详细信息:键 (id)=(7) 已存在。 [SQL:“插入学校(ID,名称,活动,创建,更新)值(nextval('schools_id_seq'),%(名称)s,%(活动)s,%(创建)s,%(更新)s ) 返回学校.id"] [参数: {'active': True, 'name': 'Testomg', 'updated': datetime.datetime(2016, 8, 25, 14, 10, 5, 703471), 'created ': datetime.datetime(2016, 8, 25, 14, 10, 5, 703458)}]

为什么对 nextval 的 sqlalchemy 调用不会返回与 postgres db 中的同一调用产生的相同的下一个 val?

更新:@RazerM 告诉我关于 echo=true 参数的事情,我不知道。与

app.config['SQLALCHEMY_ECHO']=True

我从一个新的插入中产生了(注意,在这次尝试中它获取了10,应该是67):

2016-08-25 14:47:40,127 INFO sqlalchemy.engine.base.Engine select version()
2016-08-25 14:47:40,128 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,314 INFO sqlalchemy.engine.base.Engine select current_schema()
2016-08-25 14:47:40,315 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,499 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2016-08-25 14:47:40,499 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,594 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2016-08-25 14:47:40,594 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,780 INFO sqlalchemy.engine.base.Engine show standard_conforming_strings
2016-08-25 14:47:40,780 INFO sqlalchemy.engine.base.Engine {}
2016-08-25 14:47:40,969 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2016-08-25 14:47:40,971 INFO sqlalchemy.engine.base.Engine INSERT INTO schools (id, name, active, created, updated) VALUES (nextval('schools_id_seq'), %(name)s, %(active)s, %(created)s, %(updated)s) RETURNING schools.id
2016-08-25 14:47:40,971 INFO sqlalchemy.engine.base.Engine {'name': 'Testing', 'created': datetime.datetime(2016, 8, 25, 14, 47, 38, 785031), 'active': True, 'updated': datetime.datetime(2016, 8, 25, 14, 47, 38, 785050)}
2016-08-25 14:47:41,064 INFO sqlalchemy.engine.base.Engine ROLLBACK

sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) 重复键值违反了唯一约束“schools_pkey” 详细信息:键 (id)=(10) 已存在。 [SQL:“插入学校(ID,名称,活动,创建,更新)值(nextval('schools_id_seq'),%(名称)s,%(活动)s,%(创建)s,%(更新)s ) 返回学校.id"] [参数: {'updated': datetime.datetime(2016, 8, 25, 14, 54, 18, 262873), 'created': datetime.datetime(2016, 8, 25, 14, 54, 18, 262864), 'active': True, 'name': 'Testing'}]

【问题讨论】:

  • 是否在同一个数据库中,我的意思是:在运行原始查询和 sqlalchemy 查询时,您是否在同一个数据库上操作?
  • 是的,是同一个db。

标签: postgresql flask sqlalchemy flask-sqlalchemy nextval


【解决方案1】:

序列总是递增的,因此您的 select 语句和 SQLAlchemy 都会递增该值。

Sequence Manipulation Functions中所述:

将序列对象推进到下一个值并返回该值。这是原子完成的:即使多个会话同时执行 nextval,每个会话都将安全地接收到不同的序列值。

如果已经使用默认参数创建了一个序列对象,则连续的nextval调用将返回从1开始的连续值。其他行为可以通过在CREATE SEQUENCE命令中使用特殊参数来获得;有关详细信息,请参阅其命令参考页面。

重要提示:为避免阻塞从同一序列中获取数字的并发事务,nextval 操作永远不会回滚;也就是说,一旦获取了一个值,它就被认为是已使用的,并且不会再次返回。即使周围的事务稍后中止,或者调用查询最终没有使用该值也是如此。例如,带有 ON CONFLICT 子句的 INSERT 将计算要插入的元组,包括执行任何所需的 nextval 调用,然后检测任何可能导致其遵循 ON CONFLICT 规则的冲突。这种情况会在赋值序列中留下未使用的“漏洞”。因此,PostgreSQL 序列对象不能用于获得“无间隙”序列。

【讨论】:

  • 当然,我知道我先用 select 增加到 65,然后用 insert 增加到 66。所以 nextval 应该是 67,但是 sqlalchemy 使用 id=7,而不是 67。我可以将 db nextval 增加到 90,如果我回到 sqlalchemy,它将尝试 8。
  • 请将echo=True 传递给引擎或启用日志记录并粘贴发出的SQL。
【解决方案2】:

好吧,在这种情况下,解决方案很简单,它没有解释为什么,因为我认为我们应该查看整个环境,您无法向我们展示,否则会花费很长时间。所以尝试插入尽可能多的记录,因为它将达到 67 并且下一个插入应该应用而没有任何错误,因为序列最小值将达到适当的值。当然你可以尝试先在id属性中添加server_default选项:

 server_default=db.Sequence('schools_id_seq').next_value()

所以

seq = db.Sequence('schools_id_seq')

在课堂上:

id = db.Column(db.Integer, seq, server_default=seq.next_value(), primary_key=True)

Sqlalchemy 是这样提到的:

Sequence 最初旨在成为 Python 端的指令,因此以这种方式指定它可能也是一个好主意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 1970-01-01
    相关资源
    最近更新 更多