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;...
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;...