2

假设我在脚本计算视图中有一些 SQL 脚本,该脚本采用单个值输入参数并为另一个计算视图中的输入参数生成多个输入字符串。

BEGIN 
declare paramStr clob;
params = select foo 
         from bar 
         where bar.id = :IP_ID;

select '''' || string_agg(foo, ''', ''') || ''''
into paramStr 
from :params;

var_out = select * 
          from  "_SYS_BIC"."somepackage/MULTIPLE_IP_VIEW"(PLACEHOLDER."$$IP_IDS$$" => :paramStr);
END

这按预期工作。但是,如果我更改var_out查询并尝试在 where 子句中使用变量

BEGIN 
...

var_out = select * 
          from  "_SYS_BIC"."somepackage/MULTIPLE_IP_VIEW"
          where "IP_IDS" in(:paramStr);
END

视图将激活,但我没有从查询中得到任何结果。没有运行时错误,只是一个空的结果集。当我手动将值传递给WHERE IN()子句时,一切正常。这似乎是一个基本问题,但我似乎无法让它发挥作用。我什至尝试使用char(39)而不是''''在我的连接表达式中,但没有香蕉:(

4

4 回答 4

1

SAP 注释“2315085 – Query with Multi-Value Parameter on Scripted Calculation View Fails with Incorrect Syntax Error”实际上展示了我第一次尝试时失败的 APPLY_FILTER() 方法。

它还提供了一个 UDF_IN_LIST 函数,用于将带有项目数组的长 varchar 字符串从输入参数转换为可用的列表内谓词。

不幸的是,尽管有一些参数可用(SAP Note 2457876:将错误转换为字符串长度溢出的警告),但无法使其在 sps11 rev 111.03 中工作 - (范围 3)字符串太长异常

然后 ALTER SYSTEM ALTER CONFIGURATION ('indexserver.ini', 'System') set ('sqlscript', 'typecheck_procedure_input_param') = 'false' WITH RECONFIGURE;

-重新编译计算视图

然后

select * from :var_tempout where OBJECT_ID in (select I_LIST from "BWOBJDES"."CROSS_AREA::UDF_INLIST_P"(:in_objectids,','));

无效号码异常 - 无效号码

但是从函数中取出脚本使它工作......

对于这个 SPS 11 级别 APPLY_FILTER 似乎是唯一的解决方法。真的很难说 SPS 12 的转速是多少才能拥有它。

FUNCTION "BWOBJDES"."CROSS_AREA::UDF_INLIST_P"(str_input nvarchar(5000), 
delimiter nvarchar(10)) 
RETURNS table ( I_LIST INTEGER ) LANGUAGE SQLSCRIPT SQL SECURITY INVOKER AS
/********* Begin Function Script ************/
BEGIN
DECLARE cnt int;
DECLARE temp_input nvarchar(128);
DECLARE slice NVARCHAR(10) ARRAY;

temp_input := :str_input;
cnt := 1;
WHILE length(temp_input) > 0 DO
    if instr(temp_input, delimiter) > 0 then
        slice[:cnt] := substr_before(temp_input,delimiter);
        temp_input := substr_after(temp_input,delimiter);
        cnt := :cnt + 1;
    else
        slice[:cnt] := temp_input;
        break;
    end if;
END WHILE;
tab2 = UNNEST(:slice) AS (I_LIST);
return select I_LIST from :tab2;
END;

CREATE PROCEDURE "MY_SCRIPTED_CV/proc"( IN numbers NVARCHAR(5000), OUT 
var_out
MY_TABLE_TYPE ) language sqlscript sql security definer reads sql data with 
result view
"MY_SCRIPTED_CV" as
/********* Begin Procedure Script ************/
 BEGIN
  -- not working
  --var_out = select * from MY_TABLE where NUMBER in (select I_LIST from 
  --UDF_INLIST_P(:numbers,','));

  -- working
  DECLARE cnt int;
  DECLARE temp_input nvarchar(128);
  DECLARE slice NVARCHAR(13) ARRAY;
  DECLARE delimiter VARCHAR := ',';

  temp_input := replace(:numbers, char(39), '');
  cnt := 1;
  WHILE length(temp_input) > 0 DO
    if instr(temp_input, delimiter) > 0 then
        slice[:cnt] := substr_before(temp_input,delimiter);
        temp_input := substr_after(temp_input,delimiter);
        cnt := :cnt + 1;
    else
        slice[:cnt] := temp_input;
        break;
    end if;
  END WHILE;
 l_numbers = UNNEST(:slice) AS (NUMBER);

 var_out=
 SELECT *
 FROM MAIN AS MA
 INNER JOIN l_numbers as LN
 ON MAIN.NUMBER = LN.NUMBER

 END;
于 2017-09-17T13:01:35.790 回答
1

好的,所以您在这里所做的是试图使声明动态化。对于 IN 条件,您似乎希望一旦您填写完毕paramStr,它将作为一组参数处理。

根本不是这样。
让我们以评论中的示例为例:paramStr = ' 'ip1','ip2' '

paramStr当填充到您的代码中时会发生什么:

var_out = select * 
          from  "_SYS_BIC"."somepackage/MULTIPLE_IP_VIEW"
          where "IP_IDS" in(' ''ip1'',''ip2'' ');

因此,IP_DS = 'ip1' or IP_DS = 'ip2'您实际上是在寻找匹配的记录,而不是寻找匹配的记录IP_DS = ' 'ip1','ip2' '

解决此问题的一种方法是使用该APPLY_FILTER()函数。

var_out = select * 
          from  "_SYS_BIC"."somepackage/MULTIPLE_IP_VIEW";

filterStr = ' "IP_IDS" in (''ip1'',''ip2'') ';

var_out_filt = APPLY_FILTER(:var_out, :filterStr) ;

我前段时间写过:“关于 IN 条件的多个错误”。另外,请查看APPLY_FILTER的文档。

于 2017-03-06T06:23:52.290 回答
0

我知道,这是一个相当古老的线程,但是我的发现基于我在这里从 Jenova 读到的内容,对其他人来说可能很有趣。这就是为什么我把它们写下来。

我面临着同样的问题。用户可以在我必须优化的计算视图中的输入参数中发送多个条目。不幸的是,我以前遇到过像 Jenova 和其他人一样的问题。而且,不,恕我直言,这不是关于动态视图执行或动态 sql,而是关于在脚本计算视图或表函数中以干净的方式处理列表,如果它们是在输入参数中发送的。

Lars 使用 APPLY_FILTER 的建议不适用于我的情况,因为我不能在 APPLY_FILTER 函数本身中使用复杂的语句。我必须在选择中具体化全部数据,我可以将结果分配给 APPLY_FILTER 然后执行过滤。我希望看到在第一步中应用的过滤,而不是在实现未出现在过滤器应用程序结果中的数据之后。

因此,我使用 hdbtablefunction 创建了一个执行参数字符串清理和转换为数组的函数,然后它返回 UNNEST 函数的结果。

由于函数的结果是一个表,它可以用作脚本视图或表函数中的表 - 在连接、子选择等中。

通过这种方式,我能够按预期快速处理用户输入列表,并且资源消耗更少。

FUNCTION "_SYS_BIC"."package1::transparam" (ip_string NVARCHAR(500) ) 
        RETURNS table ( "PARAMETER" nvarchar(100))
        LANGUAGE SQLSCRIPT
        SQL SECURITY INVOKER AS
        v_test varchar(1000);
        IP_DELIMITER VARCHAR(1) := ',';
        v_out VARCHAR(100):='';
        v_count INTEGER:=1;
        v_substr VARCHAR(1000):='';
        v_substr2 VARCHAR(1000):='';
        id INTEGER array;
        val VARCHAR(100) array;
    BEGIN 
    --
     v_substr:=:ip_string; 
     v_substr := REPLACE(:v_substr, '''', '');
     v_substr := REPLACE(:v_substr, ' ', '');
        while(LOCATE (:v_substr, :ip_delimiter) > 0 ) do

            -- find value
            v_out := SUBSTR(v_substr, 0, LOCATE (:v_substr, :ip_delimiter) - 1 );

            -- out to output
            val[v_count]:=v_out;

            -- increment counter
            v_count:=:v_count+1;

            -- new substring for search
            v_substr2 := SUBSTR(:v_substr, LOCATE (:v_substr, :ip_delimiter) + 1, LENGTH(:v_substr));
            v_substr := v_substr2;

        END while;

        IF(LOCATE (:v_substr, :ip_delimiter) = 0 AND LENGTH(:v_substr) > 0) THEN
            -- no delimiter in string
            val[v_count]:=v_substr;

        END IF;

        -- format output as tables
          rst = unnest(:VAL) AS ("PARAMETER");

       RETURN SELECT * FROM :rst;
    END;

可以称为

select * from "package1.transparam"('''BLU'',''BLA''')

用两行返回表

PARAMETER
---------
BLU
BLA
于 2019-04-23T14:45:01.883 回答
0

最全面的解释在这里: https ://blogs.sap.com/2019/01/17/passing-multi-value-input-parameter-from-calculation-view-to-table-function-in-sap-hana - 分步指南/

  1. 创建自定义函数将字符串拆分为多个值

然后可以使用内/左外连接进行过滤。

于 2020-05-12T12:02:23.043 回答