【问题标题】:Unable to update unix shell variable in sql plus无法在 sql plus 中更新 unix shell 变量
【发布时间】:2016-05-12 07:25:06
【问题描述】:

我有一个 shell 脚本,它存储从其父脚本作为输入拾取的值数组。

该数组需要在 PL/SQL 代码块中使用。我需要遍历 unix 数组并将元素存储在 pl/sql 集合变量中。但我无法增加数组的索引。以下是我的代码:

#!/bin/sh
# local variables declaration and initialization
v_username="$1"
v_password="$2"
v_database="$3"
v_vdsl_file="$4"

IFS=$'\n'
set -f

counter=0
declare -a v_vdsl_id
for i in $(cat $v_vdsl_file); do
v_vdsl_id[$counter]=`echo "$i"`
counter=$(($counter+1))
done;

p=0

conn1=`sqlplus -s ${v_username}/${v_password}@${v_database}<<THEEND

SET HEAD OFF
SET AUTOPRINT OFF
SET ECHO OFF
SET VERIFY OFF
SET FEEDBACK OFF
SET LINESIZE 3000
SET WRAP OFF 
SET serveroutput on

DECLARE

counter integer :=0;
type table_vdsl_id is table of integer;
vdsl_oids table_vdsl_id := table_vdsl_id();

begin

for i in 1..${#v_vdsl_id[*]}
loop
counter := counter +1;
vdsl_oids.extend;
vdsl_oids(counter) := ${v_vdsl_id[$p]};
${p} := ${p} +1;
end loop;
end;
/
THEEND`

尝试递增 unix 变量计数器时遇到错误:

0 := 0 +1;
    *
ERROR at line 88:
ORA-06550: line 88, column 9:
PLS-00103: Encountered the symbol "0" when expecting one of the following:
( begin case declare end exit for goto if loop mod null

${p} 基本上获取了 p 的值,但是如何在 sql 块中更改这个 unix 计数器的值呢?请指教。

【问题讨论】:

    标签: arrays shell unix plsql


    【解决方案1】:

    here document 只是一个静态字符串。在运行使用此此处文档的程序之前,shell 将(可选地)替换此处文档中的任何变量,但是一旦完成,shell 与生成的字符串或您将传递此字符串的程序之间没有交互.

    一个简单的解决方法是循环 shell 中的条目,以便生成的传递给 PL/SQL 的脚本只包含一系列静态语句。可能是这样的:

    conn1=$(sqlplus sqlplus -s "${v_username}/${v_password}@${v_database}"\
     "$(cat <<'____HEADER';
        SET HEAD OFF
        SET AUTOPRINT OFF
        SET ECHO OFF
        SET VERIFY OFF
        SET FEEDBACK OFF
        SET LINESIZE 3000
        SET WRAP OFF 
        SET serveroutput on
    
        DECLARE
    
        type table_vdsl_id is table of integer;
        vdsl_oids table_vdsl_id := table_vdsl_id();
    
        begin
    ____HEADER
    
      nl -ba "$v_vdsl_file" |
      while read -r lineno i ; do
          cat <<_________RECORD
            vdsl_oids.extend;
            vdsl_oids($lineno) := $i;
    ________RECORD
      done )
      cat <<'____FOOTER'
        end;
        /
    ____FOOTER
    )")
    # for debugging
    echo "$conn1"
    

    我不得不猜测 SQL 语法的一些点点滴滴,所以可能会有一些细节需要您更正。将sqlplus 替换为cat 以查看生成的内容,并检查其是否正确。

    注意变量插值周围的double quotes,以及用于循环文件中行的preference for while over for。我使用nl 来生成行号,因此我不需要为此使用笨重的 shell 算术运算符。

    【讨论】:

    • 不错的解决方案。但我不明白这个 here document 是什么。你能解释一下吗?
    • 谢谢,我还以为是错别字了……老外有时很难区分计算词汇和非计算词汇。
    【解决方案2】:

    在 PL/SQL 中,您不能将某些内容分配给常量。既然你使用了$p:=$p+1,它就是这样做的:0 := 0+1。不可能。

    除了三元组之外,另一种可能的解决方案是将数组存储在一个变量中,当然,分隔符不能在数组的值之一内 - 比如_THE_SEP_="_THE_IMPOSSIBLE_SEP_"

    1。连接数组值

    for i in $(cat $v_vdsl_file); do
      _LIST=${_LIST}${_THE_SEP_}${i}
      counter=$(($counter+1))
    done
    

    2。直接在 PL 中从 ${_LIST} 字符串中构建一个集合(这里是一个简单的 TABLE OF VARCHAR2):

    CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
    /
    DECLARE
      l_string VARCHAR2(32767) := '${_LIST}' || '${_THE_SEP_}';
      l_sep_index PLS_INTEGER;
      l_index PLS_INTEGER := 1;
      l_l_thesep PLS_INTEGER  := length('${_THE_SEP_}');
      l_tab t_my_list     := t_my_list();
    BEGIN
      LOOP
        l_sep_index := INSTR(l_string, '${_THE_SEP_}', l_index);
        EXIT
      WHEN l_sep_index = 0;
        l_tab.EXTEND;
        l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_sep_index - l_index));
        l_index            := l_sep_index + l_l_thesep;
      END LOOP;
    

    3。那么你的收藏在l_tab。用它做你想做的事。

    您可以将整个内容用作:

        _THE_SEP_=_THE_IMPOSSIBLE_SEP_
        for i in $(cat $v_vdsl_file); do
          _LIST=${_LIST}${_THE_SEP_}${i}
          counter=$(($counter+1))
        done
    
        conn1=`sqlplus -s ${v_username}/${v_password}@${v_database}<<THEEND
    
        SET HEAD OFF
        SET AUTOPRINT OFF
        SET ECHO OFF
        SET VERIFY OFF
        SET FEEDBACK OFF
        SET LINESIZE 3000
        SET WRAP OFF 
        SET serveroutput on
    
        CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
    /
        DECLARE
          l_string VARCHAR2(32767) := '${_LIST}' || '${_THE_SEP_}';
          l_sep_index PLS_INTEGER;
          l_index PLS_INTEGER := 1;
          l_l_thesep PLS_INTEGER  := length('${_THE_SEP_}');
          l_tab t_my_list     := t_my_list();
        BEGIN
          LOOP
            l_sep_index := INSTR(l_string, '${_THE_SEP_}', l_index);
            EXIT
          WHEN l_sep_index = 0;
            l_tab.EXTEND;
            l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_sep_index - l_index));
            l_index            := l_sep_index + l_l_thesep;
            dbms_output.put_line(l_tab.COUNT||'-'||l_tab(l_tab.COUNT)||'/');
          END LOOP;
        END;
    /
    THEEND`
    # see your output then:
    echo $conn1
    

    【讨论】:

      猜你喜欢
      • 2013-10-17
      • 2013-11-19
      • 1970-01-01
      • 1970-01-01
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-12
      相关资源
      最近更新 更多