oracle 如何在SQL中计算各部分之间的平均计数

ijxebb2r  于 2023-08-03  发布在  Oracle
关注(0)|答案(3)|浏览(99)

我有一堆带有相关计数的销售ID,使用ntile函数将其分成四个不同的组/部分,表数据如下所示:
existing Data of sales
我想写一个SQL来平衡所有四个组的平均计数。如果你观察数据,第四组有两个销售ID,数量巨大,应该分配给其他组,如下所示:
Required data of sales
选择TABLEA.SALESID,TABLEA.COUNT,ntile(4)OVER(ORDER BY null)作为零件编号从TABLEA;

lsmepo6l

lsmepo6l1#

对于固定数量的bucket,可以使用MODEL子句:

select sales_id,
       cnt,
       part_number,
       bucket
FROM (
  SELECT t.*,
         ROW_NUMBER() OVER (ORDER BY cnt DESC) AS rn
  FROM   table_name t
)
MODEL
  DIMENSION BY (rn)
  MEASURES (
    sales_id,
    cnt,
    part_number,
    0 AS bucket,
    0 AS b1,
    0 AS b2,
    0 AS b3,
    0 AS b4
  )
  RULES AUTOMATIC ORDER (
    bucket[1] = 1,
    b1[1] = cnt[1],
    b2[1] = 0,
    b3[1] = 0,
    b4[1] = 0,
    bucket[rn>1] = CASE LEAST(b1[cv()-1], b2[cv()-1], b3[cv()-1], b4[cv()-1])
                   WHEN b1[cv()-1] THEN 1
                   WHEN b2[cv()-1] THEN 2
                   WHEN b3[cv()-1] THEN 3
                   WHEN b4[cv()-1] THEN 4
                   END,
    b1[rn>1] = b1[cv()-1] + CASE bucket[cv()] WHEN 1 THEN cnt[cv()] ELSE 0 END,
    b2[rn>1] = b2[cv()-1] + CASE bucket[cv()] WHEN 2 THEN cnt[cv()] ELSE 0 END,
    b3[rn>1] = b3[cv()-1] + CASE bucket[cv()] WHEN 3 THEN cnt[cv()] ELSE 0 END,
    b4[rn>1] = b4[cv()-1] + CASE bucket[cv()] WHEN 4 THEN cnt[cv()] ELSE 0 END
);

字符集
其中,对于样本数据:

CREATE TABLE table_name (sales_id, cnt, part_number) AS
SELECT  1, 5000, 4 FROM DUAL UNION ALL
SELECT  2, 4000, 4 FROM DUAL UNION ALL
SELECT  3, 3000, 3 FROM DUAL UNION ALL
SELECT  4, 2000, 3 FROM DUAL UNION ALL
SELECT  5, 1000, 3 FROM DUAL UNION ALL
SELECT  6,  500, 2 FROM DUAL UNION ALL
SELECT  7,  400, 2 FROM DUAL UNION ALL
SELECT  8,  300, 2 FROM DUAL UNION ALL
SELECT  9,  200, 1 FROM DUAL UNION ALL
SELECT 10,  100, 1 FROM DUAL UNION ALL
SELECT 11,   10, 1 FROM DUAL;


输出:
| CNT|零件编号|铲斗| BUCKET |
| --|--|--| ------------ |
| 五千|四个|一个| 1 |
| 四千|四个|二个| 2 |
| 三千|三个|三个| 3 |
| 二000年|三个|四个| 4 |
| 一千|三个|四个| 4 |
| 五百|二个|三个| 3 |
| 四百|二个|四个| 4 |
| 三百|二个|四个| 4 |
| 两百|一个|三个| 3 |
| 一百|一个|三个| 3 |
| 十个|一个|四个| 4 |
fiddle

jm81lzqq

jm81lzqq2#

您可以使用PIPELINED函数将行拆分为固定数量的bucket,每次将每个连续行添加到最空的bucket:
给定样本数据:

CREATE TABLE table_name (sales_id, cnt, part_number) AS
SELECT  1, 5000, 4 FROM DUAL UNION ALL
SELECT  2, 4000, 4 FROM DUAL UNION ALL
SELECT  3, 3000, 3 FROM DUAL UNION ALL
SELECT  4, 2000, 3 FROM DUAL UNION ALL
SELECT  5, 1000, 3 FROM DUAL UNION ALL
SELECT  6,  500, 2 FROM DUAL UNION ALL
SELECT  7,  400, 2 FROM DUAL UNION ALL
SELECT  8,  300, 2 FROM DUAL UNION ALL
SELECT  9,  200, 1 FROM DUAL UNION ALL
SELECT 10,  100, 1 FROM DUAL UNION ALL
SELECT 11,   10, 1 FROM DUAL;

字符集
然后你可以创建一个函数和支持的类型:

CREATE TYPE bucket_obj AS OBJECT(
  sales_id    NUMBER,
  cnt         NUMBER,
  part_number NUMBER,
  bucket      NUMBER
);

CREATE TYPE bucket_tbl AS TABLE OF bucket_obj;

