下面是一个简单的例子。最后的
CALL
因错误而失败
SQL 编译错误:表“EXAMPLE”上 FROM 子句中的存储过程中的子作业的语句类型“INSERT”无效
具体目的是调用一个存储过程,该存储过程 (a) 执行
INSERT
并 (b) 从另一个存储过程返回多个值,然后使用返回的值。
代码示例 1 - 失败:
--NB: This TABLE must exist for all Code Samples. Not repeated. ---
CREATE OR REPLACE TABLE EXAMPLE
(
ID_STRING VARCHAR(36) NOT NULL,
INSERT_TIMESTAMP TIMESTAMP_NTZ NOT NULL
);
CREATE OR REPLACE PROCEDURE EXAMPLE_WITH_INSERT()
RETURNS TABLE (
ID_STRING VARCHAR(36) ,
INSERT_TIMESTAMP TIMESTAMP_NTZ
)
LANGUAGE SQL
AS
$$
DECLARE
ID_STRING VARCHAR(36);
INSERT_TIMESTAMP TIMESTAMP_NTZ;
RESULT RESULTSET;
BEGIN
ID_STRING:=UUID_STRING();
INSERT_TIMESTAMP:=CURRENT_TIMESTAMP();
INSERT INTO EXAMPLE(ID_STRING,INSERT_TIMESTAMP) SELECT :ID_STRING,:INSERT_TIMESTAMP; -- Appears to be causing the error later on...
RESULT :=(SELECT :ID_STRING ID_STRING,:INSERT_TIMESTAMP INSERT_TIMESTAMP);
RETURN TABLE(RESULT);
END
$$
;
CREATE OR REPLACE PROCEDURE CALLER_WITH_INSERT()
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
DECLARE
ID_STRING VARCHAR(36);
INSERT_TIMESTAMP TIMESTAMP_NTZ;
BEGIN
SELECT ID_STRING,INSERT_TIMESTAMP INTO ID_STRING,INSERT_TIMESTAMP FROM TABLE(EXAMPLE_WITH_INSERT()) ;
RETURN CONCAT(ID_STRING,'@',INSERT_TIMESTAMP::VARCHAR) ;
END
$$
;
CALL CALLER_WITH_INSERT(); -- Fails "Invalid statement type 'INSERT' for child job from stored procedure in FROM clause on TABLE
代码示例 2 - 成功(但不执行
INSERT
)。不适合。
CREATE OR REPLACE PROCEDURE EXAMPLE_NO_INSERT()
RETURNS TABLE (
ID_STRING VARCHAR(36) ,
INSERT_TIMESTAMP TIMESTAMP_NTZ
)
LANGUAGE SQL
AS
$$
DECLARE
ID_STRING VARCHAR(36);
INSERT_TIMESTAMP TIMESTAMP_NTZ;
RESULT RESULTSET;
BEGIN
ID_STRING:=UUID_STRING();
INSERT_TIMESTAMP:=CURRENT_TIMESTAMP();
-- INSERT removed.
RESULT :=(SELECT :ID_STRING ID_STRING,:INSERT_TIMESTAMP INSERT_TIMESTAMP);
RETURN TABLE(RESULT);
END
$$
;
CREATE OR REPLACE PROCEDURE CALLER_NO_INSERT()
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
DECLARE
ID_STRING VARCHAR(36);
INSERT_TIMESTAMP TIMESTAMP_NTZ;
BEGIN
SELECT ID_STRING,INSERT_TIMESTAMP INTO ID_STRING,INSERT_TIMESTAMP FROM TABLE(EXAMPLE_NO_INSERT()) ;
RETURN CONCAT(ID_STRING,'@',INSERT_TIMESTAMP::VARCHAR) ;
END
$$
;
CALL CALLER_NO_INSERT();
我的解决方法是返回一个标量
OBJECT
保存返回值。
代码示例 3 - 有效。有更好的办法吗?
CREATE OR REPLACE PROCEDURE EXAMPLE_WITH_INSERT_WORKAROUND()
RETURNS OBJECT
LANGUAGE SQL
AS
$$
DECLARE
ID_STRING VARCHAR(36);
INSERT_TIMESTAMP TIMESTAMP_NTZ;
BEGIN
ID_STRING:=UUID_STRING();
INSERT_TIMESTAMP:=CURRENT_TIMESTAMP();
INSERT INTO EXAMPLE(ID_STRING,INSERT_TIMESTAMP) SELECT :ID_STRING,:INSERT_TIMESTAMP;
RETURN OBJECT_CONSTRUCT('ID_STRING',:ID_STRING,'INSERT_TIMESTAMP',:INSERT_TIMESTAMP);
END
$$
;
CREATE OR REPLACE PROCEDURE CALLER_WITH_INSERT_WORKAROUND()
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
DECLARE
RESULT OBJECT;
BEGIN
CALL EXAMPLE_WITH_INSERT_WORKAROUND() INTO RESULT ;
RETURN CONCAT(RESULT:ID_STRING,'@',RESULT:INSERT_TIMESTAMP::VARCHAR) ;
END
$$
;
CALL CALLER_WITH_INSERT_WORKAROUND();
以下示例显示,可以调用存储过程
EXAMPLE_WITH_INSERT()
,但其 RETURN
值(据我所知)并未“处理”为 TABLE
,尽管(很好)被声明为 TABLE
。然而,存储过程EXAMPLE_NO_INSERT()
没有同样的问题。
CALL EXAMPLE_NO_INSERT();
SELECT CONCAT(ID_STRING,'@',INSERT_TIMESTAMP) FORMTTED FROM TABLE(EXAMPLE_NO_INSERT());
CALL EXAMPLE_WITH_INSERT();
SELECT CONCAT(ID_STRING,'@',INSERT_TIMESTAMP) FORMTTED FROM TABLE(EXAMPLE_WITH_INSERT()) -- FAILS
;
注意:所有建设性的可执行答案都会受到欢迎,并且很可能会被投票,即使我没有发现它们比我的解决方法更好。有很多方法可以给这只猫剥皮!
可以从外部存储过程中的内部存储过程中“拦截”结果集:
CREATE OR REPLACE PROCEDURE CALLER_INSERT()
RETURNS TEXT
LANGUAGE SQL
AS
DECLARE
ID_STRING VARCHAR(36);
INSERT_TIMESTAMP TIMESTAMP_NTZ;
RES RESULTSET;
BEGIN
res := (CALL EXAMPLE_NO_INSERT());
LET cur1 CURSOR FOR res;
-- assumption that single row is returned
FOR rw IN cur1 DO
ID_STRING := rw.ID_STRING;
INSERT_TIMESTAMP := rw.INSERT_TIMESTAMP;
END FOR;
RETURN CONCAT(ID_STRING,'@',INSERT_TIMESTAMP::TEXT) ;
END ;
要点:
res := (CALL EXAMPLE_NO_INSERT());
LET cur1 CURSOR FOR res;
FOR rw IN cur1 DO ... END FOR
内部存储过程:
CREATE OR REPLACE PROCEDURE EXAMPLE_NO_INSERT()
RETURNS TABLE (
ID_STRING VARCHAR(36) ,
INSERT_TIMESTAMP TIMESTAMP_NTZ
)
LANGUAGE SQL
AS
DECLARE
ID_STRING VARCHAR(36);
INSERT_TIMESTAMP TIMESTAMP_NTZ;
RESULT RESULTSET;
BEGIN
ID_STRING:=UUID_STRING();
INSERT_TIMESTAMP:=CURRENT_TIMESTAMP();
INSERT INTO EXAMPLE(ID_STRING,INSERT_TIMESTAMP) SELECT :ID_STRING,:INSERT_TIMESTAMP;
RESULT :=(SELECT :ID_STRING ID_STRING,:INSERT_TIMESTAMP INSERT_TIMESTAMP);
RETURN TABLE(RESULT);
END;
输出: