gpt4 book ai didi

具有聚合函数的 Oracle 动态 SQL 过程通过 ORDS 调用需要很长时间,但在 SQL Developer 中运行速度很快

转载 作者:行者123 更新时间:2023-12-04 15:21:45 27 4
gpt4 key购买 nike

我一直在努力改进我的程序,所以我开始将它们从使用 || 字符串连接更改为使用绑定(bind)变量。当我将绑定(bind)变量添加到以下过程时,我发现我需要将聚合函数添加到 where 子句,以便搜索数字在逗号分隔的数字列表中的位置。

添加聚合函数 string_to_table_numstring_to_table_varchar2 并更新过程的其他部分以使用绑定(bind)变量后,我尝试向我的 ORDS 端点发出请求。该请求用了 10 多分钟才完成。我认为这是由于聚合函数和 APEX 的组合造成的,因为原始查询的运行时间不到 500 毫秒。为了提高速度,我尝试了几种不同的功能,但我没有任何运气。

如果您有建议,我也会采纳有关如何改进整体查询的建议。我对 Oracle 比较陌生。

ORDS 端点

BEGIN
get_categoryprods2(:commasepproductids, :commasepproductskus, :pcategoryid, :sortby);
END;

参数

commasepproductids IN STRING from URI
commasepproductskus IN STRING from URI
pcategoryid IN STRING from URI
sortby IN STRING from URI
ptopcount in INT

原始查询

create or replace PROCEDURE get_categoryprods (
commasepproductids IN VARCHAR2,
commasepproductskus in varchar2,
pcategoryid IN NUMBER,
sortby IN VARCHAR2
) AS

l_cursor SYS_REFCURSOR;
v_stmt_str STRING(5000);
s_counter NUMBER;
v_productid productnew.productid%TYPE;
v_manufacturerid productmanufacturer.manufacturerid%TYPE;
v_sename productnew.sename%TYPE;
v_name productnew.name%TYPE;
v_summary varchar(3999);
v_freeground productnew.freeground%TYPE;
v_quantitydiscountid productnew.quantitydiscountid%TYPE;
v_sku productnew.sku%TYPE;
v_price productnew.price%TYPE;
v_msrp productnew.msrp%TYPE;
v_cost productnew.cost%TYPE;
v_saleprice productnew.saleprice%TYPE;
v_weight productnew.weight%TYPE;
v_percase productnew.percase%TYPE;
v_relatedproducts varchar(3999);
v_hidepriceuntilcart productnew.hidepriceuntilcart%TYPE;
v_discontinued productnew.discontinued%TYPE;
v_discounttype productnew.discounttype%TYPE;
v_unitofmeasure productnew.unitofmeasure%TYPE;
v_replacement productnew.replacement%TYPE;
v_displayorder productcategory.displayorder%TYPE;
v_friendlyurl urlmapper.friendlyurl%TYPE;
v_sortby varchar2(300);

BEGIN

