在sqlalchemy中加入值

时间:2016-08-19 10:02:34

标签: python postgresql sqlalchemy

我想在SQLalchemy中运行以下postgreSQL查询:

select c.*
from comments c
join (
  values
    (1,1),
    (3,2),
    (2,3),
    (4,4)
) as x (id, ordering) on c.id = x.id
order by x.ordering

是否可以加入类似列表或元组列表之类的东西并使用它们来提供SQLalchemy中的排序?

3 个答案:

答案 0 :(得分:2)

有关如何使SQLAlchemy编译VALUES子句,请参阅PGValues recipe

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql import column
from sqlalchemy.sql.expression import FromClause


class values(FromClause):
    named_with_column = True

    def __init__(self, columns, *args, **kw):
        self._column_args = columns
        self.list = args
        self.alias_name = self.name = kw.pop('alias_name', None)

    def _populate_column_collection(self):
        for c in self._column_args:
            c._make_proxy(self)


@compiles(values)
def compile_values(element, compiler, asfrom=False, **kw):
    columns = element.columns
    v = "VALUES %s" % ", ".join(
        "(%s)" % ", ".join(
                compiler.render_literal_value(elem, column.type)
                for elem, column in zip(tup, columns))
        for tup in element.list
    )
    if asfrom:
        if element.alias_name:
            v = "(%s) AS %s (%s)" % (v, element.alias_name, (", ".join(c.name for c in element.columns)))
        else:
            v = "(%s)" % v
    return v


>>> x = values([column("id", Integer), column("ordering", Integer)], (1, 1), (3, 2), (2, 3), (4, 4), alias_name="x")
>>> q = session.query(Comment).join(x, Comment.id == x.c.id).order_by(x.c.ordering)
>>> print(q)
SELECT comments.id AS comments_id 
FROM comments JOIN (VALUES (1, 1), (3, 2), (2, 3), (4, 4)) AS x (id, ordering) ON comments.id = x.id ORDER BY x.ordering

答案 1 :(得分:1)

from sqlalchemy import *

from yourdbmodule import dbsession


VALUES = ((1, 1), (3, 2), (2, 3), (4, 4))


temp_table = Table(
    'temp_table', MetaData(),
    Column('id', INT, primary_key=True),
    Column('ordering', INT),
    prefixes=['TEMPORARY']
)
temp_table.create(bind=dbsession.bind, checkfirst=True)


dbsession.execute(temp_table.insert().values(VALUES))


# Now you can query it
dbsession.query(Comments)\
    .join(temp_table, Comments.id == temp_table.c.id)\
    .order_by(temp_table.c.ordering)\
    .all()

答案 2 :(得分:1)

univerio的解决方案效果很好,但如果从给定值中选择一列,则会抛出错误:

>>> q = session.query(Comment, c.x.id).join(x, Comment.id == x.c.id).order_by(x.c.ordering)
>>> q.all()
ProgrammingError (ProgrammingError) table name "x" specified more than once

这是因为VALUES填充到FROM和JOIN块

>>> print(q)
SELECT comments.id AS comments_id, x.ordering as x_ordering
FROM (VALUES (1, 1), (3, 2), (2, 3), (4, 4)) AS x (id, ordering), comments
JOIN (VALUES (1, 1), (3, 2), (2, 3), (4, 4)) AS x (id, ordering) ON comments.id = x.id ORDER BY x.ordering

解决方案是从FROM语句中隐藏VALUES:

class values(FromClause):
named_with_column = True

def __init__(self, columns, *args, **kw):
    self._column_args = columns
    self.list = args
    self._hide_froms.append(self)  # This line fixes the above error
    self.alias_name = self.name = kw.pop('alias_name', None)

def _populate_column_collection(self):
    for c in self._column_args:
        c._make_proxy(self)

现在工作正常:

>>> q = session.query(Comment, c.x.id).join(x, Comment.id == x.c.id).order_by(x.c.ordering)
>>> print(q)
SELECT comments.id AS comments_id, x.ordering as x_ordering
FROM comments JOIN (VALUES (1, 1), (3, 2), (2, 3), (4, 4)) AS x (id, ordering) ON comments.id = x.id ORDER BY x.ordering