不稳定 "delayed" CTE 评估?
erratic "delayed" CTE evaluation?
我观察到 CTE 的行为出乎我的意料(而且看起来不一致)。
不太确定它是否正确...
基本上,通过 CTE,我过滤行以避免特定问题,然后使用该 CTE 的结果执行计算,这些计算会中断我认为我在 CTE 中消除的有问题的行...
以一个简单的 table 为例,其中包含一个 varchar 列,其中通常包含一个数字,但并非总是如此
CREATE TABLE MY_TABLE(ROW_ID INTEGER NOT NULL
, GOOD_ROW BOOLEAN NOT NULL
, SOME_VALUE VARCHAR NOT NULL);
INSERT INTO MY_TABLE(ROW_ID, GOOD_ROW, SOME_VALUE)
VALUES(1, TRUE, '1'), (2, TRUE, '2'), (3, FALSE, 'ABC');
我还创建了一个小的 table,只有数字可以加入
CREATE TABLE NUMBERS(NUMBER_ID INTEGER NOT NULL);
INSERT INTO NUMBERS(NUMBER_ID) VALUES(1), (2), (3);
在 SOME_VALUE 上连接这两个 table 会导致错误,因为 'ABC' 不是数字,而且看起来 JOIN 在 WHERE 子句之前计算(BAD 对此处性能的影响...)
SELECT *
FROM MY_TABLE
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE)
WHERE ROW_ID < 3; --> ERROR
所以,我尝试通过 CTE 过滤我的第一个 table,其中只有 return 行 SOME_VALUE 是数字
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE GOOD_ROW = TRUE
)
SELECT *
FROM ONLY_GOOD_ONES;
现在,我希望能够使用这个 CTE 的结果,SOME_VALUE 是数字。
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE GOOD_ROW = TRUE
)
SELECT *
FROM ONLY_GOOD_ONES
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE);
奇迹!!!
成功了!
我得到了我的 2 个预期记录。
到目前为止一切顺利...
但是,如果我对 CTE 的定义略有不同(过滤相同记录的 WHERE 子句)
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE ROW_ID < 3
)
SELECT *
FROM ONLY_GOOD_ONES;
此 CTE return与之前完全相同
但是如果我尝试加入,它会失败!
WITH ONLY_GOOD_ONES
AS (
SELECT *
FROM MY_TABLE
WHERE ROW_ID < 3
)
SELECT *
FROM ONLY_GOOD_ONES
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE);
我收到以下错误...
SQL Error [100038] [22018]: Numeric value 'ABC' is not recognized
对于第二版 CTE 的不同行为是否有特殊解释???
这是因为您针对不同的查询获得了不同的执行计划。
以下是使用工作查询执行查询的方式:
... 下面是它是如何在查询生成失败的情况下执行的。错误来自于这样一个事实,即与工作查询相比,在应用 ROW_ID < 3 过滤器之前,连接过滤器直接应用于 table 扫描。
您可以在历史记录下查看这些计划,单击查询 ID,然后单击 'profile' 选项卡。
似乎很早就应用了连接过滤器,可能是因为估计有误。当我 运行 在我的测试数据库上查询时,它们没有任何错误地完成了。
要解决这个问题,您可以随时 "Error-handling Conversion Functions":
SELECT *
FROM MY_TABLE
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TRY_TO_NUMBER(SOME_VALUE)
WHERE ROW_ID < 3;
更多信息:
https://docs.snowflake.com/en/sql-reference/functions-conversion.html#label-try-conversion-functions
实际答案是因为snowflake没有按照SQL标准,按照给出的顺序执行SQL。
当优化器决定需要时,他们在过滤之前将 t运行sforms 应用于数据。
所以当你 table MY_TABLE
SELECT some_value::NUMBER FROM my_table WHERE row_id IN (1,2);
在某些情况下,您会在所有行上发生 as_number 转换,并在 'ABC'
上爆炸。这违反了 SQL 规则,即在 SELECT t运行 表单完成之前对 WHERE 进行评估,但 Snowflake 多年来就知道这一点,这是故意的,因为它使事情变得 运行 更快。
解决方案是了解您有混合数据,因此假设代码可以而且将会 运行 乱序,因此使用 TRY_TO_NUMBER[=14 等函数的保护版本=]
最重要的是你可以写一些嵌套的 SELECTs 来避免这个问题,然后在代码周围放置类似 window 函数的东西,优化器跳回到这个行为,你 SQL又炸了。因此,解决方案是了解您是否有混合数据并进行处理。哦,抱怨这是一个错误。
我观察到 CTE 的行为出乎我的意料(而且看起来不一致)。 不太确定它是否正确...
基本上,通过 CTE,我过滤行以避免特定问题,然后使用该 CTE 的结果执行计算,这些计算会中断我认为我在 CTE 中消除的有问题的行...
以一个简单的 table 为例,其中包含一个 varchar 列,其中通常包含一个数字,但并非总是如此
CREATE TABLE MY_TABLE(ROW_ID INTEGER NOT NULL
, GOOD_ROW BOOLEAN NOT NULL
, SOME_VALUE VARCHAR NOT NULL);
INSERT INTO MY_TABLE(ROW_ID, GOOD_ROW, SOME_VALUE)
VALUES(1, TRUE, '1'), (2, TRUE, '2'), (3, FALSE, 'ABC');
我还创建了一个小的 table,只有数字可以加入
CREATE TABLE NUMBERS(NUMBER_ID INTEGER NOT NULL);
INSERT INTO NUMBERS(NUMBER_ID) VALUES(1), (2), (3);
在 SOME_VALUE 上连接这两个 table 会导致错误,因为 'ABC' 不是数字,而且看起来 JOIN 在 WHERE 子句之前计算(BAD 对此处性能的影响...)
SELECT *
FROM MY_TABLE
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE)
WHERE ROW_ID < 3; --> ERROR
所以,我尝试通过 CTE 过滤我的第一个 table,其中只有 return 行 SOME_VALUE 是数字
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE GOOD_ROW = TRUE
)
SELECT *
FROM ONLY_GOOD_ONES;
现在,我希望能够使用这个 CTE 的结果,SOME_VALUE 是数字。
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE GOOD_ROW = TRUE
)
SELECT *
FROM ONLY_GOOD_ONES
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE);
奇迹!!!
成功了! 我得到了我的 2 个预期记录。 到目前为止一切顺利...
但是,如果我对 CTE 的定义略有不同(过滤相同记录的 WHERE 子句)
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE ROW_ID < 3
)
SELECT *
FROM ONLY_GOOD_ONES;
此 CTE return与之前完全相同
但是如果我尝试加入,它会失败!
WITH ONLY_GOOD_ONES
AS (
SELECT *
FROM MY_TABLE
WHERE ROW_ID < 3
)
SELECT *
FROM ONLY_GOOD_ONES
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE);
我收到以下错误...
SQL Error [100038] [22018]: Numeric value 'ABC' is not recognized
对于第二版 CTE 的不同行为是否有特殊解释???
这是因为您针对不同的查询获得了不同的执行计划。
以下是使用工作查询执行查询的方式:
... 下面是它是如何在查询生成失败的情况下执行的。错误来自于这样一个事实,即与工作查询相比,在应用 ROW_ID < 3 过滤器之前,连接过滤器直接应用于 table 扫描。
似乎很早就应用了连接过滤器,可能是因为估计有误。当我 运行 在我的测试数据库上查询时,它们没有任何错误地完成了。
要解决这个问题,您可以随时 "Error-handling Conversion Functions":
SELECT *
FROM MY_TABLE
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TRY_TO_NUMBER(SOME_VALUE)
WHERE ROW_ID < 3;
更多信息:
https://docs.snowflake.com/en/sql-reference/functions-conversion.html#label-try-conversion-functions
实际答案是因为snowflake没有按照SQL标准,按照给出的顺序执行SQL。
当优化器决定需要时,他们在过滤之前将 t运行sforms 应用于数据。
所以当你 table MY_TABLE
SELECT some_value::NUMBER FROM my_table WHERE row_id IN (1,2);
在某些情况下,您会在所有行上发生 as_number 转换,并在 'ABC'
上爆炸。这违反了 SQL 规则,即在 SELECT t运行 表单完成之前对 WHERE 进行评估,但 Snowflake 多年来就知道这一点,这是故意的,因为它使事情变得 运行 更快。
解决方案是了解您有混合数据,因此假设代码可以而且将会 运行 乱序,因此使用 TRY_TO_NUMBER[=14 等函数的保护版本=]
最重要的是你可以写一些嵌套的 SELECTs 来避免这个问题,然后在代码周围放置类似 window 函数的东西,优化器跳回到这个行为,你 SQL又炸了。因此,解决方案是了解您是否有混合数据并进行处理。哦,抱怨这是一个错误。