IF sortby IS NULL OR sortby = 'null' OR sortby = '' THEN
v_sortby := '"p".Discontinued, "pc".DisplayOrder ';
ELSIF sortby = 'PriceAscending' THEN
v_sortby := '"p".discontinued, "p".price ';
ELSIF sortby = 'PriceDescending' THEN
v_sortby := '"p".discontinued, "p".price DESC ';
ELSIF sortby = 'Name' THEN
v_sortby := '"p".discontinued, "p".name ';
ELSE
v_sortby := '"p".discontinued, "pc".displayorder ';
END IF;
v_stmt_str := 'SELECT
"p".productid,
"pm".manufacturerid,
"p".sename,
"p".name,
to_char(substr("p".summary, 0, 3999)),
TO_NUMBER("p".freeground),
"p".quantitydiscountid,
"p".sku,
"p".price,
"p".msrp,
"p".cost,
"p".saleprice,
"p".weight,
"p".percase,
to_char(substr("p".relatedproducts, 0, 3999)),
"p".hidepriceuntilcart,
"p".discontinued,
"p".discounttype,
"p".unitofmeasure,
"p".replacement,
"pc".displayorder,
"um".friendlyurl
FROM
productnew "p"
LEFT OUTER JOIN productcategory "pc" ON "p".productid = "pc".productid
LEFT OUTER JOIN productmanufacturer "pm" ON "p".productid = "pm".productid
LEFT OUTER JOIN urlmapper "um" ON "p".productid = "um".productid
WHERE
("pc".categoryid = ' || pcategoryid || ')
AND "p".productid in ('||commasepproductids||')
--and ("p".productid in ('||commasepproductids||') -- removed because I broke the actual original proc
AND ( "p".deleted = 0 )
AND "p".published = 1 ORDER BY '||v_sortby;

s_counter := 0;
apex_json.open_array;
OPEN l_cursor FOR v_stmt_str;

FETCH l_cursor into
v_productid,
v_manufacturerid,
v_sename,
v_name,
v_summary,
v_freeground,
v_quantitydiscountid,
v_sku,
v_price,
v_msrp,
v_cost,
v_saleprice,
v_weight,
v_percase,
v_relatedproducts,
v_hidepriceuntilcart,
v_discontinued,
v_discounttype,
v_unitofmeasure,
v_replacement,
v_displayorder,
v_friendlyurl;

loop
EXIT WHEN l_cursor%notfound;
s_counter := 1;
apex_json.open_object;
apex_json.write('ProductID', v_productid);
apex_json.write('ManufacturerID', v_manufacturerid);
apex_json.write('SEName', v_sename);
apex_json.write('Name', v_name);
apex_json.write('Summary', v_summary);
apex_json.write('FreeGround', v_freeground);
apex_json.write('QuantityDiscountID', v_quantitydiscountid);
apex_json.write('SKU', v_sku);
apex_json.write('Price', v_price);
apex_json.write('MSRP', v_msrp);
apex_json.write('Cost', v_cost);
apex_json.write('SalePrice', v_saleprice);
apex_json.write('Weight', v_weight);
apex_json.write('PerCase', v_percase);
apex_json.write('RelatedProducts', v_relatedproducts);
apex_json.write('HidePriceUntilCart', v_hidepriceuntilcart);
apex_json.write('Discontinued', v_discontinued);
apex_json.write('QuantityDiscountType', v_discounttype);
apex_json.write('UnitOfMeasure', v_unitofmeasure);
apex_json.write('Replacement', v_replacement);
apex_json.write('DisplayOrder', v_displayorder);
apex_json.write('FriendlyUrl', v_friendlyurl);
apex_json.close_object;
end loop;

IF s_counter = 0 THEN
apex_json.open_object;
apex_json.write('ProductID', 0);
apex_json.write('SEName', 'NOT FOUND');
apex_json.write('Name', 'NOT FOUND');
apex_json.write('Summary', 'NOT FOUND');
apex_json.write('FreeGround', 'NOT FOUND');
apex_json.write('QuantityDiscountID', 0);
apex_json.write('SKU', 'NOT FOUND');
apex_json.write('Price', 0);
apex_json.write('MSRP', 0);
apex_json.write('Cost', 0);
apex_json.write('SalePrice', 0);
apex_json.write('Weight', 0);
apex_json.write('PerCase', 'NOT FOUND');
apex_json.write('RelatedProducts', 'NOT FOUND');
apex_json.write('HidePriceUntilCart', 'NOT FOUND');
apex_json.write('Discontinued', 'NOT FOUND');
apex_json.write('QuantityDiscountType', 'NOT FOUND');
apex_json.write('UnitOfMeasure', 'NOT FOUND');
apex_json.write('Replacement', 'NOT FOUND');
apex_json.write('FriendlyUrl', 'NOT FOUND');
apex_json.close_object;
END IF;

apex_json.close_all;
END get_categoryprods;

**新查询 1 **

create or replace PROCEDURE GET_CATEGORYPRODS2 
(
COMMASEPPRODUCTIDS IN VARCHAR2
, COMMASEPPRODUCTSKUS IN VARCHAR2
, PCATEGORYID IN NUMBER
, SORTBY IN VARCHAR2
, PTOPCOUNT IN VARCHAR2
) AS

l_cursor SYS_REFCURSOR;
v_stmt_str STRING(5000);
s_counter NUMBER;
v_productid productnew.productid%TYPE;
v_manufacturerid productmanufacturer.manufacturerid%TYPE;
v_sename productnew.sename%TYPE;
v_name productnew.name%TYPE;
v_summary varchar(3999);
v_freeground productnew.freeground%TYPE;
v_quantitydiscountid productnew.quantitydiscountid%TYPE;
v_sku productnew.sku%TYPE;
v_price productnew.price%TYPE;
v_msrp productnew.msrp%TYPE;
v_cost productnew.cost%TYPE;
v_saleprice productnew.saleprice%TYPE;
v_weight productnew.weight%TYPE;
v_percase productnew.percase%TYPE;
v_relatedproducts varchar(3999);
v_hidepriceuntilcart productnew.hidepriceuntilcart%TYPE;
v_discontinued productnew.discontinued%TYPE;
v_discounttype productnew.discounttype%TYPE;
v_unitofmeasure productnew.unitofmeasure%TYPE;
v_replacement productnew.replacement%TYPE;
v_displayorder productcategory.displayorder%TYPE;
v_friendlyurl urlmapper.friendlyurl%TYPE;
v_sortby varchar2(300);
type t_categoryprods is table of categoryprod_typ;
l_catprodrow categoryprod_typ;
l_catprods t_categoryprods;

BEGIN
IF sortby IS NULL OR sortby = 'null' OR sortby = '' THEN
v_sortby := '"p".Discontinued, "pc".DisplayOrder ';
ELSIF sortby = 'PriceAscending' THEN
v_sortby := '"p".discontinued, "p".price ';
ELSIF sortby = 'PriceDescending' THEN
v_sortby := '"p".discontinued, "p".price DESC ';
ELSIF sortby = 'Name' THEN
v_sortby := '"p".discontinued, "p".name ';
ELSE
v_sortby := '"p".discontinued, "pc".displayorder ';
END IF;
v_stmt_str := 'SELECT
"p".productid,
"pm".manufacturerid,
"p".sename,
"p".name,
to_char(substr("p".summary, 0, 3999)),
to_number("p".freeground),
"p".quantitydiscountid,
"p".sku,
"p".price,
"p".msrp,
"p".cost,
"p".saleprice,
"p".weight,
"p".percase,
to_char(substr("p".relatedproducts, 0, 3999)),
"p".hidepriceuntilcart,
"p".discontinued,
"p".discounttype,
"p".unitofmeasure,
"p".replacement,
"pc".displayorder,
"um".friendlyurl
FROM
productnew "p"
LEFT OUTER JOIN productcategory "pc" ON "p".productid = "pc".productid
LEFT OUTER JOIN productmanufacturer "pm" ON "p".productid = "pm"
.productid
LEFT OUTER JOIN urlmapper "um" ON "p".productid = "um".productid
WHERE
"pc".categoryid = :pcategoryid
AND ( ( "p".productid IN (SELECT * FROM TABLE (string_to_table_num(:commasepproductids))) OR :commasepproductids IS NULL)
AND (lower("p".sku) IN (SELECT * FROM TABLE(string_to_table_varchar2(:commasepproductskus))) OR :commasepproductskus IS NULL ) )
AND "p".deleted = 0
AND "p".published = 1
ORDER BY :sortby';
s_counter := 0;
apex_json.open_array;
OPEN l_cursor FOR v_stmt_str USING pcategoryid, commasepproductids, commasepproductids, commasepproductskus, commasepproductskus, v_sortby;

FETCH l_cursor into
v_productid,
v_manufacturerid,
v_sename,
v_name,
v_summary,
v_freeground,
v_quantitydiscountid,
v_sku,
v_price,
v_msrp,
v_cost,
v_saleprice,
v_weight,
v_percase,
v_relatedproducts,
v_hidepriceuntilcart,
v_discontinued,
v_discounttype,
v_unitofmeasure,
v_replacement,
v_displayorder,
v_friendlyurl;
loop
EXIT WHEN l_cursor%notfound;
s_counter := 1;
apex_json.open_object;
apex_json.write('ProductID', v_productid);
apex_json.write('ManufacturerID', v_manufacturerid);
apex_json.write('SEName', v_sename);
apex_json.write('Name', v_name);
apex_json.write('Summary', v_summary);
apex_json.write('FreeGround', v_freeground);
apex_json.write('QuantityDiscountID', v_quantitydiscountid);
apex_json.write('SKU', v_sku);
apex_json.write('Price', v_price);
apex_json.write('MSRP', v_msrp);
apex_json.write('Cost', v_cost);
apex_json.write('SalePrice', v_saleprice);
apex_json.write('Weight', v_weight);
apex_json.write('PerCase', v_percase);
apex_json.write('RelatedProducts', v_relatedproducts);
apex_json.write('HidePriceUntilCart', v_hidepriceuntilcart);
apex_json.write('Discontinued', v_discontinued);
apex_json.write('QuantityDiscountType', v_discounttype);
apex_json.write('UnitOfMeasure', v_unitofmeasure);
apex_json.write('Replacement', v_replacement);
apex_json.write('DisplayOrder', v_displayorder);
apex_json.write('FriendlyUrl', v_friendlyurl);
apex_json.close_object;
end loop;

IF s_counter = 0 THEN
apex_json.open_object;
apex_json.write('ProductID', 0);
apex_json.write('SEName', 'NOT FOUND');
apex_json.write('Name', 'NOT FOUND');
apex_json.write('Summary', 'NOT FOUND');
apex_json.write('FreeGround', 'NOT FOUND');
apex_json.write('QuantityDiscountID', 0);
apex_json.write('SKU', 'NOT FOUND');
apex_json.write('Price', 0);
apex_json.write('MSRP', 0);
apex_json.write('Cost', 0);
apex_json.write('SalePrice', 0);
apex_json.write('Weight', 0);
apex_json.write('PerCase', 'NOT FOUND');
apex_json.write('RelatedProducts', 'NOT FOUND');
apex_json.write('HidePriceUntilCart', 'NOT FOUND');
apex_json.write('Discontinued', 'NOT FOUND');
apex_json.write('QuantityDiscountType', 'NOT FOUND');
apex_json.write('UnitOfMeasure', 'NOT FOUND');
apex_json.write('Replacement', 'NOT FOUND');
apex_json.write('FriendlyUrl', 'NOT FOUND');
apex_json.close_object;
END IF;

apex_json.close_all;
END GET_CATEGORYPRODS2;

string_to_table_num

create or replace FUNCTION string_to_table_num (
p VARCHAR2
)
RETURN tab_number
PIPELINED IS
BEGIN
FOR cc IN (SELECT rtrim(regexp_substr(str, '[^,]*,', 1, level), ',') res
FROM (SELECT p || ',' str FROM dual)
CONNECT BY level <= length(str)
- length(replace(str, ',', ''))) LOOP
PIPE ROW(lower(cc.res));
END LOOP;

END;

string_to_table_varchar

create or replace FUNCTION string_to_table_varchar2(p VARCHAR2)
RETURN tab_varchar2
PIPELINED IS
BEGIN
FOR cc IN (SELECT rtrim(regexp_substr(str, '[^,]*,', 1, level), ',') res
FROM (SELECT p || ',' str FROM dual)
CONNECT BY level <= length(str)
- length(replace(str, ',', ''))) LOOP
PIPE ROW(lower(cc.res));
END LOOP;
END;

然后我尝试用以下函数替换 string_to_table 函数

f_convert2

create or replace FUNCTION f_convert2(p_list IN STRING)
RETURN tab_number
PIPELINED
AS
l_string LONG := p_list || ',';
l_comma_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
BEGIN
LOOP
l_comma_index := INSTR(l_string, ',', l_index);
EXIT WHEN l_comma_index = 0;
PIPE ROW ( SUBSTR(l_string, l_index, l_comma_index - l_index) );
l_index := l_comma_index + 1;
END LOOP;
RETURN ;
END f_convert2;

f_convert

create or replace FUNCTION f_convert(p_list IN STRING)
RETURN tab_varchar2
PIPELINED
AS
l_string LONG := p_list || ',';
l_comma_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
BEGIN
LOOP
l_comma_index := INSTR(l_string, ',', l_index);
EXIT WHEN l_comma_index = 0;
PIPE ROW ( SUBSTR(l_string, l_index, l_comma_index - l_index) );
l_index := l_comma_index + 1;
END LOOP;
RETURN;
END f_convert;

如果我从 Postman 或我的 Node 应用程序调用该过程,它会超时或给出 请检查 SQL 语句是否正确形成并执行无误。 SQL 错误代码:ORA-04036:实例使用的 PGA 内存超过 PGA_AGGREGATE_LIMIT。如果我将 apex_json.write 替换为 dbms_output.write_line 并通过 SQL Developer 运行它,它运行良好且速度非常快。

最佳答案

每当使用表函数时,请考虑使用 DYNAMIC_SAMPLING 提示。 Oracle 不知道函数 STRING_TO_TABLE_NUM 返回了多少行,并且可能会使用 8168 行的猜测。动态采样提示指示 Oracle 花费一些额外的解析时间来计算行数,这有望比更好的执行计划节省的时间少得多。

例如,使用这个类型和函数:

create or replace type tab_number as table of number;

create or replace FUNCTION string_to_table_num (
p VARCHAR2
)
RETURN tab_number
PIPELINED IS
BEGIN
FOR cc IN (SELECT rtrim(regexp_substr(str, '[^,]*,', 1, level), ',') res
FROM (SELECT p || ',' str FROM dual)
CONNECT BY level <= length(str)
- length(replace(str, ',', ''))) LOOP
PIPE ROW(lower(cc.res));
END LOOP;

END;
/

下面的简单示例显示 Oracle 将行数高估为 8168 而不是实际值 9:

explain plan for select * from table(string_to_table_num('1,2,3,4,5,6,7,8,9'));
select * from table(dbms_xplan.display);

Plan hash value: 127161297

---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8168 | 16336 | 29 (0)| 00:00:01 |
| 1 | COLLECTION ITERATOR PICKLER FETCH| STRING_TO_TABLE_NUM | 8168 | 16336 | 29 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------

当我们添加动态采样提示时,Oracle 得到了完美的基数:

explain plan for select /*+ dynamic_sampling(2) */ * from table(string_to_table_num('1,2,3,4,5,6,7,8,9'));
select * from table(dbms_xplan.display);

Plan hash value: 127161297

---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 18 | 11 (0)| 00:00:01 |
| 1 | COLLECTION ITERATOR PICKLER FETCH| STRING_TO_TABLE_NUM | 9 | 18 | 11 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

这个解决方案没有直接解决为什么 SQL 在不同上下文中执行不同的问题。但是,如果您可以解决两个巨大的基数问题,那么其他问题可能就不再重要了。

关于具有聚合函数的 Oracle 动态 SQL 过程通过 ORDS 调用需要很长时间,但在 SQL Developer 中运行速度很快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63174998/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com