Oracle SQL/PLSQL:基于货币拆分和求和值的分层查询

Oracle SQL/PLSQL: Hierarchical query to split and sum value based on currency

运气好的话我可以描述我的要求而不会让自己感到困惑...

我有一个多级数据集,用户可以在其中标记要汇总到不同总计中的记录。

数据输入规则:

  1. IF 'Base Unit' 列 (True/False) 被选中(即 True),然后 'Add for Option #' 和 'Remove for Option #' 必须为 NULL。
  2. 如果设置了 'Add for Option #' 或 'Remove for Option #',则 'Base Unit' 列必须为 FALSE。
  3. IF 'Add for Option #' 则设置;选项列表(字母 A-Z,以分号分隔)不能包含在不同级别的相同结构中的字母(例如,对于序列号 = 300,'Add for Option #' 不能包括 'B' 或'D'。相同的规则适用于 'Remove for Option #' 列。
  4. 例如,
  5. IF 'Add for Option #' 或 'Remove for Option #' 则同一行不能包含相同的选项(即字母);对于序列号 = 300,'Add for Option #' 不能包括 'B' 或 'D'.

'Purch Cost/Curr' 列仅为每个结构的最低级别填充 - 这些值已经乘以数量。

我需要一个 PLSQL 函数,它将: - 根据参数 (is_base_ / option_),查找 'Base Unit' 列 = is_base_ 或('Add for Option #' 列或 'Remove for Option #' 列的所有行包含 option_)。例如:

select sequence_no,
       part_no,
       component_part,
       base_unit
       add_for_option,
       remove_for_option
from prod_conf_cost_struct
where (base_unit = is_base_ and base_unit = 'True' 
    or (add_for_option = option_ or remove_for_option = option_));

Here is my NEW attempt:

function calc_cost(
  model_no_ number, 
  revision_ number, 
  sequence_no_ in number, 
  currency_ in varchar2
) return number is
  qty_ number := 0;
  cost_ number := 0;
begin

  select nvl(new_qty, qty), purch_cost 
    into qty_, cost_ 
  from prod_conf_cost_struct_clv
  where model_no = model_no_
    and revision = revision_
    and sequence_no = sequence_no_
    and (purch_curr = currency_ or purch_curr is null);

  if cost_ is null then 
    select sum(calc_cost(model_no, revision, sequence_no, purch_curr)) into cost_ 
    from prod_conf_cost_struct_clv 
    where model_no = model_no_
      and revision = revision_
      and (purch_curr = currency_ or purch_curr is null)
      and part_no in (
        select component_part
        from prod_conf_cost_struct_clv
        where model_no = model_no_
          and revision = revision_
          and sequence_no = sequence_no_);
  end if;
  return qty_ * cost_;
exception when no_data_found then 
  return 0;
end calc_cost;

除了 'Sequence No' 列之外,还有 'Model No' 和 'Revision'。

MODEL_NO - REVISION - SEQUENCE_NO 将构成组合键。

select level, sys_connect_by_path(sequence_no, '->') path, 
     calc_cost(model_no, revision, sequence_no, 'GBP') total_gbp,
     calc_cost(model_no, revision, sequence_no, 'EUR') total_eur,
     calc_cost(model_no, revision, sequence_no, 'USD') total_usd
     calc_cost(model_no, revision, sequence_no, '???') total_other
from prod_conf_cost_struct_clv
where model_no = 35
  and revision = 2
connect by prior component_part = part_no
  and prior model_no = 30
  and prior revision = 2
start with sequence_no = 22500
order by sequence_no

我需要像这样汇总的每种货币的总计(当 Base/Option 列为空白时,它将在 BASE_UNIT = True 时汇总):

恐怕你们把大家弄糊涂了:)

虽然您的要求部分难以理解,但我认为如果我必须处理这样的任务,我会做一件事。我会编写递归函数来计算从树的任何部分到叶子的成本。

下面是类似你的数据的演示:

select prod.*, level, sys_connect_by_path(seq, '->') path, 
       calc_cost(comp) total
  from prod connect by prior comp = part
  start with base = 1;


   SEQ PART COMP        QTY       COST CURR       BASE AFO RFO   LEVEL PATH             TOTAL
