SQL*Plus、@ 和相对路径

Ren*_*ger 9 oracle sqlplus scripting

不知何故,似乎 SQL*Plus(至少在 Windows 上)在调用 with@@以及路径以单点或双点开头时无法定位具有相对路径的脚本。

例如,在x:\some\whereI下有以下目录结构:

script.sql
main-dir\main-sub-dir
              call-script.sql
              script.sql
Run Code Online (Sandbox Code Playgroud)

即:两个script.sql但在不同的位置。

script.sql刚刚下的内容x:\some\where简直了

prompt SCRIPT root
Run Code Online (Sandbox Code Playgroud)

而另一个script.sql的内容是

prompt SCRIPT main-dir/main-subdir
Run Code Online (Sandbox Code Playgroud)

call-script.sql 阅读

@@script.sql
@ script.sql
Run Code Online (Sandbox Code Playgroud)

预期产出

如果我从 SQL*Plus 启动x:\some\where然后执行

@main-dir/main-sub-dir/call-scripts
Run Code Online (Sandbox Code Playgroud)

输出将是

SCRIPT main-dir/main-subdir
SCRIPT root 
Run Code Online (Sandbox Code Playgroud)

这是意料之中的,因为单曲@应该从 SQL*Plus 的启动位置@@搜索路径,并且应该从包含脚本的目录中搜索路径。

意外的输出

现在,如果我call-scripts.sql这样改变:

@@./script.sql
@ ./script.sql
Run Code Online (Sandbox Code Playgroud)

double@@似乎改变了它的行为,因为它从 SQL*Plus 开始的位置搜索路径,现在输出将是

SCRIPT root
SCRIPT root
Run Code Online (Sandbox Code Playgroud)

不是我所期望的。


这种行为是否记录在某处,更重要的是,我必须如何更改call-scripts.sql才能@@../../other-dir/other-sub-dir/script正确调用相对路径 ( )?

小智 7

是的,这是 Bug 2391334,它已经存在很长时间了,可能在不久的将来不会修复。

解决此问题的一种方法是“知道”脚本的路径,而无需对该路径进行实际硬编码。要在 SQLPlus 中执行此操作需要一个技巧——如果您尝试运行一个不存在的文件,那么您将收到一条包含路径名的错误消息。

所以这是一个实际的演示。为了模仿你的场景,我有:

c:\temp\demo
   script.sql
   maindir
      subdir
         call_script.sql
         script.sql
Run Code Online (Sandbox Code Playgroud)

我们可以做的是在 call_script.sql 前面添加一些命令,它将拾取路径。它看起来有点奇怪,但您不需要更改它 - 它只是您粘贴的固定内容

set termout off
spool _path_finder.sql
@@_nonexistent_script.sql
spool off;

var path varchar2(100);
set serverout on
declare
  output varchar2(1000) := regexp_replace(replace(q'{
@_path_finder.sql
}',chr(10)),'.*"(.*)".*','\1');
begin 
  :path:=substr(output,1,length(output)-24);
end;
/
col path new_val path
select :path path from dual;
set termout on
Run Code Online (Sandbox Code Playgroud)

这里发生的事情是我们正在运行一个不存在的脚本,它返回:

“SP2-0310:无法打开文件“path\_nonexistent_script.sql”

因此,通过一些正则表达式,我们可以提取路径,将其存储在 SQLPlus 变量中,然后从那时起使用。

所以你的 call_script.sql 的最终版本看起来像这样

set termout off
spool _path_finder.sql
@@_nonexistent_script.sql
spool off;

var path varchar2(100);
set serverout on
declare
  output varchar2(1000) := regexp_replace(replace(q'{
@_path_finder.sql
}',chr(10)),'.*"(.*)".*','\1');
begin 
  :path:=substr(output,1,length(output)-24);
end;
/
col path new_val path
select :path path from dual;
set termout on
prompt path was &path      

@@&path\script.sql
@&path\script.sql
Run Code Online (Sandbox Code Playgroud)

当我们运行它时,我们得到以下信息

SQL> @maindir\mainsubdir\call_script
path was maindir\mainsubdir
script in subdir
script in subdir
Run Code Online (Sandbox Code Playgroud)

你去吧:-)