【问题标题】:How to call an Oracle Packaged Function from Excal如何从 Excel 调用 Oracle 包函数
【发布时间】:2015-09-25 11:08:14
【问题描述】:

我正在尝试在 Oracle 11g 数据库中执行包和函数。 我有以下代码,但是在第一个 .Execute 命令时出现错误。

VBA 中的 Locals 窗口给出了以下关于错误的信息:

Description : "Unspecified error" : String
Number : -2147467259 : Long
Source : "OraOLEDB" : String

哦,我已经引用了 Microsoft ActiveX 数据对象 6.1 库。

我尝试了对代码的各种更改,但是它们都返回相同的错误消息。

Sub QueryGLComb()

Dim OraCon As ADODB.Connection
Dim recset As New ADODB.Recordset
Dim cmd As New ADODB.Command
Dim objErr As ADODB.Error
Dim lngRow As Long

On Error GoTo err_test

Set OraCon = CreateObject("ADODB.Connection")
OraCon.ConnectionString = "Provider=OraOLEDB.Oracle;" & _
                          "Data Source=DEMO;" & _
                          "User ID=scott;" & _
                          "Password=tiger;"
OraCon.Open
Set cmd = New ADODB.Command
Set cmd.ActiveConnection = OraCon

If OraCon.State = adStateClosed Then
    MsgBox "false"
End If

With cmd
  .ActiveConnection = OraCon
  .CommandText = "fnd_flex_keyval.validate_segs"
  .CommandType = adCmdStoredProc
  .CommandTimeout = 60
  .Parameters.Append .CreateParameter("return", adBoolean, adParamReturnValue)
  .Parameters.Append .CreateParameter("operation", adVariant, adParamInput, , "CHECK_COMBINATION")
  .Parameters.Append .CreateParameter("appl_short_name", adVariant, adParamInput, , "SQLGL")
  .Parameters.Append .CreateParameter("key_flex_code", adVariant, adParamInput, , "GL#")
  .Parameters.Append .CreateParameter("structure_number", adNumeric, adParamInput, , "50268")
  .Parameters.Append .CreateParameter("concat_segments", adVariant, adParamInput, , "67000.2400.4001.01")
  .Parameters.Append .CreateParameter("validation_date", adVariant, adParamInput, , "sysdate")
  .Execute
End With

Set cmd = New ADODB.Command
Set cmd.ActiveConnection = OraCon
With cmd
  .ActiveConnection = OraCon
  .CommandText = "begin FND_FLEX_KEYVAL.ERROR_MESSAGE"
  .CommandType = adCmdStoredProc
  .Parameters.Append .CreateParameter("message", adVariant, adParamReturnValue)
  .Execute
End With

MsgBox "Return Message = " & cmd.Parameters(0)

OraCon.Close

err_test:
    MsgBox Error$
    For Each objErr In OraCon.Errors
        MsgBox objErr.Description
    Next
    OraCon.Errors.Clear
    Resume Next

End Sub

第一个“.Execute”行是发生错误的地方。

这是我在 SQLDeveloper/Toad 中使用的 Oracle 代码。我调用的包是“FND_FLEX_VALIDATE”,函数是“VALIDATE_SEGS”。这是 Oracle eBS 中的一个 Oracle 提供的软件包。如果对 fnd_flex_keyval.validate_segs 的初始调用返回 true,那么一切都很好。如果它返回错误,那么我们必须调用“FND_FLEX_KEYVAL.ERROR_MESSAGE”来检索错误消息。

DECLARE
    l_return  boolean;
    l_message varchar2(240); 
BEGIN
    l_return := fnd_flex_keyval.validate_segs(operation => 'CHECK_COMBINATION'
                                           ,appl_short_name => 'SQLGL'
                                           ,key_flex_code => 'GL#'
                                           ,structure_number => 50268 --- Pass your chart of accounts id
                                           ,concat_segments => '67000.2400.4001.01' --- Pass your account combination string you want to create
                                           ,validation_date => sysdate); --- effective date by which you want the combination to be validated

   l_message := FND_FLEX_KEYVAL.ERROR_MESSAGE;
    If l_return THEN
        dbms_output.put_line('Valid');
    Else
        dbms_output.put_line(l_message);
    End if;
END;

我创建了一个新文件进行测试。我现在尝试执行的过程有 3 个参数:1 输入,2 输出。同样,当代码到达 .Execute 命令时,我仍然收到相同的错误。

Sub ExecutePackageP()

Dim OraCon As New ADODB.Connection
Dim recset As New ADODB.Recordset
Dim cmd As New ADODB.Command
Dim objErr As ADODB.Error
Dim lngRow As Long

On Error GoTo err_test

Set OraCon = CreateObject("ADODB.Connection")

OraCon.ConnectionString = "Provider=OraOLEDB.Oracle;" & _
                          "Data Source=demo;" & _
                          "User ID=scott;" & _
                          "Password=tiger;"
OraCon.Open
cmd.ActiveConnection = OraCon

If OraCon.State = adStateClosed Then
    MsgBox "false"
End If

