将文件的内容作为参数传递给 Sql*Plus 命令
Posted
技术标签:
【中文标题】将文件的内容作为参数传递给 Sql*Plus 命令【英文标题】:Passing Contents of File as Parameter to Sql*Plus Command 【发布时间】:2013-12-30 23:58:45 【问题描述】:我正在尝试编写一个 sqlplus 命令,该命令从存储在 .sql 文件中的查询创建表。
包含查询的特定 .sql 文件将作为变量 (&v_InputQuery
) 提供给我的 sqlplus 命令。
我已经尝试过类似的方法,但它不起作用。
CREATE TABLE &v_OutputTable AS
(
< &v_InputQuery
)
;
我收到一条错误消息,提示缺少 SELECT 关键字。
我真正想要的是&v_InputQuery
不是替换为用户指定的文件名,而是替换为文件的实际内容。有没有办法做到这一点?
非常感谢。
【问题讨论】:
文件(名称)是否必须直接提供给 SQL*Plus,或者您可以使用中间 shell 脚本(或批处理文件,如果您在 Windows 上)? @Alex Poole 我是 UNIX 环境的新手,所以我不熟悉 shell 脚本。我只是将其设置为方便自己,这样我就不必经常进入底层查询并用 CREATE TABLE...AS 包装它们。 【参考方案1】:是的,你可以这样做。如果您的查询位于名为 v_InputQuery.sql
的文件中,您可以这样做:
CREATE TABLE &v_OutputTable AS (
@v_InputQuery.sql
) ;
@
是该行的第一个字符,这一点很重要。 SQL*Plus 将读取该文件并将其内容放在该位置。因此,请确保文件中没有任何终止字符,例如 ;
或 /
。
【讨论】:
非常感谢。我会尽快尝试一下。我认为其中一个挑战可能是我实际上希望文件名采用变量的形式,该变量可以根据用户的输入采用不同的值。 @Steve - 您可以将@v_InputQuery.sql
替换为包含文件名的变量,例如@&v_InputQuery
;你是这个意思吗? (我不认为括号是必要的或添加任何东西 - 尽管它们不会造成伤害,除非您的查询文件已经有一个终止 ;
或 /
?)
@Jeffrey Kemp - 当我从 .sql 文件中的查询中删除终端分号时,我能够让您的方法正常工作。非常感谢您的建议。
对不起@Steve,我认为这很明显(因为文件的内容被逐字插入该位置),但会将其添加到我的答案中。【参考方案2】:
很遗憾,您不能创建 SQL*Plus 命令,而是创建一个 shell 脚本来执行它!
假设my_script.sh
在下面
#you can always complete the user interaction at unix/dos
USER=your_user
PASS=your_pass
DB=your_db
OUTPUT_TABLE=$1;
QUERY_FILE=$2;
SELECT_QUERY=`cat $QUERY_FILE`;
sqlplus -S $USER/$PASS@$DB << !
SET SERVEROUTPUT ON;
VAR EXITCODE NUMBER;
BEGIN
EXECUTE IMMEDIATE ' CREATE TABLE $OUTPUT_TABLE AS $SELECT_QUERY ';
:EXITCODE := SQLCODE;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
:EXITCODE := SQLCODE;
END;
/
exit :EXITCODE
!
如下执行脚本(取决于操作系统)
ksh my_script MY_OUTPUT_TABLE my_sql.sql;
【讨论】:
非常感谢。我没有任何使用 shell 脚本的经验,但我期待着尝试一下。 为什么选择动态 SQL?我不确定这会添加什么,因为无论如何该语句都是动态形成的。 @AlexPoole 我刚刚编辑添加了用户/密码变量,它运行得非常好,就像上面发布的一样。有 N 种方法,要么打印 DDL 并手动运行它,要么直接打印变量。在 unix 脚本中以 $XX 形式出现的内容将仅被视为 UNIX 变量!因为它使用INTERPRETER
。 Execute Immediate
,看剧本有点意思!
@MaheswaranRavisankar - 我并不是说它不起作用,只是查询 execute immediate
添加的内容,而不是单独使用 create
命令。由于语句在被解析时已完全形成,因此就 SQL 而言没有动态元素。我添加了一个单独的答案来说明我的意思 - 我认为我们可以在 cmets 中绕圈子!
@AlexPoole 没问题,我现在非常忙于一个 iPad 项目,无法花这么多钱……你太棒了!毕竟每个解决方案都是好的!取决于旁观者.. Njoy :)【参考方案3】:
扩展评论,@MaheswaranRavisankar 的方法将起作用,但动态 SQL(即execute immediate
)不是必需的,因此也不需要匿名块。可以稍微简化为:
USER=your_user
PASS=your_pass
DB=your_db
OUTPUT_TABLE=$1;
QUERY_FILE=$2;
SELECT_QUERY=`cat $QUERY_FILE`;
sqlplus -S $USER/$PASS@$DB << !
WHENEVER SQLERROR EXIT FAILURE
CREATE TABLE $OUTPUT_TABLE AS $SELECT_QUERY
!
这还允许您使用已被;
或/
终止的查询,execute immediate
版本不喜欢这种情况 - 您只需要确定您的包装脚本是否需要一个匹配的内容您的查询文件将包含。
即使whenever ...
行并不重要,但另一个答案试图以错误代码退出,所以我在某种程度上模仿了这一点。不过,这将始终以一般故障状态退出(Unix 中的1
,不确定 Windows 会做什么)。然后,您可以根据需要在脚本中使用$?
测试它是否成功。
您可以使用whenever sqlerror exit sql.sqlcode
代替通用值退出实际的SQL 错误。这样做的问题是,大多数(所有?)shell 将返回码限制在 0-255 范围内,因此大多数错误都会包装并给出一些无益的东西——一个相当可能的ORA-00955: name is already used by an existing object
错误会给出一个 shell 退出值以 187 为例。并且包装的值可能为零,这将掩盖根本发生的错误;同样合理的ORA-01536: space quota exceeded for tablespace '%s'
错误将给出一个零的shell 退出代码,这是无益的。使用exit failure
至少可以阻止这种情况。
【讨论】:
正如我所提到的,我是 shell 脚本的新手,所以这花费的时间比它应有的要长得多。但最终,它完美地工作了。非常感谢您的回答和详细的解释。以上是关于将文件的内容作为参数传递给 Sql*Plus 命令的主要内容,如果未能解决你的问题,请参考以下文章