应用业务逻辑的UPDATE COLUMN值

时间:2015-12-10 16:07:13

标签: sql database oracle plsql

我的数据如下

id name rank     
1   X     1       
2   Q     1         
2   T     2          
1   R     2          
1   E     3        

我有业务逻辑需要为id列加权,如下所示:

id  name  rank  weight  
1   X     1      33  
2   Q     1      50  
2   T     2      50  
1   R     2      33  
1   E     3      34 

100必须在所有具有相同ID的记录之间进行划分。

有两条记录,其中id = 2,因此weight = 50+50 有三条记录,其中id = 1,因此权重= 33+33+34 同样,如果id的计数= 4,则权重为25+25+25+25
如果id的计数= 6,则权重为16+16+17+17+17+17
如果id count = 7,则权重为14+14+14+14+14+15+15

我需要在SQL或PL / SQL中实现这个逻辑。

数据集包含数百万行,因此我需要通用算法。

2 个答案:

答案 0 :(得分:2)

作为伪代码我觉得你想要这样的东西。 List<YourObject> posts = (List<YourObject>) gson.fromJson(jsonOutput, new TypeToken<List<YourObject>>(){}.getType()); 将计算具有相同ID的所有项目。在您解决此问题时,请务必确保您没有进行整数除法。

Divisor

这里是一个小提琴作为概念证明。但它并没有进行更新。请注意,我已将列set weight = case when rank <= divisor - (100 - trunc(100e0 / divisor) * divisor) then trunc(100e0 / divisor) else trunc(100e0 / divisor) + 1 end 更改为rank,因为我不确定这是否是Oracle上的保留字。

rnk

http://sqlfiddle.com/#!4/ecdad/7

我对你的问题的原始印象是你需要一个with T as ( select id, rnk, count(*) over (partition by id) as divisor from <your_table> ) select id, rnk, case when rnk <= divisor - (100 - trunc(100e0 / divisor) * divisor) then trunc(100e0 / divisor) else trunc(100e0 / divisor) + 1 end as weight from T order by id, rnk; ,但现在重读之后,我不确定。也许这有效吗?我看到互联网上的其他人声称(某些)CTE可以在(部分)Oracle版本上更新。

update

我做了一些尝试,但无法让with T as ( select *, count(*) over (partition by id) as divisor from <your_table> ) update T set weight = case when rank <= divisor - (100 - trunc(100e0 / divisor) * divisor) then trunc(100e0 / divisor) else trunc(100e0 / divisor) + 1 end 与CTE合作,但这个丑陋的版本还不错:

update

http://sqlfiddle.com/#!4/ecdad/18

按照马修使用update <your_table> set weight = case when rank <= (select count(*) from <your_table> t2 where t2.id = <your_table>.id) - (100 - trunc( 100e0 / (select count(*) from <your_table> t2 where t2.id = <your_table>.id) ) * (select count(*) from <your_table> t2 where t2.id = <your_table>.id) ) then trunc(100e0 / (select count(*) from <your_table> t2 where t2.id = <your_table>.id)) else trunc(100e0 / (select count(*) from <your_table> t2 where t2.id = <your_table>.id)) + 1 end; 的示例,您可以使用此表单进行适用于CTE的更新:

merge

答案 1 :(得分:2)

您不需要PL / SQL。

这是SELECT语句,可根据需要计算weight列。要将结果放入表中,您只需将其包装在MERGE

基本思想是将原始权重计算为100/count(*)并舍入小数值。然后,在每个id内添加所有这些内容,并查看您的价格低于100。例如,如果舍入值的总和为96,则为4,因此您需要在每个ID的前4行中添加1。

WITH test_data AS ( 
      SELECT 1 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 6
      UNION ALL
      SELECT 2 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 3
      UNION ALL
      SELECT 3 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 1
      UNION ALL
      SELECT 4 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 27
     ),
     pass1 AS
       (SELECT d.*,
               100 / COUNT (*) OVER (PARTITION BY d.id) raw_rank,
               FLOOR (100 / COUNT (*) OVER (PARTITION BY d.id)) raw_rank_floor,
               ROW_NUMBER () OVER (PARTITION BY d.id ORDER BY d.RANK) rank_in_id
        FROM   test_data d),
     pass2 AS
       (SELECT p1.*,
               100 - SUM (raw_rank_floor) OVER (PARTITION BY p1.id) adds_needed
        FROM   pass1 p1)
SELECT p2.id,
       p2.name,
       p2.RANK,
       p2.raw_rank_floor + CASE WHEN p2.rank_in_id <= p2.adds_needed THEN 1 ELSE 0 END weight
FROM   pass2 p2
ORDER BY p2.id, p2.RANK;
order by p2.id, p2.rank;

完成更新

要将上述概念应用于更新,只需将其包装在MERGE语句中即可。这是一个完整的例子。此示例matt_target可替代您的表名称。

DROP TABLE matt_target;

CREATE TABLE matt_target ( id number, name varchar2(1), rank number, weight number(3,0) );

INSERT INTO matt_target (id, name, rank ) (
      SELECT 1 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 6
      UNION ALL
      SELECT 2 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 3
      UNION ALL
      SELECT 3 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 1
      UNION ALL
      SELECT 4 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 27
      UNION ALL
      SELECT 5 ID, dbms_random.string('U',1) name, rownum rank FROM DUAL connect by rownum <= 300     );

COMMIT;      

-- This is your answer right here.  Do this.
MERGE INTO matt_target t
USING (
WITH pass1 AS
       (SELECT d.rowid row_id, 
               d.*,
               100 / COUNT (*) OVER (PARTITION BY d.id) raw_rank,
               FLOOR (100 / COUNT (*) OVER (PARTITION BY d.id)) raw_rank_floor,
               ROW_NUMBER () OVER (PARTITION BY d.id ORDER BY d.RANK) rank_in_id
        FROM   matt_target d),
     pass2 AS
       (SELECT p1.*,
               100 - SUM (raw_rank_floor) OVER (PARTITION BY p1.id) adds_needed
        FROM   pass1 p1)
SELECT p2.row_id,
       p2.id,
       p2.name,
       p2.RANK,
       p2.raw_rank_floor + CASE WHEN p2.rank_in_id <= p2.adds_needed THEN 1 ELSE 0 END weight
FROM   pass2 p2
ORDER BY p2.id, p2.RANK ) u
ON ( t.rowid = u.row_id )
WHEN MATCHED THEN UPDATE SET t.weight = u.weight;

-- Check the results.
SELECT * FROM matt_target;
相关问题