With cmd
    .ActiveConnection = OraCon
    .CommandType = adCmdStoredProc
    .CommandText = "XXX_VALIDATE_ACCOUNT"
    .CommandTimeout = 60
    .Parameters.Append .CreateParameter("param1", adVariant, adParamInput, , "67000.2400.4001.01")
    .Parameters.Append .CreateParameter("param2", adBoolean, adParamOutput)
    .Parameters.Append .CreateParameter("param3", adVariant, adParamOutput)
    .Execute
End With

MsgBox "Reurtn = P_CCID=" & cmd.Parameters(1) & "; P_RESULT=" & cmd.Parameters(2) & ";"

OraCon.Close
End sub

我将参数类型从 adVariant 更改为 adVarchar,并收到一条错误消息,指出 Parameter 对象定义不正确。只有 adVariant 似乎有效。我记得有一个网站显示了针对不同数据库执行时要使用的类型,它说 varchar2 使用 adVariant。 添加 xxx_validate_account 的过程代码:(注意,此过程调用一个名为 fnd_flex_ext.get_ccid 的内部 oracle 函数)

CREATE OR REPLACE PROCEDURE APPS."XXX_VALIDATE_ACCOUNT" (p_account_flex in varchar2,
                         p_valid out boolean,
                         p_message out varchar2) is

x_structure_id number;
x_ccid number := 0;

pragma autonomous_transaction;

begin

execute immediate 'alter session set nls_language = ''AMERICAN''';

p_valid := false;

select chart_of_accounts_id
into x_structure_id
from gl_ledgers
where short_name = 'XXX';

x_ccid := fnd_flex_ext.get_ccid(application_short_name => 'SQLGL',
                        key_flex_code => 'GL#',
                        structure_number => x_structure_id,
                        validation_date => to_char(sysdate,'YYYY/MM/DD HH24:MI:SS'),
                        concatenated_segments => p_account_flex);

if x_ccid > 0 then
    p_valid := true;
    commit;
else
    p_valid := false;
    p_message := fnd_message.get;
end if;

exception when others then
        p_message := sqlerrm;
        p_valid := false;

end xxx_validate_account;
/

更新:2015 年 9 月 25 日: 不幸的是,由于其他软件包和应用程序正在使用它,我无法更新该软件包。 因此,我决定创建一个具有简单功能的新包。 我确保输入和输出不是布尔值以消除该问题。 不过,对于我的生活,我仍然遇到同样的问题! 我尝试在参数字符串中使用“adVarchar,100”,但 excel 只是不断告诉我参数类型错误。认真地认为我的机器有某种问题......

要遵循的新功能和代码:

create or replace package apps.xxxx_return_username is 
function return_username (p_user_id in number) return varchar2;
end xxxx_return_username;
/
create or replace package body apps.xxxx_return_username as
function return_username(p_user_id in number) return varchar2 is
x_user_name varchar2(100);
begin
select user_name into x_user_name
from apps.fnd_user where user_id = p_user_id;
return x_user_name;
exception
  when no_data_found then return 'xxxxx';
end return_username;
end xxxx_return_username;
/


Sub newfunctioncall()

Dim OraCon As New ADODB.Connection
Dim recset As New ADODB.Recordset
Dim cmd As New ADODB.Command

Set OraCon = CreateObject("ADODB.Connection")

OraCon.ConnectionString = "Provider=OraOLEDB.Oracle;" & _
                          "Data Source=demo;" & _
                          "User ID=scott;" & _
                          "Password=tiger;"
OraCon.Open
Set cmd = New ADODB.Command
Set cmd.ActiveConnection = OraCon

If OraCon.State = adStateClosed Then
    MsgBox "false"
End If

Set param1 = cmd.CreateParameter("param1", adNumeric, adParamInput, , "15483")
cmd.Parameters.Append param1
Set param2 = cmd.CreateParameter("param2", adVariant, , adParamReturnValue)
cmd.Parameters.Append param2

cmd.CommandType = adCmdStoredProc
cmd.CommandText = "xxecu_return_username.return_username"
Set recset = cmd.Execute

MsgBox "Reurtn = USERNAME=" & cmd.Parameters(0)

OraCon.Close

End Sub

【问题讨论】:

  • 如果您注释掉On Error 哪一行会引发错误?
  • 嗨蒂姆。第一个“.Execute”行是发生错误的地方。
  • 可以添加XXX_VALIDATE_ACCOUNT的代码吗?此外,我在回答中建议的更改之一是从 adVariant 切换到实际数据类型,在大多数情况下它是 adVarchar
  • 刚刚想到。我使用的是 32 位的 Excel,但安装了 64 位的 Oracle 客户端。这可能是个问题吗?不确定我是否可以同时安装 32 和 64 客户端,然后以某种方式选择在 excel 中使用哪一个。
  • 但是,我只是想。在同一个工作簿中,我有一个宏来执行同一个数据库的简单 sql 查询,并且可以 100% 工作。所以现在认为这可能不是驱动程序问题。

标签: excel oracle vba stored-procedures


【解决方案1】:

很好地描述了你的问题的背景......

