This is the function I created:
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) ';
I’m using the ROW_NUMBER function because the RATE_DATE field is actually datetime and so there are multiple records per date.
When I call the function by itself, it works fine. However, when I try to use it in a view, I get the unsupported subquery type error. The view works fine without it. Can anyone think of what I can do to either fix the error or work around it by rewriting the query?
EDIT 1: View code and exact error message
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
Error message: SQL Error [2031] [42601]: SQL compilation error: Unsupported subquery type cannot be evaluated
Advertisement
Answer
The error is a correlated subquery and the are not supported (beyond some tiny toy examples)
But the basic form is
SELECT a.a (select b.b from b where b.a = a.a order by b.y limit 1) FROM a;
in effect for each row, a sub-query is run on another table to get a value. There are many tricks done in other DB’s to make this “work” but in effect there is work done on each row. Snowflake does not types of for each row operations.
The good news is there are other patterns that are effectively the same, that snowflake does support, the two patterns are really the same use a CTE or join to a sub-select which is the same thing.
so the above becomes:
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
So we first process/shape “all records” from the other/sub table, and we only keep the rows that have the count/shape we want, and then join to that result. The is very parallelizable, so performs well. The reason Snowflake does not auto translate a sub-query for you, is it rather easy to get it wrong, and they presently are spending there development efforts working on features that do not exist at all, etc etc, and it can be rewritten by you, given you understand your model.