------ ---- ---- ---------- ---------- ---- ---------- --- --- ------- ----------- ----------
     1 A    A1            5                          1               1 ->1                850
     2 A1   A11           3                          0 B             2 ->1->2             114
     3 A11  A111          4          2 EUR           0     B;D       3 ->1->2->3            8
     4 A11  A112          2         15 EUR           0               3 ->1->2->4           30
     5 A1   A12           8          7 EUR           0               2 ->1->5              56
    11 B    B1            5                          1               1 ->11               870
    12 B1   B11           3                          0               2 ->11->12           174
    13 B11  B111          4         12 GBP           0               3 ->11->12->13        48
    14 B11  B112          2          5 GBP           0               3 ->11->12->14        10

total 包含组件成本,例如 B15 * (3 * (4 * 12 + 2 * 5)),即 870

函数和示例数据如下:

create or replace function calc_cost(i_comp in varchar2) return number is
  v_qty number := 0;
  v_cost number := 0;
begin
  select qty, cost into v_qty, v_cost from prod where comp = i_comp;
  if v_cost is null then 
    select sum(calc_cost(comp)) into v_cost from prod where part = i_comp;
  end if;
  return v_qty * v_cost;
exception when no_data_found then 
  return 0;
end;

数据:

create table prod(seq, part, comp, qty, cost, curr, base, afo, rfo) as (
    select  1, 'A',   'A1',   5, null, null,  1, null, null  from dual union all
    select  2, 'A1',  'A11',  3, null, null,  0, 'B',  null  from dual union all
    select  3, 'A11', 'A111', 4,    2, 'EUR', 0, null, 'B;D' from dual union all
    select  4, 'A11', 'A112', 2,   15, 'EUR', 0, null, null  from dual union all
    select  5, 'A1',  'A12',  8,    7, 'EUR', 0, null, null  from dual union all
    select 11, 'B',   'B1',   5, null, null,  1, null, null  from dual union all
    select 12, 'B1',  'B11',  3, null, null,  0, null, null  from dual union all
    select 13, 'B11', 'B111', 4,   12, 'GBP', 0, null, null  from dual union all
    select 14, 'B11', 'B112', 2,    5, 'GBP', 0, null, null  from dual );

您没有指定相同的 part / component 是否可以有不同的货币,如果可以,输出结果如何。无论如何,您可以找到这些货币并独立地为每种货币进行计算。您需要将第二个参数添加到函数中并编写类似 where part = i_comp and curr = i_curr or curr is null 的内容。

还有 ask_for_option / remove_for_option 你可能可以在 case when 中处理它们。

我看到你在这个问题上付出了很多努力,但以你目前的问题形式很难回答得更好。您应该提供示例数据,而不仅仅是图像,并根据用户的选择向我们展示您期望的输出结果。

但我希望这个功能可以帮助您解决问题。我假设如果 cost 不为 null 那么我们在叶中,否则函数递归地查找子组件。


编辑:

Lets say that seq = 14 was in EUR not GBP

update prod set curr = 'EUR' where seq = 14;

正如我所说,确切的解决方案取决于您需要的输出。如果您知道所有可能的货币,那么您可以修改函数来处理货币并显示费用,如下所示:

create or replace function calc_cost(i_comp in varchar2, i_curr in varchar2) 
  return number is

  v_qty number := 0;
  v_cost number := 0;
begin

  select qty, cost into v_qty, v_cost from prod 
    where comp = i_comp and (curr = i_curr or curr is null);

  if v_cost is null then 
    select sum(calc_cost(comp, i_curr)) into v_cost 
      from prod where part = i_comp and (curr = i_curr or curr is null);
  end if;
  return v_qty * nvl(v_cost, 0);
exception when no_data_found then 
  return 0;
end;

select seq, part, comp, qty, cost, curr, 
       calc_cost(comp, 'EUR') eur, calc_cost(comp, 'GBP') gbp
  from prod 
  connect by part = prior comp
  start with part = 'B';

   SEQ PART COMP        QTY       COST CURR        EUR        GBP
 ----- ---- ---- ---------- ---------- ---- ---------- ----------
    11 B    B1            5                        150        720
    12 B1   B11           3                         30        144
    13 B11  B111          4         12 GBP           0         48
    14 B11  B112          2          5 EUR          10          0      

零件 B 的价格为 150 欧元和 720 英镑。

您可以在数据的有趣部分找到所有不同的货币,将它们与您的 table 连接起来,然后以这种方式调用函数。结果将是,对于每个 seq,您将获得与不同货币一样多的行。然后您可以使用 listagg() 并在一个单元格中将值显示为 150 EUR; 720 GBP

您还可以创建一些类型对象并将函数修改为 return table 元组(货币,cost_in_this_currency)。问题是你想如何显示数据。或者您可以将价值转换为通用货币,但为此您需要每日 table 比率。