【问题标题】:cartesian product unbounded number of groups in presto sqlpresto sql中的笛卡尔积无限数量的组
【发布时间】:2021-07-15 03:55:00
【问题描述】:

给定一个像这样的表

     Key        |   Value
----------------+-------------
 A              | 1
 A              | 2
 B              | 1
 B              | 2
 C              | 1
 C              | 2

如何使用SQL 生成所有(keys, value) 的乘积?

这是所需的输出:

   Group |   Key        |   Value
---------+--------------+----
 1       |A             | 1
 1       |B             | 1
 1       |C             | 1
 2       |A             | 1
 2       |B             | 1
 2       |C             | 2
 3       |A             | 1
 3       |B             | 2
 3       |C             | 1
 4       |A             | 1
 4       |B             | 2
 4       |C             | 2
 5       |A             | 2
 5       |B             | 2
 5       |C             | 2
 6       |A             | 2
 6       |B             | 1
 6       |C             | 2
 7       |A             | 2
 7       |B             | 2
 7       |C             | 1
 8       |A             | 2
 8       |B             | 1
 8       |C             | 1

为了说明目的,我可以在Python 中这样做,使用:

In [1]: from itertools import product
In [2]: list(product([('A',1), ('A', 2)], [('B', 1), ('B', 2)], [('C', 1), ('C', 2)]))
Out[2]:
[(('A', 1), ('B', 1), ('C', 1)),
 (('A', 1), ('B', 1), ('C', 2)),
 (('A', 1), ('B', 2), ('C', 1)),
 (('A', 1), ('B', 2), ('C', 2)),
 (('A', 2), ('B', 1), ('C', 1)),
 (('A', 2), ('B', 1), ('C', 2)),
 (('A', 2), ('B', 2), ('C', 1)),
 (('A', 2), ('B', 2), ('C', 2))]

在我的实际应用程序中,(key, value) 对的数量并没有提前固定。

【问题讨论】:

  • 您使用的是哪个版本的 Presto?键的数量是固定的吗?
  • Trino 版本 340(2020 年 8 月 8 日)。而且key的个数不固定,每个key的value个数固定为10个。

标签: sql presto cartesian-product trino


【解决方案1】:

您可以使用递归 CTE 来做到这一点——但将值放在单个列中更简单:

with recursive kv as (
      select key, value, dense_rank() over (order by key) as seqnum
      from t
     ),
     cte(kv, seqnum) as (
      select array[row(key, value)] as kv, 1 as seqnum
      from kv
      where seqnum = 1
      union all
      select kv || row(kv.key, kv.value), kv.seqnum
      from cte join
           kv
           on kv.seqnum = kv + 1      
     )
select cte.*, row_number() over (order by seqnum)
from cte;

如果愿意,您可以取消嵌套数组以获得单独的行。

很遗憾,我手头没有 Presto/Trino 来检查语法。

【讨论】:

  • Presto missing column aliases in recursive WITH query 遇到错误
【解决方案2】:

没有直接的方法可以满足您的要求,但这是一种可能的方法:

WITH t(key, value) AS (
    VALUES
     ('A', 1),
     ('A', 2),
     ('B', 1),
     ('B', 2),
     ('C', 1),
     ('C', 2)
),
grouped AS (
   SELECT
       combinations(array_agg(ROW(key, value)), 3) combinations,
       count(distinct key) key_count
   FROM t
),
combinations AS (
    SELECT row_number() OVER () AS "group", combination
    FROM grouped CROSS JOIN UNNEST(combinations) AS u(combination)
    WHERE cardinality(array_distinct(transform(combination, e -> e[1]))) = key_count
),
unnested AS (
    SELECT "group", key, value
    FROM combinations, UNNEST(combination) AS u(key, value)
    ORDER BY "group", "key"
)
SELECT * FROM unnested

=>

 group | key | value
-------+-----+-------
     1 | A   |     1
     1 | B   |     1
     1 | C   |     1
     2 | A   |     1
     2 | B   |     2
     2 | C   |     1
     3 | A   |     2
     3 | B   |     1
     3 | C   |     1
     4 | A   |     2
     4 | B   |     2
     4 | C   |     1
     5 | A   |     1
     5 | B   |     1
     5 | C   |     2
     6 | A   |     1
     6 | B   |     2
     6 | C   |     2
     7 | A   |     2
     7 | B   |     1
     7 | C   |     2
     8 | A   |     2
     8 | B   |     2
     8 | C   |     2
(24 rows)

它是这样工作的:

  1. 创建一个条目数组(即ROW(key, value))并计算不同键的总数
  • 使用combinations 函数从该数组的元素生成所有可能的三向组合。这会产生一些“无效”的组合:多次包含相同键的组合。
  • 过滤生成的组合以排除任何不包含所有键的组合。使用 row_number 窗口函数为每个剩余组合分配一个组 ID。
  • 从组合数组中取消嵌套单个键/值对以产生您期望的输出。

【讨论】:

  • 嗯。所以在我的设置中,键的数量是没有限制的。所以我不确定组合是否适用
  • 小于15,值也小于15
  • 顺便说一句,递归 CTE 在这种情况下会有所帮助吗?
  • 理论上可以,但是 Trino 中的递归 CTE 目前效率不高,并且有很多限制。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多