使用函数时雪花不支持的子查询

Posted

技术标签:

【中文标题】使用函数时雪花不支持的子查询【英文标题】:Snowflake unsupported subquery when using function 【发布时间】:2021-05-04 23:54:24 【问题描述】:

这是我创建的函数:

CREATE OR REPLACE FUNCTION NS_REPORTS.AP."COUPA_GET_EXCH_RATE"("from_curr_id" NUMBER(38,0), "to_curr_id" NUMBER(38,0), "date" DATE)
RETURNS FLOAT
LANGUAGE SQL
AS '
    SELECT 
        COALESCE((
            SELECT 
                RATE 
            FROM 
                (
                    SELECT
                        ROW_NUMBER() OVER (PARTITION BY DATE(RATE_DATE) ORDER BY RATE_DATE DESC) ROW_NUM
                        , RATE
                    FROM
                        CONNECTORS.COUPA.EXCHANGE_RATE
                    WHERE
                        FROM_CURRENCY_ID = from_curr_id
                        AND TO_CURRENCY_ID = to_curr_id
                        AND DATE(RATE_DATE) = date
                ) R
            WHERE
                ROW_NUM = 1
        ), 1)
';

我使用的是 ROW_NUMBER 函数,因为 RATE_DATE 字段实际上是日期时间,因此每个日期有多个记录。

当我自己调用该函数时,它工作正常。但是,当我尝试在视图中使用它时,会出现不支持的子查询类型错误。没有它,视图工作正常。谁能想到我可以做些什么来修复错误或通过重写查询来解决它?

编辑 1:查看代码和确切的错误消息

CREATE OR REPLACE VIEW COUPA_REQUISITION
AS
SELECT
    RH.ID REQ_NUM
    , RL.LINE_NUM REQ_LINE_NUM
    , OH.PO_NUMBER 
    , REPLACE(REPLACE(OH.CUSTOM_FIELDS:"legacy-po-number", '"', ''), '.0', '') LEGACY_PO_NUMBER
    , S."NAME" SUPPLIER
    , OH.STATUS 
    , UR.FULLNAME REQUESTED_BY
    , UC.FULLNAME CREATED_BY
    , OL.RECEIVED
    , DATE(RH.SUBMITTED_AT) ORDER_DATE
    , DATE(RH.NEED_BY_DATE) NEEDED_BY_DATE
    , RL."DESCRIPTION" ITEM
    , CAST(NULL AS VARCHAR) CHART_OF_ACCOUNTS
    , REPLACE(OH.CUSTOM_FIELDS:"purchase-type", '"', '') PURCHASE_TYPE
    , COM."NAME" COMMODITY
    , ACT.NS_SUB_NAME SUBSIDIARY
    , ACT.NS_ACCT_NAME_FULL "ACCOUNT"
    , ACT.NS_DEPT_NAME_FULL DEPARTMENT
    , ACT.NS_L3_DEPT_NAME L3_DEPARTMENT
    , ACT.NS_LOC_NAME "LOCATION"
    , RL.QUANTITY QTY
    , OL.LINE_NUM ORDER_LINE_NUM
    , RL.TOTAL * NS_REPORTS.AP.COUPA_GET_EXCH_RATE(RL.CURRENCY_ID, 1, DATE(RH.SUBMITTED_AT)) LINE_TOTAL
    , RL.TOTAL - OL.INVOICED UNINVOICED_AMOUNT
    , OL.INVOICED INVOICED_TOTAL
    , RLSUM.TOTAL TOTAL
    , REPLACE(IL.CUSTOM_FIELDS:"amortization-schedule"."name", '"', '') AMORTIZATION_SCHEDULE
    , CASE WHEN COALESCE(IL.CUSTOM_FIELDS:"amortization-start-date", '') <> '' THEN DATE(REPLACE(IL.CUSTOM_FIELDS:"amortization-start-date", '"', '')) ELSE NULL END AMORTIZATION_START_DATE
    , CASE WHEN COALESCE(IL.CUSTOM_FIELDS:"amortization-end-date", '') <> '' THEN DATE(REPLACE(IL.CUSTOM_FIELDS:"amortization-end-date", '"', '')) ELSE NULL END AMORTIZATION_END_DATE
    , CASE WHEN COALESCE(OH.CUSTOM_FIELDS:"contract-start-date", '') <> '' THEN DATE(REPLACE(OH.CUSTOM_FIELDS:"contract-start-date", '"', '')) ELSE NULL END CONTRACT_START_DATE
    , CASE WHEN COALESCE(OH.CUSTOM_FIELDS:"contract-end-date", '') <> '' THEN DATE(REPLACE(OH.CUSTOM_FIELDS:"contract-end-date", '"', '')) ELSE NULL END CONTRACT_END_DATE
