Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何区分sqlalchemy INSERT中的冲突…;关于冲突(Upsert)_Python_Postgresql_Sqlalchemy - Fatal编程技术网

Python 如何区分sqlalchemy INSERT中的冲突…;关于冲突(Upsert)

Python 如何区分sqlalchemy INSERT中的冲突…;关于冲突(Upsert),python,postgresql,sqlalchemy,Python,Postgresql,Sqlalchemy,我正在阅读SqlAlchemy关于postgres方言的upsert操作的文档 有没有办法知道upsert是插入还是更新 文档似乎忽略了这一细节。来自postgresql文档: 成功完成后,INSERT命令返回表单的命令标记 INSERT oid count 计数是插入或更新的行数。如果count正好是一,并且目标表具有oid,则oid是分配给插入行的oid。单行必须已插入,而不是已更新。否则oid为零 因此,可以通过检查查询消息来检查更新与插入 可以使用语法使用OID创建表 CREATE T

我正在阅读SqlAlchemy关于postgres方言的upsert操作的文档

有没有办法知道upsert是插入还是更新


文档似乎忽略了这一细节。

来自postgresql文档:

成功完成后,INSERT命令返回表单的命令标记

INSERT oid count
计数是插入或更新的行数。如果count正好是一,并且目标表具有oid,则oid是分配给插入行的oid。单行必须已插入,而不是已更新。否则oid为零

因此,可以通过检查查询消息来检查更新与插入

可以使用语法使用
OID
创建表

CREATE TABLE mytable (...) WITH OIDS 
,或可以在具有语法的现有表上启用OID

ALTER TABLE mytable SET WITH OIDS
使用sqlalchemy,可以使用
OID
创建一个表,如下所示:

import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
engine = sa.create_engine('postgresql+psycopg2://user:pass@hostname:port/db')

class Person(Base):
    __tablename__ = 'people'
    __table_args__ = {'postgresql_with_oids': True}
    email = sa.Column(sa.Text, primary_key=True)
    name = sa.Column(sa.Text, nullable=False)

Base.metadata.create_all(engine)
而insert on conflict语句的构造可以如下所示:

p = {'email': 'hal@hal.hal', 'name': 'Hally hal'}
stmt = pg_insert(Person).values(p)
stmt = stmt.on_conflict_do_update(
    index_elements = [Person.email],
    set_ = {'name': stmt.excluded.name}
)
最后,执行语句后,返回一个结果代理,该代理具有一个属性
lastrowid
,该属性对应于查询消息
INSERT oid count
中的
oid

因此,如果您第一次执行
stmt

r = engine.execute(stmt)
r.lastrowid
将在创建新行后输出
一个整数>0

每隔一次,
r.lastrowid
将输出
0


如果需要一次跟踪多行的upsert,可以将额外的列设置为从insert语句的on CONSTRICT do update部分更新的标志

根据具体要求,有很多方法可以做到这一点。这里有一个选择

添加一个额外的列
conflict\u updated\u at=sa.column(sa.Datetime(True))

将upsert定义更改为

stmt = pg_insert(Person).values(p)
stmt = stmt.on_conflict_do_update(
    index_elements = [Person.email],
    set_ = {'name': stmt.excluded.name,
            'conflict_updated_at': sa.func.now()}
)

只需添加最后一个
RETURNING
子句,其中包含:

...
RETURNING (tbl.xmax = 0) AS inserted
对于插入的行返回
true
,对于更新的行返回
false
。不过,这依赖于未记录的实现细节。有关详细说明,请参阅:

添加OID(如另一个答案中所建议的)会增加成本,使表膨胀并烧掉OID(如果您的表不是很小的话)。这就是为什么带OID的
default\u的常规设置早就被更改为
off
(Postgres 8.1)

在用户表中使用OID被认为是不推荐的,因此 安装时应禁用此变量。应用程序 特定表格需要OID时,应使用OID指定

创建表


插入冲突是一次插入/更新一行,还是成批插入?一次插入一行我认为当您执行upsert语句时,返回值的类型是
ResultProxy
,您可以使用
is\u insert
方法检查
execute
d语句是否插入了数据-。我无法确认这一点,因为我没有使用postgress数据库。我看到了
ResultProxy.is\u insert()
,但我不确定它是否适用于
postgresql.dml.insert()
@Haleemulali谢谢,只是对属性的一个简单介绍,但我无法修改注释。谢谢你的回答!你知道有没有一种方法可以用sqlite方言测试
on\u conflict\u Do\u update
代码?我的单元测试都在sqlite中,到目前为止,我只使用了标准的SQL表达式。我已经在一些数据类型上使用了
with_variant
,但不知道如何处理操作。它不起作用,因为sqlite没有postgresql那样的upsert机制。对于此测试,您唯一的途径是使用postgresql数据库。这里有一个关于配置postgresql以实现这种用途的很好的答案:谢谢,我接受你的回答,因为(除了不推荐的原因和膨胀的原因)这比迁移表架构的工作量要小得多。请注意,它建立在可能在未来版本中更改的实现细节上(第11页之后).未记录的实施细节这对生产安全吗?乍一看,这似乎更像是一个黑客行为。@Sanandrea:旁白:用SQLite测试应该在Postgres上运行的项目是一个根本上有缺陷的想法。相关:吹毛求疵:“RDBMS不可知论者”是一个错误的目标。甚至不可能,因为目前还没有接近SQL标准的RDBMS。(而且标准有相当多的弱点。)博士后可能是最接近的大的。在可能的情况下,选择SQL标准方式仍然是一个有效的想法。好的,我们要把它拖出来。我想我们都明白了。