CREATE FUNCTION generate_buckets(
  i_num_buckets IN NUMBER
) RETURN bucket_tbl PIPELINED
IS
  v_buckets SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST();
  v_bucket PLS_INTEGER;

  FUNCTION emptiest_bucket RETURN NUMBER
  IS
    v_sz  PLS_INTEGER := v_buckets(1);
    v_idx PLS_INTEGER := 1;
  BEGIN
    FOR i IN 2 .. v_buckets.COUNT LOOP
      IF v_buckets(i) < v_sz THEN
        v_sz := v_buckets(i);
        v_idx := i;
      END IF;
    END LOOP;
    RETURN v_idx;
  END;
BEGIN
  v_buckets.EXTEND(i_num_buckets);
  FOR i IN 1 .. i_num_buckets LOOP
    v_buckets(i) := 0;
  END LOOP;

  FOR rw IN (SELECT sales_id, cnt, part_number
             FROM   table_name
             ORDER BY cnt DESC)
  LOOP
    v_bucket := emptiest_bucket();
    PIPE ROW (bucket_obj(rw.sales_id, rw.cnt, rw.part_number, v_bucket));
    v_buckets(v_bucket) := v_buckets(v_bucket) + rw.cnt;
  END LOOP;
END;
/


然后声明:

SELECT *
FROM   TABLE(generate_buckets(4)) t
ORDER BY bucket, sales_id;


输出:
| CNT|零件编号|铲斗| BUCKET |
| --|--|--| ------------ |
| 五千|四个|一个| 1 |
| 四千|四个|二个| 2 |
| 三千|三个|三个| 3 |
| 五百|二个|三个| 3 |
| 两百|一个|三个| 3 |
| 一百|一个|三个| 3 |
| 二000年|三个|四个| 4 |
| 一千|三个|四个| 4 |
| 四百|二个|四个| 4 |
| 三百|二个|四个| 4 |
| 十个|一个|四个| 4 |
fiddle

7gs2gvoe

7gs2gvoe3#

为了好玩,使用递归查询:

with data(sales_id, cnt, part_number) as (
    SELECT  1, 5000, 4 FROM DUAL UNION ALL
    SELECT  2, 4000, 4 FROM DUAL UNION ALL
    SELECT  3, 3000, 3 FROM DUAL UNION ALL
    SELECT  4, 2000, 3 FROM DUAL UNION ALL
    SELECT  5, 1000, 3 FROM DUAL UNION ALL
    SELECT  6,  500, 2 FROM DUAL UNION ALL
    SELECT  7,  400, 2 FROM DUAL UNION ALL
    SELECT  8,  300, 2 FROM DUAL UNION ALL
    SELECT  9,  200, 1 FROM DUAL UNION ALL
    SELECT 10,  100, 1 FROM DUAL UNION ALL
    SELECT 11,   10, 1 FROM DUAL
),
rdata as (
    select row_number() over(order by cnt desc, sales_id) as rn, d.*
    from data d
),
cte(rn, sales_id, cnt, part_number, b1, b2, b3, b4, bucket) as
(
    select d.rn, d.sales_id, d.cnt, d.part_number, d.cnt as b1, 0 as b2, 0 as b3, 0 as b4,
        1 as bucket
    from rdata d
    where rn = 1
    
    union all
    
    select r.rn, r.sales_id, r.cnt, r.part_number, 
        case 
            when least(b1, b2, b3, b4) = b1
                then b1+r.cnt
            else b1
        end,
        case 
            when least(b1, b2, b3, b4) = b2
                and least(b1, b2, b3, b4) <> b1
                then b2+r.cnt
            else b2
        end,
        case 
            when least(b1, b2, b3, b4) = b3
                and least(b1, b2, b3, b4) <> b1
                and least(b1, b2, b3, b4) <> b2
                then b3+r.cnt
            else b3
        end,
        case 
            when least(b1, b2, b3, b4) = b4
                and least(b1, b2, b3, b4) <> b1
                and least(b1, b2, b3, b4) <> b2
                and least(b1, b2, b3, b4) <> b3
                then b4+r.cnt
            else b4
        end,
        case 
            when least(b1, b2, b3, b4) = b1 then 1
            when least(b1, b2, b3, b4) = b2
                and least(b1, b2, b3, b4) <> b1 then 2
            when least(b1, b2, b3, b4) = b3
                and least(b1, b2, b3, b4) <> b1
                and least(b1, b2, b3, b4) <> b2
                then 3
            else 4
        end
    from cte c
    join rdata r on r.rn = c.rn + 1
    
)
select sales_id, cnt, part_number, bucket from cte
order by bucket, sales_id
;

字符集
| 中国国家电视台|零件号|铲斗| bucket |
| --|--|--| ------------ |
| 五千|四个|一个| 1 |
| 四千|四个|二个| 2 |
| 三千|三个|三个| 3 |
| 五百|二个|三个| 3 |
| 两百|一个|三个| 3 |
| 一百|一个|三个| 3 |
| 二000年|三个|四个| 4 |
| 一千|三个|四个| 4 |
| 四百|二个|四个| 4 |
| 三百|二个|四个| 4 |
| 十个|一个|四个| 4 |

相关问题