HANA - 将字符串变量传递给SQL脚本中的WHERE IN()子句

问题描述 投票:1回答:3

让我们假设我在脚本计算视图中有一些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)而不是'''',但没有香蕉:(

hana sql-scripts hana-sql-script
3个回答
1
投票

SAP注释“2315085 - 脚本计算视图上的多值参数查询失败,语法错误不正确”实际上展示了我第一次尝试时失败的APPLY_FILTER()方法。

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

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

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

-recompile Calc视图

然后

select * from:var_tempout where OBJECT_ID in(从“BWOBJDES”中选择I_LIST。“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;

0
投票

好的,所以你在这里做的是试图让声明变得动态。对于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) ;

我前段时间写过:"On multiple mistakes with IN conditions"。另外,请查看APPLY_FILTER的文档。


0
投票

我知道,这是一个相当古老的线索但是我的发现基于我在这里从Jenova读到的内容可能对其他人来说很有趣。 Thatswhy我写下来了。

我遇到了同样的问题。用户可以在calc视图中的输入参数中发送多个entrie,我必须进行优化。不幸的是,我之前遇到了像Jenova和其他人一样的问题。并且,不,这不是关于动态视图执行或动态sql,而是关于在脚本化的calc视图或表函数中以干净的方式处理列表(如果它们是在输入参数中发送的)。

Lars的使用APPLY_FILTER的建议在我的情况下不适用,因为我不能在APPLY_FILTER函数本身中使用复杂的语句。我必须在select中实现整个数据量,这个结果我可以分配给APPLY_FILTER,然后执行过滤。我希望看到在第一步中应用的过滤不是在实现过滤器应用程序的结果中没有出现的数据之后。

所以我使用hdbtable函数来创建一个执行参数字符串清理和转换为数组的函数,然后返回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
© www.soinside.com 2019 - 2024. All rights reserved.