一尘不染

在SQL中不同行的两列中进行计算

sql

我有桌子t1

ID    NAME        AGE GENDER   BALANCE
----- ----- --------- ----- ---------
1001  John         10 M            10
1002  Meena         5 F             0
1003  Nikh         11 M             0
1004  divs          7 F             0
1005  neha          4 F             0

从第二行开始,如果性别为M,则余额(第二行)应为age(2)+ balance(1)

否则Balance(1)-age(2)

最终结构应该像

ID    NAME        AGE GENDER   BALANCE
----- ----- --------- ----- ---------
1001  John         10 M            10
1002  Meena         5 F             5
1003  Nikh         11 M             16
1004  divs          7 F             9
1005  neha          4 F             5

请帮助我查询/过程


阅读 135

收藏
2021-05-16

共1个答案

一尘不染

这样的事情怎么样?

with sample_data as (select 1001 id, 'John' name, 10 age, 'M' gender, 10 balance from dual union all
                     select 1002 id, 'Meena' name, 5 age, 'F' gender, 0 balance from dual union all
                     select 1003 id, 'Nikh' name, 11 age, 'M' gender, 0 balance from dual union all
                     select 1004 id, 'divs' name, 7 age, 'F' gender, 0 balance from dual union all
                     select 1005 id, 'neha' name, 4 age, 'F' gender, 0 balance from dual)
select id,
       name,
       age,
       gender,
       sum(case when gender = 'F' then -1 * age else age end) over (order by id) balance
from   sample_data;

        ID NAME         AGE GENDER    BALANCE
---------- ----- ---------- ------ ----------
      1001 John          10 M              10
      1002 Meena          5 F               5
      1003 Nikh          11 M              16
      1004 divs           7 F               9
      1005 neha           4 F               5

我猜想第一行的余额(这里是id的顺序)是10,因为那是John的年龄,他是男性,而不是一个任意数字。


预计到达时间:这是上述解决方案的替代方案。我 强烈
建议您针对类似生产的数据量测试所有内容(在这里我使用了with子句来模拟一个具有5行的名为sample_data的表,您只需要使用您的表即可)。这样,您可以获得的时间安排应突出显示您的方案中性能最高的方法。希望您的经理不会对事实视而不见(如果他是事实,请逃之fast。

1)没有解析功能的SQL语句:

with sample_data as (select 1001 id, 'John' name, 10 age, 'M' gender, 10 balance from dual union all
                     select 1002 id, 'Meena' name, 5 age, 'F' gender, 0 balance from dual union all
                     select 1003 id, 'Nikh' name, 11 age, 'M' gender, 0 balance from dual union all
                     select 1004 id, 'divs' name, 7 age, 'F' gender, 0 balance from dual union all
                     select 1005 id, 'neha' name, 4 age, 'F' gender, 0 balance from dual)
select sd1.id,
       sd1.name,
       sd1.age,
       sd1.gender,
       sum(case when sd2.gender = 'F' then -1 * sd2.age else sd2.age end) balance
from   sample_data sd1
       inner join sample_data sd2 on (sd1.id >= sd2.id)
group by sd1.id,
         sd1.name,
         sd1.age,
         sd1.gender
order by id;

        ID NAME         AGE GENDER    BALANCE
---------- ----- ---------- ------ ----------
      1001 John          10 M              10
      1002 Meena          5 F               5
      1003 Nikh          11 M              16
      1004 divs           7 F               9
      1005 neha           4 F               5

2)程序性方法(慢速逐行{打哈欠})(不推荐):

create or replace procedure calc_balance1
as
  v_balance number := 0;
  cursor cur is
    with sample_data as (select 1001 id, 'John' name, 10 age, 'M' gender, 10 balance from dual union all
                     select 1002 id, 'Meena' name, 5 age, 'F' gender, 0 balance from dual union all
                     select 1003 id, 'Nikh' name, 11 age, 'M' gender, 0 balance from dual union all
                     select 1004 id, 'divs' name, 7 age, 'F' gender, 0 balance from dual union all
                     select 1005 id, 'neha' name, 4 age, 'F' gender, 0 balance from dual)
    select id,
           name,
           age,
           gender,
           balance
    from   sample_data;
