如何创建从子查询返回数组的查询?

时间:2016-11-28 20:43:50

标签: postgresql sqlalchemy

我有一个架构,其中包含公司的地址列表。对于报告,我需要对这些公司进行一些过滤,并包括它们所在的所有区域的列表。我要编写的查询的相关核心部分是:

 
SELECT name, ARRAY(SELECT DISTINCT state
                   FROM location
                   WHERE location.foo_id=foo.id)
FROM foo;

我用来测试它的基本代码非常简单(注意我使用PostgreSQL作为我的数据库):

import sqlalchemy as sa
from sqlalchemy.sql import distinct, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker


Base = declarative_base()


class Company(Base):
    __tablename__ = 'company'
    id = sa.Column(sa.Integer, sa.Sequence('company_id_seq'), primary_key=True)
    name = sa.Column(sa.Unicode)


class Location(Base):
    __tablename__ = 'location'
    id = sa.Column(sa.Integer, sa.Sequence('location_id_seq'), primary_key=True)
    company_id = sa.Column(sa.Integer, sa.ForeignKey(Company.id))
    company = relationship(Company, backref='locations')
    state = sa.Column(sa.Unicode, nullable=False)

engine = sa.create_engine('postgresql:///pytest', echo=False)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

session = Session()
for (name, states) in [
        ('Acme', ['Alabama', 'Georgia']),
        ('Example Inc', ['Florida', 'Florida']),
        ('Empty', [])]:
    session.add(Company(
        name=name,
        locations=[Location(state=s) for s in states]))
session.flush()

# Desired result:
#
#  [('Acme', ['Alabama', 'Georgia']),
#   ('Example Inc', ['Florida']),
#   ('Empty', [])]

states_per_foo = session.query(distinct(Location.state))\
    .filter(Location.company_id == Company.id)
search_query = session.query(Company, func.array(states_per_foo))
print(search_query.all())

到目前为止,我尝试的所有排列都会因各种SQLAlchemy错误或生成的无效SQL而失败。

2 个答案:

答案 0 :(得分:3)

代码如下:

sq = (
    session
    .query(Location.state.distinct())
    .filter(Location.company_id == Company.id)
    .correlate(Company)
    .as_scalar()
)
q = session .query(Company.name, func.array(sq).label("states"))

准确地生成(忽略一些额外的括号)您要编写的SQL查询:

SELECT company.name,
       array(
               (SELECT DISTINCT location.state AS anon_1
                FROM location
                WHERE location.company_id = company.id)) AS states
FROM company

结果是:

('Acme', ['Georgia', 'Alabama'])
('Example Inc', ['Florida'])
('Empty', [])

原始版本: 轻微修改:添加.distinct()以删除重复项。

不是您要查找的SQL查询,而是以下查询的结果:

q = (
    session
    .query(Company.name, func.array_agg(Location.state.distinct()))
    .outerjoin(Location)
    .group_by(Company.name)
)
for x in q.all():
    print(x)

是(由于分组而按Company.name排序):

('Acme', ['Alabama', 'Georgia'])
('Empty', [None])
('Example Inc', ['Florida'])

请注意[None],而不是[] Empty。这也可以通过特殊方式处理。

答案 1 :(得分:0)

import sqlalchemy as sa
engine = sa.create_engine('postgresql://...')
result = engine.execute('''SELECT name, 
                               ARRAY(SELECT DISTINCT state
                                     FROM location
                                     WHERE location.foo_id=foo.id)
                           FROM foo''')
rows = result.fetchall()
for row in rows:
   print(type(row[0]))  # <class 'str'>
   print(row[0])        # Acme
   print(type(row[1]))  # <class 'list'>
   print(row[1])        # ['Alabama', 'Georgia']