我的数据如下
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中实现这个逻辑。
数据集包含数百万行,因此我需要通用算法。
答案 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;