FROM 
    CONNECTORS.COUPA.REQUISITION_HEADER RH
    JOIN CONNECTORS.COUPA.REQUISITION_LINE RL ON RL.REQUISITION_HEADER_ID = RH.ID
    JOIN NS_REPORTS.AP.COUPA_ACCOUNT ACT ON ACT.COUPA_ACCT_ID = RL.ACCOUNT_ID
    JOIN CONNECTORS.COUPA."USER" UR ON UR.ID = RH.REQUESTED_BY_ID 
    JOIN CONNECTORS.COUPA."USER" UC ON UC.ID = RH.CREATED_BY_ID 
    JOIN (
        SELECT
            REQUISITION_HEADER_ID 
            , SUM(TOTAL) TOTAL
        FROM
            CONNECTORS.COUPA.REQUISITION_LINE 
        GROUP BY
            REQUISITION_HEADER_ID 
    ) RLSUM ON RLSUM.REQUISITION_HEADER_ID = RH.ID
    LEFT JOIN CONNECTORS.COUPA.ORDER_LINE OL ON OL.ID = RL.ORDER_LINE_ID 
    LEFT JOIN CONNECTORS.COUPA.ORDER_HEADER OH ON OH.ID = OL.ORDER_HEADER_ID 
    LEFT JOIN CONNECTORS.COUPA.COMMODITY COM ON COM.ID = OL.COMMODITY_ID  
    LEFT JOIN CONNECTORS.COUPA.SUPPLIER S ON S.ID = OH.SUPPLIER_ID 
    LEFT JOIN CONNECTORS.COUPA.INVOICE_LINE IL ON IL.ORDER_LINE_ID = OL.ID 

错误信息: SQL 错误 [2031] [42601]:SQL 编译错误: 无法评估不受支持的子查询类型

【问题讨论】:

我知道雪花错误消息不是很有帮助,但您能否分享一下您的视图是什么样的以及确切的错误消息是什么? 【参考方案1】:

错误是相关子查询,不支持(除了一些小玩具示例)

但基本形式是

SELECT a.a
       (select b.b from b where b.a = a.a order by b.y limit 1)
FROM a;

对于每一行,子查询都会在另一个表上运行以获取值。在其他数据库中做了很多技巧来使这个“工作”,但实际上每一行都有工作。 Snowflake 不会对每行操作进行类型化。

好消息是其他模式实际上是相同的,雪花确实支持,这两种模式实际上是相同的,使用 CTE 或加入子选择,这是同一件事。

所以上面变成:

WITH subquery AS (
    SELECT b.a, b.b FROM b
    QUALIFY row_number() over (partition by b.a order by b.y) = 1
)
SELECT a.a
    sq.b
FROM a
JOIN subquery AS sq 
    ON sq.a = a.a

所以我们首先处理/塑造来自其他/子表的“所有记录”,并且我们只保留具有我们想要的计数/形状的行,然后加入该结果。是非常可并行化的,所以性能很好。 Snowflake 没有为您自动翻译子查询的原因是,它很容易出错,而且他们目前正在那里进行开发工作,致力于研究根本不存在的功能等,并且可以通过以下方式重写你,只要你了解你的模型。

【讨论】:

将此标记为答案,因为它与我最终所做的非常相似。感谢大家的帮助。【参考方案2】:

如果将其移至FROM 子句会怎样?我将其表述为:

SELECT COALESCE(MAX(er.rate), 1)
FROM (SELECT er.*
      FROM CONNECTORS.COUPA.EXCHANGE_RATE er
      WHERE er.FROM_CURRENCY_ID = in_from_curr_id AND
            er.TO_CURRENCY_ID = in_to_curr_id AND
            DATE(er.RATE_DATE) = in_date
      ORDER BY RATE_DATE DESC
      LIMIT 1
     ) er;

请注意,我更改了参数的名称,因此它们更明显是输入参数。

【讨论】:

不过,我做不到 MAX(er.rate)。 MAX(如果有)必须在 er.rate_date 上。否则我会得到最大的利率,而不是最近的。 @MikeCaputo 。 . .子查询只返回一行,所以MAX() 并没有真正做任何事情。只保证结果集中只有一行。 这样做我得到了同样的不受支持的子查询类型错误。 @MikeCaputo 。 . .我在想将逻辑移到 FROM 子句可能会解决问题。

以上是关于使用函数时雪花不支持的子查询的主要内容,如果未能解决你的问题,请参考以下文章

雪花不支持的子查询类型无法在 UDF 标量中评估

雪花中不支持的子查询

雪花标量 UDF 返回 无法评估不支持的子查询类型

雪花:无法评估不受支持的子查询类型

替换雪花中的子查询

SQL 编译错误:无法评估不受支持的子查询类型 - SELECT 子句中的函数调用