SQL数组展平:为什么CROSS JOIN UNNEST不会为每一行加入每个嵌套值?

时间:2017-12-06 09:05:49

标签: sql google-bigquery

这个问题不是要解决一个特定的问题,而是要了解用于压扁数组的常见SQL习惯用法背后的实际情况。幕后有一些魔力,我想偷看句法糖的幕后,看看发生了什么。

让我们考虑下表t1

t1

现在让我们假设我们有一个名为FLATTEN的函数,它接受一个类型数组的列并解压缩该列中的每个数组,这样我们就为每个数组中的每个值留下一行 - 如果我们运行SELECT FLATTEN(numbers_array) AS flattened_numbers FROM t1,我们期待以下内容,我们称之为t2

t2

在SQL中,CROSS JOIN通过将第一个表中的每一行与第二个表中的每一行组合来组合来自两个表的行。因此,如果我们运行SELECT id, flattened.flattened_numbers from t1 CROSS JOIN flattened,我们就会获得

enter image description here

现在flatten只是一个虚构的函数,你可以看到将它与CROSS JOIN结合起来并不是很有用,因为id列的每个原始值都与flattened_numbers混合在一起来自每个原始行。由于我们没有WHERE子句只选择我们想要的CROSS JOIN行,所以一切都变得混乱了。

人们实际用于展平数组的模式如下所示: SELECT id, flattened_numbers FROM t1 CROSS JOIN UNNEST(sequences.some_numbers) AS flattened_numbers,生成

enter image description here

但我不明白为什么CROSS JOIN UNNEST模式确实有效。因为CROSS JOIN不包含WHERE子句,所以我希望它的行为就像我上面概述的FLATTEN函数一样,其中每个unnested值都与{{1 }}

有人可以“解包”t1模式中实际发生的事情,确保每一行只与其自己的嵌套值相连(而不是其他行的嵌套值)?

1 个答案:

答案 0 :(得分:6)

考虑这个问题的最佳方法是查看逐行发生的情况。设置一些输入数据,我们有:

WITH t1 AS (
  SELECT 1 AS id, [0, 1] AS numbers_array UNION ALL
  SELECT 2, [2, 4, 5]
)
...

(我在第二行使用第三个元素来使事情变得更有趣)。如果我们只是从中选择,我们得到如下输出:

WITH t1 AS (
  SELECT 1 AS id, [0, 1] AS numbers_array UNION ALL
  SELECT 2, [2, 4, 5]
)
SELECT * FROM t1;
+----+---------------+
| id | numbers_array |
+----+---------------+
| 1  | [0, 1]        |
| 2  | [2, 4, 5]     |
+----+---------------+

现在让我们谈谈取消。 UNNEST函数接受一个数组并返回数组元素类型的值表。大多数BigQuery表是定义为列集合的SQL表,而值表包含一些 value 类型的行。对于numbers_arrayUNNEST(numbers_array)返回值类型为INT64的值表,因为numbers_array是一个元素类型为INT64的数组。此值表包含numbers_arrayt1当前行的id中的所有元素。

对于UNNEST(numbers_array)为1的行,+-----+ | f0_ | +-----+ | 0 | | 1 | +-----+ 返回的值表的内容为:

SELECT * FROM UNNEST([0, 1]);

这与我们使用以下查询得到的相同:

UNNEST([0, 1])
在这种情况下,

INT64表示"根据01id"创建一个值表。

同样,对于UNNEST(numbers_array)为2的行,+-----+ | f0_ | +-----+ | 2 | | 4 | | 5 | +-----+ 返回的值表的内容为:

CROSS JOIN

现在让我们谈谈CROSS JOIN如何适应图片。在大多数情况下,您在两个不相关的表之间使用CROSS JOIN。换句话说,UNNEST右侧表格的内容未由左侧表格的当前内容定义。

对于数组和UNNEST(numbers_array)t1生成的值表的内容会根据t1的当前行而更改。当我们加入这两个表时,我们得到UNNEST(numbers_array)的当前行与WITH t1 AS ( SELECT 1 AS id, [0, 1] AS numbers_array UNION ALL SELECT 2, [2, 4, 5] ) SELECT id, number FROM t1 CROSS JOIN UNNEST(numbers_array) AS number; +----+--------+ | id | number | +----+--------+ | 1 | 0 | | 1 | 1 | | 2 | 2 | | 2 | 4 | | 2 | 5 | +----+--------+ 的所有行的叉积。例如:

numbers_array

2 + 3 = 5在第一行有两个元素,第二行有三个元素,因此我们在查询结果中得到numbers_array行。

要回答有关如何与展开CROSS JOIN然后执行WITH t1 AS ( SELECT 1 AS id, [0, 1] AS numbers_array UNION ALL SELECT 2, [2, 4, 5] ), t2 AS ( SELECT number FROM t1 CROSS JOIN UNNEST(numbers_array) AS number ) SELECT number FROM t2; +--------+ | number | +--------+ | 0 | | 1 | | 2 | | 4 | | 5 | +--------+ 不同的问题,请查看此查询的结果:< / p>

t2

在这种情况下,number是一个SQL表,其中包含名为CROSS JOIN的列,并带有这些值。如果我们在t1t2之间执行WITH t1 AS ( SELECT 1 AS id, [0, 1] AS numbers_array UNION ALL SELECT 2, [2, 4, 5] ), t2 AS ( SELECT number FROM t1 CROSS JOIN UNNEST(numbers_array) AS number ) SELECT id, numbers_array, number FROM t1 CROSS JOIN t2; +----+---------------+--------+ | id | numbers_array | number | +----+---------------+--------+ | 1 | [0, 1] | 0 | | 1 | [0, 1] | 1 | | 1 | [0, 1] | 2 | | 1 | [0, 1] | 4 | | 1 | [0, 1] | 5 | | 2 | [2, 4, 5] | 0 | | 2 | [2, 4, 5] | 1 | | 2 | [2, 4, 5] | 2 | | 2 | [2, 4, 5] | 4 | | 2 | [2, 4, 5] | 5 | +----+---------------+--------+ ,我们会得到所有行的真实叉积:

CROSS JOIN UNNEST(numbers_array)

那么这与前一个t2查询的区别是什么?在这种情况下,t1的内容不会因t1的每一行而发生变化。对于t2中的第一行,t1中有五行。对于t2中的第二行,CROSS JOIN中有五行。因此,两者之间的5 + 5 = 10总共返回import React from "react"; import { render } from "react-dom"; import { BrowserRouter as Router, Route, Link } from "react-router-dom"; const ParamsExample = () => ( <Router> <div> <h2>Accounts</h2> <ul> <li><Link to={`/${encodeURI("%netflix")}`}>Netflix</Link></li> <li><Link to="/zillow-group">Zillow Group</Link></li> <li><Link to="/yahoo">Yahoo</Link></li> <li><Link to="/modus-create">Modus Create</Link></li> </ul> <Route path="/:id" component={Child} /> </div> </Router> ) const Child = ({ match }) => ( <div> <h3>ID: {match.params.id}</h3> </div> ) render(<ParamsExample />, document.getElementById("react")); 行。