【问题标题】:Multiple columns returned by subquery are not yet supported尚不支持子查询返回的多个列
【发布时间】:2019-09-11 02:30:57
【问题描述】:

给定下表:

transaction_id    user_id    product_id
             1         10            AA
             2         10            CC
             3         10            AA
             4         10            CC
             5         20            AA
             6         20            BB
             7         20            BB
             8         30            BB
             9         30            BB
            10         30            BB
            11         40            CC
            12         40            AA
            13         40            CC
            14         40            BB
            15         40            BB
            16         50            EE
            17         60            EE

使用以下查询:

select
  product_id,
  count(distinct user_id) as count_repeat_users
from
  product_usage_log
where
  (product_id, user_id) in (
    select
      product_id,
      user_id
    from (
      select
        product_id,
        user_id,
        count (distinct transaction_id) as transactions
      from
        product_usage_log
      group by
        product_id,
        user_id
    ) t
    where transactions >= 2
  )
group by product_id

返回以下结果:

product_id    count_repeat_users
        AA                     1
        BB                     3
        CC                     2

(note that 'EE' doesn't appear, as expected)

上述查询的目的是为每个产品返回至少与该产品进行过两次交易的用户计数。上面的查询满足了这一点,但是它使用了一个带有IN 谓词的多列子查询。此功能在 Presto 中不可用(虽然过去两年一直在讨论但没有成功)。

如果无法使用where (product_id, user_id) in (...),如何复制上述结果?

注意:我尝试将where 条件展平为两个连续的条件,问题是现在所有列匹配每一行的条件变成所有列匹配任何行的条件。换句话说,现在只要产品在子表中,它就会匹配用户-产品对,并且用户在子表中,但不一定在同一行。

因此,另一种表述问题的方式是:在 Presto 中,如何根据子查询中 SAME 行中存在的几个值来创建条件?

【问题讨论】:

    标签: sql presto


    【解决方案1】:

    如何在无法使用 where (product_id, user_id) in (...) 的情况下复制上述结果?

    这在 Presto 中直接可用。 您只需要将子查询生成的值包装在匿名ROWs 中,这样它们实际上就是单列。

    使用 Presto 318 进行测试:

    presto:default> SELECT
                 ->     x, y
                 -> FROM (VALUES (1,2), (3,4), (5,6)) t(x, y)
                 -> WHERE (x, y) IN (
                 ->     SELECT (z, w)
                 ->     FROM (VALUES (1,1), (3,4), (5,5)) u(z, w)
                 -> );
     x | y
    ---+---
     3 | 4
    (1 row)
    

    tpch.tiny 架构的另一个示例:

    presto:tiny> SELECT orderkey
              -> FROM orders
              -> JOIN customer ON orders.custkey = customer.custkey
              -> WHERE (orderkey, nationkey) IN (
              ->     SELECT (suppkey, nationkey) FROM supplier
              -> );
     orderkey
    ----------
            3
    (1 row)
    

    注意:我不完全确定这对于NULLs 是否正确。我想这对您来说不是问题,并且您的子查询不会为 product_id, user_id 生成 NULLs。

    【讨论】:

    • 这是我一直在寻找的解决方案。奇怪的是,我在文档中或在与无法从 in 谓词 (github.com/prestodb/presto/issues/6385) 所涉及的子查询中返回多列相关的问题中找不到它的任何引用。
    • 从技术上讲,这是解决方法,而不是解决方案。这里我 not 从子查询返回多个列。相反,我将结果打包到 row 类型的单个列中。 @Jivan,谈到这个问题——大多数参与其中的人现在都在建立 prestosql.io 社区。我鼓励你也加入。 Presto 相关的任何内容都有一个松弛组 (prestosql.io/community.html)。
    【解决方案2】:

    您可以使用窗口函数。我认为这会奏效:

    select product_id, count(distinct user_id)
    from (select pul.*,
                 count(*) over (partition by product_id, user_id) as cnt
          from product_usage_log pul
         ) pul
    where cnt >= 2
    group by product_id;
    

    根据您的示例数据,我猜transaction_id 是独一无二的。如果不是,则在子查询中使用count(distinct transaction_id)

    【讨论】:

      【解决方案3】:

      我看不出您使用 WHERE...IN... 的原因(至少从您的示例数据来看)。
      没有它,您可以获得所需的东西:

      select t.product_id, count(*) count_repeat_users
      from (
        select user_id, product_id
        from product_usage_log  
        group by user_id, product_id
        having count(transaction_id) > 1
      ) as t
      group by product_id  
      

      请参阅demo(对于 SQL Server,但由于代码是标准 SQL,它也应该适用于 Presto)。
      结果:

      product_id | count_repeat_users
      AA         |                  1
      BB         |                  3
      CC         |                  2
      

      【讨论】:

      • 谢谢。尽管您的解决方案确实解决了这种特殊情况,但我一直在寻找使用 Piotr Findeisen 提供的 where 子句的更通用的解决方案。
      猜你喜欢
      • 1970-01-01
      • 2018-09-21
      • 1970-01-01
      • 1970-01-01
      • 2015-03-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多