我看到了一些可能出错的地方。在不确切知道它可能是哪一个的情况下,我认为这是一个可以执行您所寻求的工作的代码示例——尽管我承认我可能有一些错误。

首先,在您的过程中,如果您将变量声明为参数:

create or replace procedure validate_segs(
  operation in varchar2,
  appl_short_name in varchar2,
  key_flex_code in varchar2,
  structure_number in number,
  concat_segments in varchar2,
  validation_date in date,
  message out varchar2,
  results out numeric
) as
BEGIN
  message := 'Hello';
  results := 1;
END;

然后,在 VBA 中,我认为您可以做这样的事情。使用 VBA/OleDB 和 Oracle,我从来没有让布尔值表现出来。它们在 C# 中运行良好,但我在 VBA 上没有成功,所以我将它转换为这个示例的数字,它似乎总是在 VBA 中转换为布尔值。

With cmd
  .ActiveConnection = OraCon
  .CommandText = "validate_segs"
  .CommandType = adCmdStoredProc
  .CommandTimeout = 60
  .NamedParameters = True

  .Parameters.Append .CreateParameter( _
      "operation", adVarChar, adParamInput, 255, "CHECK_COMBINATION")
  .Parameters.Append .CreateParameter( _
      "appl_short_name", adVarChar, adParamInput, 255, "SQLGL")
  .Parameters.Append .CreateParameter( _
      "key_flex_code", adVarChar, adParamInput, 255, "GL#")
  .Parameters.Append .CreateParameter( _
      "structure_number", adNumeric, adParamInput, 10, 50268)
  .Parameters.Append .CreateParameter( _
      "concat_segments", adVarChar, adParamInput, 255, "67000.2400.4001.01")
  .Parameters.Append .CreateParameter( _
      "validation_date", adDate, adParamInput, , Now())
  .Parameters.Append .CreateParameter("message", adVarChar, adParamOutput, 255)
  .Parameters.Append .CreateParameter("return", adNumeric, adParamOutput, 1)
  .Execute
End With

然后,从存储过程中实际挖掘结果:

Dim result As Boolean
Dim message As String

message = cmd.Parameters(6).Value
result = cmd.Parameters(7).Value

我知道你可能被 VBA 锁定了,但是如果你有选择的机会,.NET 的界面为调试这类事情提供了一个更好的环境。

-- 编辑 9/24/15--

我认为您的代码几乎就在那里,但我可以看到还有两个问题,其中一个我不能 100% 确定是一个问题,但我认为另一个是。我在我的解决方案中较早地避开了这一点,但我现在认为这可能是核心问题:OLEDB 和 Oracle 布尔值似乎不能很好地结合在一起。见这篇文章:

https://support.microsoft.com/en-us/kb/306530

要解决此问题,如果您将其切换为 Oracle 和 VBA 中的数字,它应该可以工作。

另一个我不确定是否真实的问题是,当我认为您应该使用字符串(或 Varchar)Db 类型时,您正在使用 Variant Db 类型。您提到您在文档中看到使用变体,所以也许这无关紧要......但是 Varchar 会起作用。您确实需要文本长度的附加参数才能正确编译(我使用 255 进行踢球)。

因此,我建议的两个更改是:

1) 将存储过程中的布尔值更改为数字:

CREATE OR REPLACE PROCEDURE "XXX_VALIDATE_ACCOUNT" (
   p_account_flex in varchar2, p_valid out number, p_message out varchar2) is
      -- blah blah blah
   p_valid := 0;

2) 更改您的 VBA 代码以期望返回一个数字(并将 Variant 转换为 Varchar)

.Parameters.Append .CreateParameter("param1", adVarChar, adParamInput, 255, _
    "67000.2400.4001.01")
.Parameters.Append .CreateParameter("param2", adNumeric, adParamOutput)
.Parameters.Append .CreateParameter("param3", adVarChar, adParamOutput, 255)

即使您将其转换为布尔值

Dim result As Boolean
result = cmd.Parameters(1).Value

我必须说,调试器返回的错误消息根本没有帮助。

【讨论】:

  • 嗨哈姆伯恩。感谢你的回复。我设置了一个新文件来尝试连接到 Oracle 以针对不同的过程进行测试。此过程有一个 1 输入作为 varchar2,2 个输出,1 是布尔值,1 是 varchar2。我已将新代码放在我原来的问题中。
【解决方案2】:

您如何知道您正在 Oracle 数据库中执行有效命令?您可能想先使用 SQLplus 进行尝试。不太清楚你的函数调用在哪里。可能是:

begin FND_FLEX_KEYVAL.ERROR_MESSAGE

但这并不是一个完整的声明。至少它需要以下内容。

begin 
   FND_FLEX_KEYVAL.ERROR_MESSAGE; 
end;

但是,如果 FND_FLEX_KEYVAL.ERROR_MESSAGE 是一个函数,则之前的代码也不会运行。在这种情况下,您要选择函数值:

select FND_FLEX_KEYVAL.ERROR_MESSAGE from dual

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 2015-02-17
    相关资源
    最近更新 更多