![](/img/trans.png)
[英]Does BigQuery guarantee column order when performing SELECT * query from subselect?
[英]BigQuery reproducing a query that use another table in a subselect
我无法在 BigQuery 中重现类似于以下 MSSQL 查询的查询:
SELECT
COL1,
COL2, COL3,
CASE
WHEN ( COL1 % 2 ) = 0 THEN COL2
ELSE (SELECT TOP 1 COL99 FROM ANOTHER_TABLE AS AT WHERE AT.COL8 = T.COL2 AND AT.COL9 < T.COL3 ORDER BY AT.COL9 DESC)
END AS COL4
FROM TABLE AS T
首先,我尝试重现 BQ 上的查询,如下所示:
SELECT
COL1,
COL2, COL3,
CASE
WHEN ( COL1 % 2 ) = 0 THEN COL2
ELSE (SELECT COL99 FROM PROJECT.DATASET.ANOTHER_TABLE AS AT WHERE AT.COL8 = T.COL2 AND AT.COL9 < T.COL3 ORDER BY AT.COL9 DESC LIMIT 1)
END AS COL4
FROM PROJECT.DATASET.TABLE AS T
但它会导致错误: Correlated subqueries that reference other tables are not supported unless they can be de-correlated, such as by transforming them into an efficient JOIN.
我可以理解这个错误,我同意原始查询不是很优化,因为可以对表中的每一行执行子查询。
知道我尝试了以下不会导致错误但给出错误(太多)结果的方法:
SELECT
COL1,
COL2, COL3,
CASE
WHEN ( COL1 % 2 ) = 0 THEN COL2
ELSE AT.COL99
END AS COL4
FROM PROJECT.DATASET.TABLE AS T
LEFT JOIN (
SELECT * FROM (
SELECT
COL99,
COL8,
COL9
ROW_NUMBER() OVER (PARITION BY COL8 ORDER BY COL9 DESC) AS rn
) AS TMP
/*WHERE TMP.rn = 1*/
) AS AT
ON AT.COL8 = T.COL2
AND AT.COL9 < T.COL3
此查询返回的行比预期的多,知道条件“AND AT.COL9 < T.COL3”是正常的,但我很难找出如何采用最小 ROW_NUMBER 值 (rn) 来重现原始查询的 TOP 1 .
我试图将TMP.rn = 1
放入 AT 表中,但问题是它并不总是第一个符合条件AND AT.COL9 < T.COL3
的值。
要恢复,我的目标是能够在 BigQuery 上重现此问题顶部的第一个查询,我已经尝试了一些但我正在阻止如何获取与条件AND AT.COL9 < T.COL3
匹配的 ROW_NUMBER (rn) 的最小值AND AT.COL9 < T.COL3
.
有没有人有过类似的用例?
编辑:添加输入和 output:
列1 | 列2 | 列3 |
---|---|---|
1234 | AAA级 | 25/12/2022 |
1235 | BBB | 25/12/2022 |
1236 | CCC 认证 | 25/12/2022 |
1337 | AAA级 | 24/12/2022 |
1238 | AAA级 | 23/12/2022 |
1239 | AAA级 | 22/12/2022 |
COL99 | COL8 | COL9 |
---|---|---|
1111 | AAA级 | 25/12/2022 |
2222 | BBB | 25/12/2022 |
3333 | CCC 认证 | 25/12/2022 |
9999 | AAA级 | 23/12/2022 |
8888 | AAA级 | 22/12/2022 |
7777 | AAA级 | 21/12/2022 |
列1 | 列2 | 列3 | 列4 |
---|---|---|---|
1234 | AAA级 | 25/12/2022 | AAA级 |
1235 | BBB | 25/12/2022 | NULL |
1236 | CCC 认证 | 25/12/2022 | CCC 认证 |
1237 | AAA级 | 24/12/2022 | 9999 |
1238 | AAA级 | 23/12/2022 | AAA级 |
1239 | AAA级 | 22/12/2022 | 7777 |
您可以使用FIRST_VALUE()
window function:
SELECT DISTINCT T.COL1, T.COL2, T.COL3,
CASE
WHEN T.COL1 % 2 = 0 THEN T.COL2
ELSE FIRST_VALUE(AT.COL99) OVER (PARTITION BY T.COL1, T.COL2, T.COL3 ORDER BY AT.COL9 DESC)
END AS COL4
FROM FIRST_TABLE AS T LEFT JOIN ANOTHER_TABLE AS AT
ON AT.COL8 = T.COL2 AND AT.COL9 < T.COL3 AND T.COL1 % 2 <> 0;
如果COL1
在第一个表中是唯一的,您可以将PARTITION BY
子句简化为:
OVER (PARTITION BY T.COL1 ORDER BY AT.COL9 DESC)
请参阅演示(对于 MySql,但它是标准 SQL)。
@forpas 提供的查询在我的示例中返回了良好的结果,但没有返回我在实际用例中等待的结果。
但是@forpas 的想法启发了我,我找到了解决问题的方法。
它在@forpas 提供的链接中给出了相同的结果,查询在 MySQL 中如下所示:
SELECT T.COL1, T.COL2, T.COL3,
CASE
WHEN T.COL1 % 2 = 0 THEN T.COL2
ELSE AT1.COL99
END AS COL4
FROM FIRST_TABLE AS T
LEFT JOIN (
SELECT * FROM (
SELECT
AT.COL99,
T.COL2,
T.COL3,
ROW_NUMBER() OVER (PARTITION BY T.COL3, T.COL2, AT.COL8 ORDER BY AT.COL9 DESC) AS COUNTER
FROM ANOTHER_TABLE AS AT
INNER JOIN FIRST_TABLE AS T
ON AT.COL8 = T.COL2 AND AT.COL9 < T.COL3) TEMP
WHERE TEMP.COUNTER = 1
) AS AT1
ON AT1.COL2 = T.COL2 AND AT1.COL3 = T.COL3 ;
查询可能很复杂,如果有人有更优化的东西,我会很乐意尝试。
谢谢@forpas 的提议!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.