Need help here, I've created a function table
在这里需要帮助,我已经创建了一个功能表
SELECT *
FROM TABLE (getsubs_api_v2(639377700080));
but using this script I can only output one row of data, so I'm trying to to use subquery inside TABLE()
but it returns
但是使用这个脚本我只能输出一行数据,所以我试图在TABLE()中使用子查询,但它返回
ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
*Cause:
*Action:
My experiment query below:
我的实验查询如下:
SELECT * FROM TABLE (SELECT getsubs_api_v2(SUBSTR(name, 2,
LENGTH(name)-1)) FROM pin.service_alias_list_t@brm_prod WHERE
rec_id = 1 AND SUBSTR(name, 2,
LENGTH(name)-1) IN ('639377700080', '639373000273', '639373700013',
'639373700020', '639373700038') );
Can you help me enhance this script?
你能帮我改进这个脚本吗?
In case you need the function getsubs_api_v2, please see below.
如果您需要函数getsubs_api_v2,请参阅下文。
CREATE OR REPLACE FUNCTION getsubs_api_v2(mobtel number) RETURN t_tf_tab PIPELINED AS
l_tab t_tf_tab := t_tf_tab();
soap_request VARCHAR2(30000);
soap_respond CLOB;
http_req utl_http.req;
http_resp utl_http.resp;
resp XMLType;
soap_err exception;
v_code VARCHAR2(200);
v_msg VARCHAR2(1800);
v_len number;
v_txt Varchar2(32767);
BEGIN
--UTL_HTTP.SET_PROXY(p_proxy);
-- Define the SOAP request according the the definition of the web service being called
soap_request:= '<?xml version = "1.0" encoding = "UTF-8"?>'||
'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'||
' <soap:Body xmlns:ns1="http://xmlns.oracle.com/SBLMVNE/MVNERetrieveSubscriberDetail/MVNERetrieveSubscriberDetailProcess">'||
' <ns1:retrieveSubscriberDetailRequest>'||
' <ns1:header>'||
' <ns1:InterfaceName></ns1:InterfaceName>'||
' <ns1:InterfaceId></ns1:InterfaceId>'||
' <ns1:CorrelationId></ns1:CorrelationId>'||
' </ns1:header>'||
' <ns1:mvno></ns1:mvno>'||
' <ns1:searchCriteriaType>MSISDN</ns1:searchCriteriaType>'||
' <ns1:searchCriteriaValue>'||mobtel||'</ns1:searchCriteriaValue>'||
' </ns1:retrieveSubscriberDetailRequest>'||
' </soap:Body>'||
'</soap:Envelope>';
http_req:= utl_http.begin_request
( 'http://192.168.0.1:8001/soa-infra/services/Dash/MVNERetrieveSubscriberDetail!1.0*soa_7e28c041-0e10-4cd0-b956-5ba9f6d5e56d/mvneretrievesubscriberdetailprocess_client_ep?wsdl'
, 'POST'
, 'HTTP/1.1'
);
utl_http.set_header(http_req, 'Content-Type', 'text/xml');
utl_http.set_header(http_req, 'Content-Length', length(soap_request));
utl_http.set_header(http_req, 'Download', ''); -- header requirements of particular web service
utl_http.write_text(http_req, soap_request);
http_resp:= utl_http.get_response(http_req);
utl_http.get_header_by_name(http_resp, 'Content-Length', v_len, 1); -- Obtain the length of the response
FOR i in 1..CEIL(v_len/32767) -- obtain response in 32K blocks just in case it is greater than 32K
LOOP
utl_http.read_text(http_resp, v_txt, case when i < CEIL(v_len/32767) then 32767 else mod(v_len,32767) end);
soap_respond := soap_respond || v_txt; -- build up CLOB
END LOOP;
utl_http.end_response(http_resp);
resp:= XMLType.createXML(soap_respond); -- Convert CLOB to XMLTYPE
for y in (
with test_mike as (select resp xml_data from dual)
select x.*, x1.*, x2.*, x3.* from test_mike t,
xmltable(xmlnamespaces('http://xmlns.oracle.com/SBLMVNE/MVNERetrieveSubscriberDetail/MVNERetrieveSubscriberDetailProcess' as "ns3",
'http://schemas.xmlsoap.org/soap/envelope/' as "ns1", 'http://www.w3.org/2005/08/addressing' as "ns2"),
'ns1:Envelope/ns1:Body/ns3:retrieveSubscriberDetailResponse/ns3:subscribers/ns3:Subscriber'
passing t.xml_data
columns subs_id number path 'ns3:subscriberId',
service_num number path 'ns3:serviceNo1',
duo_num number path 'ns3:serviceNo2',
iccid varchar2(30) path 'ns3:serviceNo4',
creation_date varchar2(30) path 'ns3:creationDt',
subs_status xmltype path 'ns3:subscriberStatus',
cos xmltype path 'ns3:cos',
wallets xmltype path 'ns3:wallets/ns3:wallet') x,
xmltable(xmlnamespaces('http://xmlns.oracle.com/SBLMVNE/MVNERetrieveSubscriberDetail/MVNERetrieveSubscriberDetailProcess' as "ns1"),
'ns1:subscriberStatus'
passing x.subs_status
columns mvne_status number path 'ns1:Status',
status_date varchar2(30) path 'ns1:statusDt',
expiration_date varchar2(30) path 'ns1:statusExpiryDt') x1,
xmltable(xmlnamespaces('http://xmlns.oracle.com/SBLMVNE/MVNERetrieveSubscriberDetail/MVNERetrieveSubscriberDetailProcess' as "ns1"),
'ns1:cos'
passing x.cos
columns cos_id number path 'ns1:Id',
cos_name varchar2(10) path 'ns1:Name') x2,
xmltable(xmlnamespaces('http://xmlns.oracle.com/SBLMVNE/MVNERetrieveSubscriberDetail/MVNERetrieveSubscriberDetailProcess' as "ns1"),
'ns1:wallet'
passing x.wallets
columns wallet_id number path 'ns1:Id',
wallet_name varchar2(20) path 'ns1:Name',
wallet_type varchar2(20) path 'ns1:Type',
wallet_balance number path 'ns1:Balance',
wallet_currency varchar2(10) path 'ns1:currency',
wallet_expdt varchar2(30) path 'ns1:expiryDt') x3) loop
--l_tab.extend;
pipe row(t_tf_row(y.subs_id, y.service_num, y.duo_num,y.iccid,y.creation_date,y.mvne_status,y.status_date,y.expiration_date,y.cos_id,y.cos_name,y.wallet_id,y.wallet_name,y.wallet_type,y.wallet_balance,y.wallet_currency,y.wallet_expdt));
end loop;
return;
END;
Okay, so I've revised my query
好的,所以我修改了我的查询
SELECT *
FROM TABLE
(getsubs_api_v2(cursor(select SUBSTR(name, 2, LENGTH(name)-1)
FROM pin.service_alias_list_t@brm_prod
WHERE rec_id = 1
AND SUBSTR(name, 2, LENGTH(name)-1) IN ('639373000273','639377700080')
)));
but this time, I get an error:
但这一次,我收到一个错误:
ORA-06553: PLS-306: wrong number or types of arguments in call to 'GETSUBS_API_V2'
06553. 00000 - "PLS-%s: %s"
*Cause:
*Action:
Error at Line: 3 Column: 4
2 个解决方案
#1
0
The TABLE()
syntax for a table collection expression needs to be passed a collection expression; that can be a subquery but the documentation says:
表集合表达式的TABLE()语法需要传递一个集合表达式;这可以是子查询,但文档说:
The collection_expression can be a subquery, a column, a function, or a collection constructor. Regardless of its form, it must return a collection value—that is, a value whose type is nested table or varray.
collection_expression可以是子查询,列,函数或集合构造函数。无论其形式如何,它都必须返回一个集合值 - 即类型为嵌套表或varray的值。
You are using a subquery which returns multiple pipelined function results, but not as a collection. You could potentially use multiset
to get all the function results into a collection but it would be long-winded and isn't necessary.
您正在使用子查询返回多个流水线函数结果,但不是作为集合。你可能会使用multiset将所有的函数结果都集成到一个集合中,但它会冗长而且不是必需的。
You need to cross-join your real table with a table collection expression that uses the values from the real table:
您需要将实际表与表集合表达式交叉连接,该表达式表达式使用真实表中的值:
SELECT t.*
FROM pin.service_alias_list_t@brm_prod salt
CROSS JOIN TABLE (getsubs_api_v2(SUBSTR(salt.name, 2, LENGTH(salt.name )-1))) t
WHERE salt.rec_id = 1
AND SUBSTR(salt.name, 2, LENGTH(salt.name)-1) IN ('639373000273','639377700080');
I've given your real table an alias of salt
and the table collection expression an alias of t
so you can distinguish between them. Your function is now called for each row in your real table.
我已经为你的真实表提供了盐的别名,而表集合表达式是t的别名,因此你可以区分它们。现在为实际表中的每一行调用您的函数。
Passing string into the function will cause implicit conversion, which isn't ideal; an explicit to_number()
would be better, as long as Oracle doesn't try to evaluate that for substrings that are no in your IN
list and which can't be converted; which it shouldn't do.
将字符串传递给函数将导致隐式转换,这是不理想的;一个明确的to_number()会更好,只要Oracle不尝试评估IN列表中不存在且无法转换的子字符串;它不应该这样做。
Quick demo with a dummy function:
带虚拟功能的快速演示:
create function pipe_demo(p_string varchar2, p_quantity number)
return sys.odcivarchar2list pipelined as
begin
for i in 1..p_quantity loop
pipe row (p_string);
end loop;
return;
end;
/
And table:
和表:
create table demo_table(string varchar2(20), quantity number);
insert into demo_table (string, quantity) values ('First string', 3);
insert into demo_table (string, quantity) values ('Second string', 2);
insert into demo_table (string, quantity) values ('Third string', 4);
And equivalent query:
和等效查询:
select t.column_value
from demo_table dt
cross join table(pipe_demo(dt.string, dt.quantity)) t;
COLUMN_VALUE
--------------------
First string
First string
First string
Second string
Second string
Third string
Third string
Third string
Third string
9 rows selected
#2
0
Remove second SELECT from this line:
从此行中删除第二个SELECT:
SELECT * FROM TABLE (SELECT getsubs_api_v2(SUBSTR(name, 2,
Just:
只是:
SELECT * FROM TABLE ( getsubs_api_v2(SUBSTR(name, 2,
See an example in the documentation how a pipelined function should be called:
https://docs.oracle.com/cd/B28359_01/appdev.111/b28425/pipe_paral_tbl.htm#CIHBGDJD
请参阅文档中有关如何调用流水线函数的示例:https://docs.oracle.com/cd/B28359_01/appdev.111/b28425/pipe_paral_tbl.htm#CIHBGDJD
Example 13-10 How to Pipeline Function Results from One Function to Another
例13-10如何将函数结果从一个函数转换为另一个函数
SELECT * FROM TABLE( f(CURSOR(SELECT * FROM TABLE(g()))) )
;SELECT * FROM TABLE(f(CURSOR(SELECT * FROM TABLE(g()))));
As you see in this example, there is no any SELECT clause between TABLE(
and a pipelined function name f(
.
正如您在此示例中看到的,TABLE之间没有任何SELECT子句(和流水线函数名称f(。
#1
0
The TABLE()
syntax for a table collection expression needs to be passed a collection expression; that can be a subquery but the documentation says:
表集合表达式的TABLE()语法需要传递一个集合表达式;这可以是子查询,但文档说:
The collection_expression can be a subquery, a column, a function, or a collection constructor. Regardless of its form, it must return a collection value—that is, a value whose type is nested table or varray.
collection_expression可以是子查询,列,函数或集合构造函数。无论其形式如何,它都必须返回一个集合值 - 即类型为嵌套表或varray的值。
You are using a subquery which returns multiple pipelined function results, but not as a collection. You could potentially use multiset
to get all the function results into a collection but it would be long-winded and isn't necessary.
您正在使用子查询返回多个流水线函数结果,但不是作为集合。你可能会使用multiset将所有的函数结果都集成到一个集合中,但它会冗长而且不是必需的。
You need to cross-join your real table with a table collection expression that uses the values from the real table:
您需要将实际表与表集合表达式交叉连接,该表达式表达式使用真实表中的值:
SELECT t.*
FROM pin.service_alias_list_t@brm_prod salt
CROSS JOIN TABLE (getsubs_api_v2(SUBSTR(salt.name, 2, LENGTH(salt.name )-1))) t
WHERE salt.rec_id = 1
AND SUBSTR(salt.name, 2, LENGTH(salt.name)-1) IN ('639373000273','639377700080');
I've given your real table an alias of salt
and the table collection expression an alias of t
so you can distinguish between them. Your function is now called for each row in your real table.
我已经为你的真实表提供了盐的别名,而表集合表达式是t的别名,因此你可以区分它们。现在为实际表中的每一行调用您的函数。
Passing string into the function will cause implicit conversion, which isn't ideal; an explicit to_number()
would be better, as long as Oracle doesn't try to evaluate that for substrings that are no in your IN
list and which can't be converted; which it shouldn't do.
将字符串传递给函数将导致隐式转换,这是不理想的;一个明确的to_number()会更好,只要Oracle不尝试评估IN列表中不存在且无法转换的子字符串;它不应该这样做。
Quick demo with a dummy function:
带虚拟功能的快速演示:
create function pipe_demo(p_string varchar2, p_quantity number)
return sys.odcivarchar2list pipelined as
begin
for i in 1..p_quantity loop
pipe row (p_string);
end loop;
return;
end;
/
And table:
和表:
create table demo_table(string varchar2(20), quantity number);
insert into demo_table (string, quantity) values ('First string', 3);
insert into demo_table (string, quantity) values ('Second string', 2);
insert into demo_table (string, quantity) values ('Third string', 4);
And equivalent query:
和等效查询:
select t.column_value
from demo_table dt
cross join table(pipe_demo(dt.string, dt.quantity)) t;
COLUMN_VALUE
--------------------
First string
First string
First string
Second string
Second string
Third string
Third string
Third string
Third string
9 rows selected
#2
0
Remove second SELECT from this line:
从此行中删除第二个SELECT:
SELECT * FROM TABLE (SELECT getsubs_api_v2(SUBSTR(name, 2,
Just:
只是:
SELECT * FROM TABLE ( getsubs_api_v2(SUBSTR(name, 2,
See an example in the documentation how a pipelined function should be called:
https://docs.oracle.com/cd/B28359_01/appdev.111/b28425/pipe_paral_tbl.htm#CIHBGDJD
请参阅文档中有关如何调用流水线函数的示例:https://docs.oracle.com/cd/B28359_01/appdev.111/b28425/pipe_paral_tbl.htm#CIHBGDJD
Example 13-10 How to Pipeline Function Results from One Function to Another
例13-10如何将函数结果从一个函数转换为另一个函数
SELECT * FROM TABLE( f(CURSOR(SELECT * FROM TABLE(g()))) )
;SELECT * FROM TABLE(f(CURSOR(SELECT * FROM TABLE(g()))));
As you see in this example, there is no any SELECT clause between TABLE(
and a pipelined function name f(
.
正如您在此示例中看到的,TABLE之间没有任何SELECT子句(和流水线函数名称f(。