我有一个简单的表格:
CREATE TABLE "MY_TABLE" (
id varchar2(255) NOT NULL PRIMARY KEY,
name varchar2(255),
website varchar2(500)
);
website
的格式可以是https://www.google.com
,如果是,那么我需要更新该行并将其设置为google.com
。
然后,我需要计算有多少个name
具有相同的更新域。我需要尽可能高效地完成这项工作,因此我正在尝试使用游标和函数。如果我有:
insert into MY_TABLE2(id, name, website) values ('1', 'a', 'dom1');
insert into MY_TABLE2(id, name, website) values ('2', 'b', 'dom2');
insert into MY_TABLE2(id, name, website) values ('3', 'c', 'dom1');
insert into MY_TABLE2(id, name, website) values ('4', 'd', null);
好的代码应该这样做:
select mt1.name AS "SPOT NAME", (select count(mt2.id)
from my_table2 mt2
where mt1.website = mt2.website )
from my_table2 mt1;
我有以下代码:
这将检查传递的website
是否满足域的条件(假设regex是ok的):
CREATE OR REPLACE FUNCTION is_domain_format(website my_table.website%type) RETURN varchar2 AS
v_is_in_domain_format boolean;
v_bool_as_varchar varchar2(5);
begin
v_is_in_domain_format := regexp_like(website, '^[a-z0-9][-a-z.0-9]*[a-z0-9]$') and not regexp_like(website, '\.\.');
IF v_is_in_domain_format THEN
v_bool_as_varchar := 'true';
ELSE
v_bool_as_varchar := 'false';
END IF;
RETURN v_bool_as_varchar;
end is_domain_format;
然后我们将website
实际格式化为域:
CREATE OR REPLACE FUNCTION formate_url (website_ my_table.website%type) RETURN varchar2 AS
v_domain varchar2(255) default null;
BEGIN
v_domain := REGEXP_REPLACE(website_, '(http[s]?://)?(www\.)?(.*?)((/|:)(.)*|$)', '\3');
RETURN v_domain;
END formate_url;
然后我有一个嵌套表:
CREATE TYPE t_spot AS OBJECT (
v_id varchar2(255),
v_name varchar2(255),
v_domain varchar2(255)
);
最后是一个流水线函数:
CREATE TYPE t_spot_tab IS TABLE OF t_spot;
create or replace function f_get_spots_piped
return t_spot_tab PIPELINED as
type r_spot is record ( v_id my_table.id%type,
v_website my_table.website%type);
cursor c_spots_unformated is select id,website
from my_table
where website is not null and is_domain_format(website) = 'false';
begin
for spot_unformated_domain in c_spots_unformated loop
update my_table set website = formate_url(spot_unformated_domain.website) where spot_unformated_domain.id = id;
end loop;
for i in 1 .. 5 loop
--this is only for a test...
PIPE ROW (t_spot(1, 'a','b'));
end loop;
RETURN;
end;
我不明白为什么会出现以下错误:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SYS.F_GET_SPOTS_PIPED", line 13
ORA-06512: at "SYS.F_GET_SPOTS_PIPED", line 13
14551. 00000 - "cannot perform a DML operation inside a query "
*Cause: DML operation like insert, update, delete or select-for-update
cannot be performed inside a query or under a PDML slave.
*Action: Ensure that the offending DML operation is not performed or
use an autonomous transaction to perform the DML operation within
the query or PDML slave.
这是故意的还是我在某个地方犯了错?
我的意思是,如果这个异常是预期的,我怎么能写PL/SQL函数,将有效地更新和选择?
最后,我需要以下内容:
Name| Domain | Number_of_occurances_of_that_domain
因此,具有管道函数的查询应该看起来像这样:
select myTable.v_name as "Name", myTable.v_domain as "Domain", (select count(myTable1.v_id)
from f_get_spots_piped() myTable1 where myTable.v_id=myTable1.v_id) as "Number_of_occurances_of_that_domain "
编辑1
谢谢@Litlefoot给我很好的解释!
然而,我已经尝试了这两种选择,他们都失败了,与以前类似的信息。
尝试1。
在管道函数中添加了pragma_transaction,如下所示:
create or replace function f_get_spots_piped
return t_spot_tab PIPELINED as
pragma autonomous_transaction;
尝试2.
创建一个将执行更新的过程,然后从管道函数调用它。
create or replace procedure update_spots as
pragma autonomous_transaction;
type r_spot is record ( v_id my_table.id%type,
v_website my_table.website%type);
cursor c_spots_unformated is select id,website
from my_table
where website is not null and is_domain_format(website) = 'false';
begin
for spot_unformated_domain in c_spots_unformated loop
update my_table set website = formate_url(spot_unformated_domain.website) where spot_unformated_domain.id = id;
end loop;
end;
和原始功能:
CREATE TYPE t_spot_tab IS TABLE OF t_spot;
create or replace function f_get_spots_piped
return t_spot_tab PIPELINED as
begin
update_spots;
for i in 1 .. 5 loop
PIPE ROW (t_spot('a','b'));
end loop;
RETURN;
end;
当我这样做时,它失败了:select * from f_get_spots_piped() ;
再次感谢你的回答,希望你能从这里给我指出一个好的方向,因为我浪费了很多时间来设置这个查询:(
1条答案
按热度按时间mrphzbgm1#
错误描述对此进行了解释;虽然,你不明白它说的是什么,这里有一个简单的例子:
这是一个示例表:
函数更新该表并将作业设置为小写,返回更新的行数。这基本上类似于你的函数:
如果在查询中调用函数(即在
select
语句中),您将得到错误:在这个函数中执行的DML显然是
update
。错误提示你不能在查询中做**,查询是select f_test from dual
。但是,如果您在PL/SQL中调用该函数,则可以工作:
让我们恢复更改(即作业再次大写):
错误描述还说,如果函数被声明为自治事务,你可以克服这个问题。那是什么?
自治事务允许您离开调用事务的上下文,执行独立事务,并返回到调用事务而不影响其状态。自治事务没有到调用事务的链接,因此只有提交的数据可以由两个事务共享。
(Tim Hall比我描述得更好)。
好吧,让我们来做:
现在,您可以从查询中调用该函数(请记住,它之前引发了一个错误):
表格内容:
就是这样现在,这取决于你是否可以将函数设置为自治事务(通常,我们在用于日志记录目的的过程或应该返回顺序和非无间隙数据的函数中使用它)。最好让函数 * 保持原样 * 并在PL/SQL中使用它;或者,切换到一个过程并在其中执行DML。