Oracle 12c:WITH 子句中的函数不适用于 ADODB

Oracle 12c: Functions in the WITH Clause do not work with ADODB

select banner
from v$version
;


BANNER
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
"CORE   12.1.0.2.0  Production"
TNS for Solaris: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production

在其 12c 版本中,Oracle 添加了允许在 SQL 语句顶部直接声明 Pl/SQL 函数的功能(参见 https://oracle-base.com/articles/12c/with-clause-enhancements-12cr1

这可能是一个非常方便的功能,尤其是。在需要从数据库中提取数据的项目中,用户权限仅限于 SELECT 语句。

以下(显然已简化)查询 运行在 Oracle SQL 中很好 SQL 开发人员:

with 
  function add_string(p_string in varchar2) return varchar2
  is
    --Function to add a string
    l_buffer varchar2(32767);
  begin
    l_buffer := p_string || ' works!';
    --
    return l_buffer;
    --
  end ; 
--
select add_string('Yes, it') as outVal
from dual
;

---------
OUTVAL
Yes, it works!

现在,我正在尝试将基于相同原理的查询的结果集加载到 MS-Access 2007 中的 ADODB 记录集中。我知道 ADO 不能无缝地处理以 WITH 子句开头的查询(请参阅,例如 Why can't I do a "with x as (...)" with ADODB and Oracle?)。我通常解决此问题的方法是将查询包含在 SELECT 块中,如下所示:

select hey_yo 
from (
  with sub as (
    select 'hey' as hey
    from dual
  )
  select hey || ' yo!' as hey_yo
  from sub
)
;

---------
HEY_YO
hey yo!

不幸的是,在处理 WITH 子句中的函数时,这似乎不是 Oracle 中的合法语法:

select *
from (
  with 
    function add_string(p_string in varchar2) return varchar2
    is
      --Function to add a string
      l_buffer varchar2(32767);
    begin
      l_buffer := p_string || ' works!';
      --
      return l_buffer;
      --
    end ; 
  --
  select add_string('Yes, it') as outVal
  from dual
)
;

---------
PLS-00103: Encountered the symbol ")" when expecting one of the following:

   . , @ ; for <an identifier>
   <a double-quoted delimited-identifier> group having intersect
   minus order partition start subpartition union where connect
   sample
06550. 00000 -  "line %s, column %s:\n%s"
*Cause:    Usually a PL/SQL compilation error.

这是我在 VBA 中尝试 运行 的子程序:

Sub TestSub()

    Dim conn As ADODB.Connection
    Dim rs As ADODB.Recordset

    'Connection details
    Dim strHostName As String
    Dim nPortNum As Integer
    Dim strUser As String
    Dim strPassword As String
    Dim strServiceName As String


    Dim strConnection As String
    Dim strSQL As String

    Set conn = New ADODB.Connection

    '[... set credentials ...]

    'Open Connection
    With conn
        .ConnectionString = "Provider=MSDAORA;" & _
        "Data Source=(DESCRIPTION=(ADDRESS_LIST=" & _
        "(ADDRESS=(PROTOCOL=TCP)(HOST=" & strHostName & ")(PORT=" & nPortNum & ")))(CONNECT_DATA=(SERVICE_NAME=" & strServiceName & ")));" & _
        "User ID=" & strUser & ";Password=" & strPassword & ";"

        .Open

    End With

    Set rs = New ADODB.Recordset

    strSQL = "WITH FUNCTION add_string(p_string IN VARCHAR2) RETURN VARCHAR2 IS l_buffer VARCHAR2(32767); BEGIN l_buffer := p_string || ' works!'; RETURN l_buffer; END ; SELECT add_string('Yes, it') AS outval FROM dual"
    rs.Open strSQL, conn, adOpenStatic, adLockReadOnly

    '[... do stuff with data ...]
    rs.MoveFirst
    Debug.Print rs.Fields(0).Value

    rs.Close
    Set rs = Nothing

    conn.Close
    Set conn = Nothing

End Sub

有什么办法解决这个问题吗? (不幸的是,在数据库中编译函数不是这个特定项目的选项)。

更新: 我应该提到 运行 宁 VBA 代码时我得到的错误:

Run-Time error 3704: Operation is not allowed when the object is closed.

rs.MoveFirst

不幸的是,我无法测试以下内容,因为我这里只有一个 Oracle 11。

理论上应该可以。但是您应该使用 ADODB.Command 将 SQL 按原样直接发送到 Oracle

试试这个:

Dim strConnection As String
Dim strSQL As String

Dim cmdQuery As ADODB.Command


Set conn = New ADODB.Connection

'[... set credentials ...]

'Open Connection
With conn
    .ConnectionString = "Provider=MSDAORA;" & _
    "Data Source=(DESCRIPTION=(ADDRESS_LIST=" & _
    "(ADDRESS=(PROTOCOL=TCP)(HOST=" & strHostName & ")(PORT=" & nPortNum & ")))(CONNECT_DATA=(SERVICE_NAME=" & strServiceName & ")));" & _
    "User ID=" & strUser & ";Password=" & strPassword & ";"

    .Open

End With

Set rs = New ADODB.Recordset

    strSQL = "WITH FUNCTION add_string(p_string IN VARCHAR2) RETURN VARCHAR2 IS l_buffer VARCHAR2(32767); BEGIN l_buffer := p_string || ' works!'; RETURN l_buffer; END ; SELECT add_string('Yes, it') AS outval FROM dual"

Set cmdQuery = New ADODB.Command
cmdQuery.ActiveConnection = conn
cmdQuery.CommandText = strSQL


With rs
    .LockType = adLockPessimistic
    .CursorType = adUseClient
    .CursorLocation = adUseClient
    .Open cmdQuery
End With    

'[... do stuff with data ...]

使用 "Oracle Provider for OLE DB" 而不是 "Microsoft OLE DB Provider for Oracle"。可以从这里下载 Oracle 提供程序:32-bit Oracle Data Access Components (ODAC) and NuGet Downloads

Microsoft 提供程序已 deprecated 多年,未进一步开发,不应再使用。 Oracle 提供程序的当前版本是 12.1,即它应该也支持新的 Oracle 12c 特性。

连接字符串将是 Provider=OraOLEDB.Oracle; ... 而不是 Provider=MSDAORA;...