使用SQLAlchemy执行INSERT INTO SELECT RETURNING操作

3

我真的在努力理解SQLAlchemy,但是进展缓慢而且令人困惑。我有以下查询语句:

INSERT INTO
    user (name, email)
SELECT
    :name, :email
WHERE NOT EXISTS (
    SELECT * FROM other WHERE other.id > :some_id
)
RETURNING id

我希望以比提供原始SQL更加Pythonic的方式通过SQLAlchemy运行此内容。

我无法确定哪些函数存在于哪些对象上 - filterwhereinsertadd... 文档内容冗长且令人不知所措。

我有一些生成的user对象,它们正在等待添加到数据库中 - 如果有人可以帮助我构建这个查询,那么这应该是应该完成这项任务的查询。

我希望所有这些都在一个查询中完成,因为存在竞争条件,有时我会得到一个新的other对象,这会使当前的user对象无效,因此我不再想将它们添加到数据库中。


您是否定义了Table或声明性(ORM)类?换句话说,您是尝试使用核心还是ORM - SQLAlchemy实际上是两个库。 - Ilja Everilä
我理解的没错吧,这是一个有条件的插入操作,如果姓名和电子邮件的记录在用户表中不存在? - pi.
@IljaEverilä - 我认为这就是我感到困惑的地方,即1个方面中有2个库。我们有许多从“Base”继承并使用declarative_base的类,但没有任何从“Table”继承的类(这进一步使我在文档中寻找时变得更加复杂)。典型的数据库交互调用类似于session.add(some_obj)这样的东西-因此我认为那是ORM? - dwanderson
是的,这就是ORM在发挥作用。请注意,这两者并不完全分离,ORM是建立在Core之上的,因此它们非常搭配。关于CoreORM的两个教程可以帮助您形成对各自内容的理解。例如,您不需要从Table继承,而是直接使用Table对象本身。 - Ilja Everilä
1个回答

4

我使用了 SQLAlchemy-Core 来处理您的查询。我将为简单的 Table 对象和使用 declarative_base 的模型提供示例。

给定以下演示代码:

from sqlalchemy import table, column, Unicode, String, Integer

user = table(
    'user',
    column('id', Integer),
    column('name', Unicode),
    column('email', String),
)

other = table(
    'other',
    column('id', Integer),
    column('name', Unicode),
    column('email', String),
)

如果您使用ORM,则定义可能如下:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, Unicode, String


Base = declarative_base()

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    name = Column('name', Unicode)
    email = Column('email', String)


class Other(Base):
    __tablename__ = 'other'

    id = Column(Integer, primary_key=True)
    name = Column('name', Unicode)
    email = Column('email', String)

# Pull out the `Table` objects from the mapped models.
user = User.__table__
other = Other.__table__

鉴于此,您可以像这样渲染查询:
from sqlalchemy import select, literal, exists, text

# Your inputs.
name = 'foo'
email = 'foo@example.com'
other_id = 1

# Compile the query for demonstration purposes
print(
    user
    .insert()
    .from_select(
        ['name', 'email'],
        select(
            [literal(name),
             literal(email)])
        .where(~exists([other.c.id])
               .where(other.c.id > other_id))
    )
    .returning(text('id'))
)

编译查询将会给你下面的SQL字符串:
INSERT INTO "user" (name, email) SELECT :param_1 AS anon_1, :param_2 AS anon_2 
WHERE NOT (EXISTS (SELECT other.id 
FROM other 
WHERE other.id > :id_1)) RETURNING id

如果我手头有declarative_base而不是table类,怎么办?之前没有表达清楚,抱歉;不过这看起来是一个很好的开始路径。 - dwanderson
一个小问题是,为轻量级 table() 构建的列集合应该是 column() 构造,或者说它是这样 记录的 - Ilja Everilä
1
@dwanderson,您可以通过执行Foo.__table__来获取映射类的Table对象。 - univerio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接