在 sqlalchemy 中选择一个 Join 给出了太多的行

Selecting a Join in sqlalchemy gives too many rows

我正在尝试构建一个复合 SQL 查询,该查询从我之前执行的连接构建 table。 (将 SqlAlchemy(核心部分)与 python3 和 Postgresql 9.4 结合使用)

我在此处包含了我的 python3 代码的相关部分。我首先使用 select 和 group_by 创建 "in_uuid_set"。然后我加入 "in_uuid_set" 和 "in_off_messages" 得到 "jn_in"。 最后,我尝试通过 selecting 并生成所需的列来从 "jn_in" 构建一个新的 table "incoming":

in_uuid_set = \
    sa.select([in_off_messages.c.src_uuid.label('remote_uuid')])\
    .select_from(in_off_messages)\
    .where(in_off_messages.c.dst_uuid == local_uuid)\
    .group_by(in_off_messages.c.src_uuid)\
    .alias()


jn_in = in_uuid_set.join(in_off_messages,\
    and_(\
        in_off_messages.c.src_uuid == in_uuid_set.c.remote_uuid,\
        in_off_messages.c.dst_uuid == local_uuid,\
        ))\
    .alias()


incoming = sa.select([\
    in_off_messages.c.msg_uuid.label('msg_uuid'),\
    in_uuid_set.c.remote_uuid.label('remote_uuid'),\
    in_off_messages.c.msg_type.label('msg_type'),\
    in_off_messages.c.date_sent.label('date_sent'),\
    in_off_messages.c.content.label('content'),\
    in_off_messages.c.was_read.label('was_read'),\
    true().label('is_incoming')]
    )\
    .select_from(jn_in)

令人惊讶的是,我发现 "incoming" 的行数比 "jn_in" 多。 "incoming" 有 12 行,而 "jn_in" 只有 2 行。我希望 "incoming" 的行数 (2) 与 "jn_in".

相同

我还在此处包括 SqlAlchemy 为 "incoming":

生成的 SQL 输出
SELECT in_off_messages.msg_uuid  AS msg_uuid,
       anon_1.remote_uuid        AS remote_uuid,
       in_off_messages.msg_type  AS msg_type,
       in_off_messages.date_sent AS date_sent,
       in_off_messages.content   AS content,
       in_off_messages.was_read  AS was_read,
       1                         AS is_incoming
FROM   in_off_messages,
       (SELECT in_off_messages.src_uuid AS remote_uuid
        FROM   in_off_messages
        WHERE  in_off_messages.dst_uuid = :dst_uuid_1
        GROUP  BY in_off_messages.src_uuid) AS anon_1,
       (SELECT anon_1.remote_uuid            AS anon_1_remote_uuid,
               in_off_messages.msg_uuid      AS in_off_messages_msg_uuid,
               in_off_messages.orig_src_uuid AS in_off_messages_orig_src_uuid,
               in_off_messages.src_uuid      AS in_off_messages_src_uuid,
               in_off_messages.dst_uuid      AS in_off_messages_dst_uuid,
               in_off_messages.msg_type      AS in_off_messages_msg_type,
               in_off_messages.date_sent     AS in_off_messages_date_sent,
               in_off_messages.content       AS in_off_messages_content,
               in_off_messages.was_read      AS in_off_messages_was_read
        FROM   (SELECT in_off_messages.src_uuid AS remote_uuid
                FROM   in_off_messages
                WHERE  in_off_messages.dst_uuid = :dst_uuid_1
                GROUP  BY in_off_messages.src_uuid) AS anon_1
               JOIN in_off_messages
                 ON in_off_messages.src_uuid = anon_1.remote_uuid
                    AND in_off_messages.dst_uuid = :dst_uuid_2) AS anon_2 

这个 SQL 输出对我来说有些不对劲,主要是因为我看到 GROUP BY 的次数太多了。我本以为它会出现一次,但在这里似乎出现了两次。

我的猜测是某些大括号以某种方式错位了(在生成的 SQL 中)。我还怀疑我对 alias() 做错了什么,尽管我不确定。

我应该怎么做才能得到想要的结果("jn_in" 和 "incoming" 的行数相同)?

玩了一会儿代码后,我找到了修复它的方法。 答案最终与 alias() 有关。 为了使这项工作,应该省略第二个 alias() (jn_in),像这样:

in_uuid_set = \
    sa.select([in_off_messages.c.src_uuid.label('remote_uuid')])\
    .select_from(in_off_messages)\
    .where(in_off_messages.c.dst_uuid == local_uuid)\
    .group_by(in_off_messages.c.src_uuid)\
    .alias()


jn_in = in_uuid_set.join(in_off_messages,\
    and_(\
        in_off_messages.c.src_uuid == in_uuid_set.c.remote_uuid,\
        in_off_messages.c.dst_uuid == local_uuid,\
        ))
# <<< The alias() is gone >>>



incoming = sa.select([\
    in_off_messages.c.msg_uuid.label('msg_uuid'),\
    in_uuid_set.c.remote_uuid.label('remote_uuid'),\
    in_off_messages.c.msg_type.label('msg_type'),\
    in_off_messages.c.date_sent.label('date_sent'),\
    in_off_messages.c.content.label('content'),\
    in_off_messages.c.was_read.label('was_read'),\
    true().label('is_incoming')]
    )\
    .select_from(jn_in)

然而,第一个 alias() (in_uuid_set) 似乎不能省略。如果我试图忽略它,我会收到此错误消息:

E               subquery in FROM must have an alias
E               LINE 2: FROM (SELECT in_off_messages.src_uuid AS remote_uuid 
E                            ^
E               HINT:  For example, FROM (SELECT ...) [AS] foo.

作为对此的概括,如果你有一个 select 想作为子句放在其他地方,那么你想要别名()它,但是如果你有一个你想要的连接作为一个子句,你不应该 alias() 它。

为了完整起见,我在此处包含新代码的结果 SQL:

SELECT in_off_messages.msg_uuid  AS msg_uuid,
       anon_1.remote_uuid        AS remote_uuid,
       in_off_messages.msg_type  AS msg_type,
       in_off_messages.date_sent AS date_sent,
       in_off_messages.content   AS content,
       in_off_messages.was_read  AS was_read,
       1                         AS is_incoming
FROM   (SELECT in_off_messages.src_uuid AS remote_uuid
        FROM   in_off_messages
        WHERE  in_off_messages.dst_uuid = :dst_uuid_1
        GROUP  BY in_off_messages.src_uuid) AS anon_1
       JOIN in_off_messages
         ON in_off_messages.src_uuid = anon_1.remote_uuid
            AND in_off_messages.dst_uuid = :dst_uuid_2  

比问题中的短得多。