根据同一张表中是不是存在相关行获取行

Posted

技术标签:

【中文标题】根据同一张表中是不是存在相关行获取行【英文标题】:Get rows based on the existence of related rows in the same table根据同一张表中是否存在相关行获取行 【发布时间】:2018-12-01 23:29:50 【问题描述】:

这就是我的数据的样子。如果我执行以下查询:

select * from gdax_trades where order_type='limit' limit 5;

我得到的回报是这样的:

 row_id  |               order_id               |  price  | funds | maker_order_id | taker_order_id | trade_id | product_id |              client_oid              | reason | remaining_size |    size    |  sequence  | side |          time           | order_type | event_type 
---------+--------------------------------------+---------+-------+----------------+----------------+----------+------------+--------------------------------------+--------+----------------+------------+------------+------+-------------------------+------------+------------
 3697499 | 01d63a5b-a5b7-4153-b93d-bd18c249d9c3 | 4113.06 |       |                |                |          | BTC-USD    | 50028bab-81da-4842-98f0-2a1206669567 |        |                |       0.01 | 7446101470 | buy  | 2018-11-29 04:15:39.047 | limit      | received
 3697501 | 9295111b-2e23-445c-9f52-52d2f26fb418 | 4131.93 |       |                |                |          | BTC-USD    | de58f4a6-4577-4680-b083-df34ade6c001 |        |                | 0.12792387 | 7446101472 | sell | 2018-11-29 04:15:39.071 | limit      | received
 3697504 | 4c09878d-8bf9-49d7-9fc7-ca81b7da9e42 | 4131.19 |       |                |                |          | BTC-USD    | a55e0315-8b65-4525-a7a7-debcf6f17bb5 |        |                | 0.10898271 | 7446101475 | sell | 2018-11-29 04:15:39.155 | limit      | received
 3697506 | 0a157570-a811-420e-81ff-0ead9cc34984 | 4132.69 |       |                |                |          | BTC-USD    | 45086077-34be-441e-947f-99fe60bd88ef |        |                | 0.12146031 | 7446101477 | sell | 2018-11-29 04:15:39.24  | limit      | received
 3697508 | e8e1d02f-e627-4eac-a2e5-61c08399d6ef | 4117.83 |       |                |                |          | BTC-USD    | 00000000-818a-0006-0001-000011037107 |        |                |      0.001 | 7446101479 | sell | 2018-11-29 04:15:39.259 | limit      | received
(5 rows)

表中还有其他行对应每个order_id,但没有order_type='limit'。例如,如果我尝试查找与第一个 order_id 对应的所有行:

select * from gdax_trades where order_id='01d63a5b-a5b7-4153-b93d-bd18c249d9c3';

我明白了:

 row_id  |               order_id               |  price  | funds | maker_order_id | taker_order_id | trade_id | product_id |              client_oid              |  reason  | remaining_size | size |  sequence  | side |          time           | order_type | event_type 
---------+--------------------------------------+---------+-------+----------------+----------------+----------+------------+--------------------------------------+----------+----------------+------+------------+------+-------------------------+------------+------------
 3697499 | 01d63a5b-a5b7-4153-b93d-bd18c249d9c3 | 4113.06 |       |                |                |          | BTC-USD    | 50028bab-81da-4842-98f0-2a1206669567 |          |                | 0.01 | 7446101470 | buy  | 2018-11-29 04:15:39.047 | limit      | received
 3697500 | 01d63a5b-a5b7-4153-b93d-bd18c249d9c3 | 4113.06 |       |                |                |          | BTC-USD    |                                      |          |           0.01 |      | 7446101471 | buy  | 2018-11-29 04:15:39.047 |            | open
 3697662 | 01d63a5b-a5b7-4153-b93d-bd18c249d9c3 | 4113.06 |       |                |                |          | BTC-USD    |                                      | canceled |           0.01 |      | 7446101633 | buy  | 2018-11-29 04:15:40.522 |            | done
(3 rows)

我想要的是一个 SQLAlchemy 查询,它返回给我的行带有对应于“限制”订单的order_id。我尝试进行自引用连接:

GDAXTradeAlias = aliased(GDAXTrade)

orders = (
    sess
    .query(GDAXTrade)
    .filter( GDAXTrade.time.between(start_dt, end_dt) )
    .filter(GDAXTrade.order_type=='limit')
    .join(GDAXTradeAlias, GDAXTrade.order_id==GDAXTradeAlias.order_id)
    .filter( GDAXTrade.time.between(start_dt, end_dt) )
    .all() )

但这并没有让我得到想要的结果。有人有什么建议吗?

【问题讨论】:

Postgres 版本和表定义(CREATE TABLE 语句)会有所帮助(而示例输出中的许多不相关的列则没有)。 【参考方案1】:

可能有很多方法。我建议使用EXISTS 半连接。可能是最快的一个非常清晰的阅读:

SELECT *
FROM   gdax_trades g
WHERE  EXISTS (
   SELECT FROM gdax_trades
   WHERE  order_type = 'limit'
   AND    order_id = g.order_id
   );

EXISTS 表达式的SELECT 列表可以保持为空。只有至少一行的存在是相关的。

在对同一张表进行两次寻址时,我们至少需要一个表别名(示例中为g)。我没有对引用子查询中本地表的列进行表限定,因为它首先是可见的。仅将对外部查询的引用限定为g.order_id。这是明确的最低要求。如果你愿意,你可以更明确。

在结果中包含“限价”订单。您可以通过添加 final 轻松排除它们:

...
WHERE order_type IS DISTINCT FROM 'limit'

IS DISTINCT FROM 因为order_type 似乎可以为空(不清楚您的示例结果中是'' 还是NULL)。 WHERE order_type <> 'limit' 将排除带有order_type IS NULL 的行。

查询从外部表返回唯一行,即使有多个“限价”订单具有相同的order_id。在这种情况下,具有连接或子查询的各种替代查询技术会返回重复项。相关:

How do I (or can I) SELECT DISTINCT on multiple columns?

【讨论】:

感谢您的回复!我还使用子查询发布了答案。我很好奇您是否认为您的解决方案会更高效? .between(start_dt, end_dt) 不谈,原则上存在功能差异:如果您的子查询发现超过 1 个具有相同 order_id 的“限制”订单,您会从连接中获得重复行,而 @987654340 @ 返回唯一的行 - 这也是它可能更快的原因:一旦找到相同的 order_id 的单行,它就会停止查找。如果order_id在“限价”订单中是唯一的,则不会有太大区别。我会为你的情况选择EXISTS【参考方案2】:

我使用子查询找到了答案。我很好奇人们对此有何看法

    sub_query = ( 
        sess
        .query(GDAXTrade)
        .filter( GDAXTrade.time.between(start_dt, end_dt) )
        .filter(GDAXTrade.order_type=='limit')
        .subquery() 
        )

    orders = (
        sess
        .query(GDAXTrade)
        .join(sub_query, GDAXTrade.order_id==sub_query.c.order_id, isouter=True)
        .filter(GDAXTrade.order_id==sub_query.c.order_id)
        .filter( GDAXTrade.time.between(start_dt, end_dt) )
        .order_by(GDAXTrade.time.asc())
        .all()
        )

【讨论】:

以上是关于根据同一张表中是不是存在相关行获取行的主要内容,如果未能解决你的问题,请参考以下文章

MySQL - 基于同一表中的行求和列值

根据同一张表的结果优化选择表中的所有行?

oracle数据库:表连接

INNER JOIN 同一张表

如何比较同一张表(SQL Server)中的 2 行?

在检查行是不是已存在时,选择不会从表中获取任何内容