使用pl/sql记录类型显示仓库中产品的平均数量
我需要显示一个产品名称列表,其中ID在100-105之间,所有仓库的平均数量,以及产品数量最多的仓库的名称(对于每个产品)。唯一的问题是平均值(f_avg)不起作用,相反,f_avg显示的是最大数量的产品 输出示例为:使用pl/sql记录类型显示仓库中产品的平均数量,sql,oracle,plsql,Sql,Oracle,Plsql,我需要显示一个产品名称列表,其中ID在100-105之间,所有仓库的平均数量,以及产品数量最多的仓库的名称(对于每个产品)。唯一的问题是平均值(f_avg)不起作用,相反,f_avg显示的是最大数量的产品 输出示例为: 金斯敦HyperX捕食者平均产品:173种,主要分布在旧金山 英特尔至强E5-2687W V4平均产品:90种主要在多伦多 EVGA 12G-P4-3992-KR平均产品:178种主要分布在旧金山 这是我的代码,任何人都知道平均值有什么问题,我也有一个错误: ORA-00979:
DECLARE
CURSOR cur_emp_detail IS
SELECT pproduct_name, avg_quantity, (SELECT w.warehouse_name FROM warehouses w WHERE w.warehouse_id = (SELECT i.warehouse_id FROM inventories i join products p on p.product_id = i.product_id HAVING i.quantity = MAX (i.quantity))) as ware
FROM (SELECT p.product_name as pproduct_name, AVG (i.quantity) AS avg_quantity
FROM products p JOIN inventories i ON p.product_id = i.product_id
WHERE (p.product_id BETWEEN 100 AND 105)
GROUP BY p.product_name);
TYPE type_record_type IS RECORD (
emp_f_name products.product_name%TYPE,
emp_avg inventories.quantity%type,
emp_wh warehouses.warehouse_name%type);
emp_rec_type type_record_type;
BEGIN
OPEN cur_emp_detail;
LOOP
FETCH cur_emp_detail INTO emp_rec_type;
EXIT WHEN cur_emp_detail%NOTFOUND;
dbms_output.Put_line(emp_rec_type.emp_f_name||' average product: '||emp_rec_type.emp_avg||' mostly found in '||emp_rec_type.emp_wh);
END LOOP;
CLOSE cur_emp_detail;
END;
/
使用
批量收集
比在游标中循环要高效得多。这减少了上下文切换的数量,并且仍然允许您将每一行作为记录类型引用
DECLARE
TYPE product_rec IS RECORD
(
emp_f_name products.product_name%TYPE,
emp_avg inventories.quantity%TYPE,
emp_wh warehouses.warehouse_name%TYPE
);
TYPE product_t IS TABLE OF product_rec;
l_products product_t;
BEGIN
SELECT product_name, avg_quantity, warehouse_name
BULK COLLECT INTO l_products
FROM (SELECT p.product_name,
AVG (i.quantity) OVER (PARTITION BY p.product_id) AS avg_quantity,
w.warehouse_name,
ROW_NUMBER ()
OVER (PARTITION BY PRODUCT_NAME ORDER BY QUANTITY DESC, WAREHOUSE_NAME) warehouse_order
FROM products p
JOIN inventories i ON p.product_id = i.product_id
JOIN warehouses w ON i.warehouse_id = w.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105))
WHERE warehouse_order = 1;
FOR i IN 1 .. l_products.COUNT
LOOP
DBMS_OUTPUT.Put_line (
l_products (i).emp_f_name
|| ' average product: '
|| l_products (i).emp_avg
|| ' mostly found in '
|| l_products (i).emp_wh);
END LOOP;
END;
/
使用
批量收集
比在游标中循环要高效得多。这减少了上下文切换的数量,并且仍然允许您将每一行作为记录类型引用
DECLARE
TYPE product_rec IS RECORD
(
emp_f_name products.product_name%TYPE,
emp_avg inventories.quantity%TYPE,
emp_wh warehouses.warehouse_name%TYPE
);
TYPE product_t IS TABLE OF product_rec;
l_products product_t;
BEGIN
SELECT product_name, avg_quantity, warehouse_name
BULK COLLECT INTO l_products
FROM (SELECT p.product_name,
AVG (i.quantity) OVER (PARTITION BY p.product_id) AS avg_quantity,
w.warehouse_name,
ROW_NUMBER ()
OVER (PARTITION BY PRODUCT_NAME ORDER BY QUANTITY DESC, WAREHOUSE_NAME) warehouse_order
FROM products p
JOIN inventories i ON p.product_id = i.product_id
JOIN warehouses w ON i.warehouse_id = w.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105))
WHERE warehouse_order = 1;
FOR i IN 1 .. l_products.COUNT
LOOP
DBMS_OUTPUT.Put_line (
l_products (i).emp_f_name
|| ' average product: '
|| l_products (i).emp_avg
|| ' mostly found in '
|| l_products (i).emp_wh);
END LOOP;
END;
/
我建议使用如下子查询的结构化方法 1)生成明细数据的子查询:产品、仓库和数量 请注意,我假设表
存货
有一个主键产品标识,仓库标识
,如果没有主键,您必须通过分组产品标识,仓库标识
额外聚合数量
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105);
PRODUCT_ WAREHOUSE_NAM QUANTITY
-------- ------------- ----------
Kingston San Francisco 173
EVGA Toronto 178
Intel Toronto 70
Intel Toronto 90
Kingston Toronto 173
2)使用分析函数计算平均数量和仓库等级
with prd as (
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105)
),
prd2 as (
select
PRODUCT_NAME, WAREHOUSE_NAME, QUANTITY,
avg(QUANTITY) over (partition by PRODUCT_NAME) avg_quantity,
row_number() over (partition by PRODUCT_NAME order by QUANTITY desc, WAREHOUSE_NAME) warehouse_order
from prd)
select * from prd2;
PRODUCT_ WAREHOUSE_NAM QUANTITY AVG_QUANTITY WAREHOUSE_ORDER
-------- ------------- ---------- ------------ ---------------
EVGA Toronto 178 178 1
Intel Toronto 90 80 1
Intel Toronto 70 80 2
Kingston San Francisco 173 173 1
Kingston Toronto 173 173 2
3)仅获取每个产品的顶级仓库记录
with prd as (
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105)
),
prd2 as (
select
PRODUCT_NAME, WAREHOUSE_NAME, QUANTITY,
avg(QUANTITY) over (partition by PRODUCT_NAME) avg_quantity,
row_number() over (partition by PRODUCT_NAME order by QUANTITY desc, WAREHOUSE_NAME) warehouse_order
from prd)
select PRODUCT_NAME, WAREHOUSE_NAME, AVG_QUANTITY
from prd2
where warehouse_order = 1;
PRODUCT_ WAREHOUSE_NAM AVG_QUANTITY
-------- ------------- ------------
EVGA Toronto 178
Intel Toronto 80
Kingston San Francisco 173
如果您坚持使用游标和记录类型,那么核心逻辑就是上面的查询
注二:
使用MAX(quantity)
获得顶级记录是一种幼稚的方法,如果你有联系,那么这种方法会失败,即MAX等于多条记录!
请参阅我的实现,其中,如果是领带,则显示具有较低名称的仓库
order by QUANTITY desc, WAREHOUSE_NAME
可以按NAME
s进行分组,但如果有更多同名产品,则分组失败-通常按ID
s进行分组
样本数据
create table products as
select 100 product_id, 'Kingston' product_name from dual union all
select 101 product_id, 'Intel ' product_name from dual union all
select 102 product_id, 'EVGA ' product_name from dual;
create table inventories as
select 100 product_id, 173 quantity, 1 warehouse_id from dual union all
select 100 product_id, 173 quantity, 2 warehouse_id from dual union all
select 101 product_id, 90 quantity, 2 warehouse_id from dual union all
select 101 product_id, 70 quantity, 2 warehouse_id from dual union all
select 102 product_id, 178 quantity, 2 warehouse_id from dual;
create table warehouses as
select 1 warehouse_id, 'San Francisco' warehouse_name from dual union all
select 2 warehouse_id, 'Toronto' warehouse_name from dual;
我建议使用如下子查询的结构化方法 1)生成明细数据的子查询:产品、仓库和数量 请注意,我假设表
存货
有一个主键产品标识,仓库标识
,如果没有主键,您必须通过分组产品标识,仓库标识
额外聚合数量
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105);
PRODUCT_ WAREHOUSE_NAM QUANTITY
-------- ------------- ----------
Kingston San Francisco 173
EVGA Toronto 178
Intel Toronto 70
Intel Toronto 90
Kingston Toronto 173
2)使用分析函数计算平均数量和仓库等级
with prd as (
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105)
),
prd2 as (
select
PRODUCT_NAME, WAREHOUSE_NAME, QUANTITY,
avg(QUANTITY) over (partition by PRODUCT_NAME) avg_quantity,
row_number() over (partition by PRODUCT_NAME order by QUANTITY desc, WAREHOUSE_NAME) warehouse_order
from prd)
select * from prd2;
PRODUCT_ WAREHOUSE_NAM QUANTITY AVG_QUANTITY WAREHOUSE_ORDER
-------- ------------- ---------- ------------ ---------------
EVGA Toronto 178 178 1
Intel Toronto 90 80 1
Intel Toronto 70 80 2
Kingston San Francisco 173 173 1
Kingston Toronto 173 173 2
3)仅获取每个产品的顶级仓库记录
with prd as (
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105)
),
prd2 as (
select
PRODUCT_NAME, WAREHOUSE_NAME, QUANTITY,
avg(QUANTITY) over (partition by PRODUCT_NAME) avg_quantity,
row_number() over (partition by PRODUCT_NAME order by QUANTITY desc, WAREHOUSE_NAME) warehouse_order
from prd)
select PRODUCT_NAME, WAREHOUSE_NAME, AVG_QUANTITY
from prd2
where warehouse_order = 1;
PRODUCT_ WAREHOUSE_NAM AVG_QUANTITY
-------- ------------- ------------
EVGA Toronto 178
Intel Toronto 80
Kingston San Francisco 173
如果您坚持使用游标和记录类型,那么核心逻辑就是上面的查询
注二:
使用MAX(quantity)
获得顶级记录是一种幼稚的方法,如果你有联系,那么这种方法会失败,即MAX等于多条记录!
请参阅我的实现,其中,如果是领带,则显示具有较低名称的仓库
order by QUANTITY desc, WAREHOUSE_NAME
可以按NAME
s进行分组,但如果有更多同名产品,则分组失败-通常按ID
s进行分组
样本数据
create table products as
select 100 product_id, 'Kingston' product_name from dual union all
select 101 product_id, 'Intel ' product_name from dual union all
select 102 product_id, 'EVGA ' product_name from dual;
create table inventories as
select 100 product_id, 173 quantity, 1 warehouse_id from dual union all
select 100 product_id, 173 quantity, 2 warehouse_id from dual union all
select 101 product_id, 90 quantity, 2 warehouse_id from dual union all
select 101 product_id, 70 quantity, 2 warehouse_id from dual union all
select 102 product_id, 178 quantity, 2 warehouse_id from dual;
create table warehouses as
select 1 warehouse_id, 'San Francisco' warehouse_name from dual union all
select 2 warehouse_id, 'Toronto' warehouse_name from dual;
值得一提的是,大容量收集会消耗更多内存,这取决于您可能遇到错误ORA-04030或ORA-04031的数据量。在这种情况下,您可以考虑使用代码<代码>限制>代码>批量收集到 <代码>批量收集< /代码>增加复杂性,如果您期望的数据集不大,那么正常循环总是更好的。仅选择了~5种产品的信息,因此<10行不应占用太多内存。尽管在子查询中添加了
GROUP BY i.warehouse\u id
,但仍会失败,因为ORA-00979:没有GROUP BY表达式
-具有i.quantity=MAX(i.quantity)不允许使用
,因为i.quantity
不是分组依据
列。简单的自我测试。谢谢@marmite。我更新了查询以使用类似于您的查询。值得一提的是,批量收集会消耗更多内存,这取决于您可能遇到错误ORA-04030或ORA-04031的数据量。在这种情况下,您可以考虑使用代码<代码>限制>代码>批量收集到 <代码>批量收集< /代码>增加复杂性,如果您期望的数据集不大,那么正常循环总是更好的。仅选择了~5种产品的信息,因此<10行不应占用太多内存。尽管在子查询中添加了GROUP BY i.warehouse\u id
,但仍会失败,因为ORA-00979:没有GROUP BY表达式
-具有i.quantity=MAX(i.quantity)不允许使用
,因为i.quantity
不是分组依据
列。简单的自我测试。谢谢@marmite。我更新了查询以使用类似于您的查询。