begin
  for rec in cur
  loop
    v_balance := v_balance + case when rec.gender = 'F' then -1 * rec.age 
                                  else rec.age
                             end;
    dbms_output.put_line('id = '||rec.id||', name = '||rec.name||', age = '||rec.age||', gender = '||rec.gender||', balance = '||v_balance);
  end loop;
end calc_balance1;
/

begin
  calc_balance;
end;
/

id = 1001, name = John, age = 10, gender = M, balance = 10
id = 1002, name = Meena, age = 5, gender = F, balance = 5
id = 1003, name = Nikh, age = 11, gender = M, balance = 16
id = 1004, name = divs, age = 7, gender = F, balance = 9
id = 1005, name = neha, age = 4, gender = F, balance = 5

但是,如果您必须为此提供一个过程,则可以将查询与分析函数一起使用,并将其粘贴在ref游标中,例如:

create or replace procedure calc_balance2 (p_refcur out sys_refcursor)
as
begin
  open p_refcur for with sample_data as (select 1001 id, 'John' name, 10 age, 'M' gender, 10 balance from dual union all
                                         select 1002 id, 'Meena' name, 5 age, 'F' gender, 0 balance from dual union all
                                         select 1003 id, 'Nikh' name, 11 age, 'M' gender, 0 balance from dual union all
                                         select 1004 id, 'divs' name, 7 age, 'F' gender, 0 balance from dual union all
                                         select 1005 id, 'neha' name, 4 age, 'F' gender, 0 balance from dual)
                    select id,
                           name,
                           age,
                           gender,
                           sum(case when gender = 'F' then -1 * age else age end) over (order by id) balance
                    from   sample_data
                    order by id;
end calc_balance2;
/

------------------

我看到了你写的程序;这是我的替代方法:

-- mimicking your test_divs table:
create table test_divs as
select 1001 id, 'John' name, 10 age, 'M' gender, 10 balance from dual union all
select 1002 id, 'Meena' name, 5 age, 'F' gender, 0 balance from dual union all
select 1003 id, 'Nikh' name, 11 age, 'M' gender, 0 balance from dual union all
select 1004 id, 'divs' name, 7 age, 'F' gender, 0 balance from dual union all
select 1005 id, 'neha' name, 4 age, 'F' gender, 0 balance from dual;

create or replace procedure t1_d12_v2
as
begin
  merge into test_divs tgt
  using (select id,
                name,
                age,
                gender,
                sum(case when gender = 'F' then -1 * age else age end) over (order by id) balance
         from   test_divs) src
    on (tgt.id = src.id)
  when matched then
    update set tgt.balance = src.balance;
end t1_d12_v2;
/

select * from test_divs;

        ID NAME         AGE GENDER    BALANCE
---------- ----- ---------- ------ ----------
      1001 John          10 M              10
      1002 Meena          5 F               0
      1003 Nikh          11 M               0
      1004 divs           7 F               0
      1005 neha           4 F               0

begin
  t1_d12_v2;
  commit;
end;
/

select * from test_divs;

        ID NAME         AGE GENDER    BALANCE
---------- ----- ---------- ------ ----------
      1001 John          10 M              10
      1002 Meena          5 F               5
      1003 Nikh          11 M              16
      1004 divs           7 F               9
      1005 neha           4 F               5

强烈 建议您使用类似生产的数据测试这两种方法,然后看看哪种方法效果更好。(如果您的经理真的对分析函数毫无用处,那么我会将“
src”子查询交换为我想到的另一个sql语句-一个带有join和group by的语句。)

像您所做的那样逐行进行更新,每次循环时,都会在sql和pl /
sql之间进行两次上下文切换。当您可以在单个sql语句中完成全部操作时,何必麻烦您呢?严重地。

2021-05-16