Advertisement
shinemic

PL/SQL 统计频次表中位数

May 28th, 2023
1,654
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PL/SQL 3.86 KB | None | 0 0
  1. -- 频次统计表
  2. CREATE TABLE T_SCORE (
  3.     NUM INTEGER, -- 数值
  4.     FRQ INTEGER  -- 出现次数
  5. );
  6.  
  7. -- 计算频次统计表中位数的函数
  8. CREATE OR REPLACE FUNCTION F_MEDIAN (p_table_name IN VARCHAR2)
  9. RETURN NUMBER
  10. IS
  11.   v_count NUMBER;
  12. BEGIN
  13.     WITH MED AS (
  14.         SELECT NUM,
  15.                FRQ,
  16.                SUM(FRQ) OVER(ORDER BY NUM RANGE UNBOUNDED PRECEDING) FRQ_CUMSUM,
  17.                     -- 频率累加滑窗
  18.                SUM(FRQ) OVER() TOT_CNT,
  19.                     -- 固定值,样本总数
  20.                ROW_NUMBER() OVER(ORDER BY NUM) RN
  21.                     -- 按数值大小计数,为后续计算
  22.         FROM t_score
  23.     ),
  24.     -- MED_FILTER:初筛一下,判断中位数落在哪个区间
  25.     MED_FILTER AS (
  26.         SELECT *
  27.           FROM MED
  28.          WHERE FRQ_CUMSUM >= FLOOR((TOT_CNT + 1) / 2)
  29.     )
  30.     --SELECT * FROM MED;
  31.     SELECT CASE WHEN 2 * FRQ_CUMSUM > TOT_CNT THEN NUM
  32.                     -- 包含两种情况:奇数样本数以及落在区间内
  33.                 ELSE (   SELECT (M1.NUM + M2.NUM) / 2 "MEDIAN"
  34.                            FROM MED M1
  35.                       LEFT JOIN MED M2
  36.                              ON M1.RN + 1 = M2.RN
  37.                           WHERE M1.FRQ_CUMSUM = MED_FILTER.FRQ_CUMSUM
  38.                      )
  39.                     -- 落在区间之间,需要到 MED 表找原始记录
  40.             END AS "MEDIAN" INTO v_count
  41.       FROM MED_FILTER
  42.      WHERE FRQ_CUMSUM = (SELECT MIN(FRQ_CUMSUM) FROM MED_FILTER);
  43.   -- 返回查询到的记录数
  44.   RETURN v_count;
  45. END;
  46.  
  47. -- 模拟 n 从 1 到 2^30,计算 n / m_n
  48. DECLARE
  49.   i NUMBER := 1;
  50.   m NUMBER;
  51. BEGIN
  52.   EXECUTE IMMEDIATE 'TRUNCATE TABLE NEWBIE.T_SCORE';
  53.   WHILE i <= POWER(2, 30) LOOP
  54.     INSERT INTO T_SCORE VALUES (i, i);
  55.     IF BITAND(i, i - 1) = 0 THEN
  56.         m := F_MEDIAN('T_SOCRE');
  57.         -- 为实时输出结果,这里弄了个存储过程
  58.         TO_DBG_FILE('F_MEDIAN.log',
  59.                     LPAD(i, 10) || ' => ' ||
  60.                     RPAD(m, 10) || ' (' ||
  61.                     TO_CHAR(i/m, 'FM999999999999999.000000000000000') || ')');
  62.     END IF;
  63.     i := i + 1;
  64.   END LOOP;
  65. END;
  66.  
  67. -- 实时输出日志到文件
  68. CREATE OR REPLACE PROCEDURE TO_DBG_FILE(p_fname VARCHAR2, p_log VARCHAR2) AS
  69.   l_file UTL_FILE.file_type;
  70. BEGIN
  71.   -- 需提前建好 LOG_DIR 这个 DIRECTORY
  72.   l_file := UTL_FILE.fopen('LOG_DIR', p_fname, 'A');
  73.   UTL_FILE.put_line(l_file, p_log);
  74.   UTL_FILE.fflush(l_file);
  75.   UTL_FILE.fclose(l_file);
  76. END TO_DBG_FILE;
  77.  
  78. ------------------- 实验结果-------------------
  79. ---         1 => 1          (1.000000000000000)
  80. ---         2 => 2          (1.000000000000000)
  81. ---         4 => 3          (1.333333333333333)
  82. ---         8 => 6          (1.333333333333333)
  83. ---        16 => 12         (1.333333333333333)
  84. ---        32 => 23         (1.391304347826087)
  85. ---        64 => 46         (1.391304347826087)
  86. ---       128 => 91         (1.406593406593407)
  87. ---       256 => 181        (1.414364640883978)
  88. ---       512 => 362        (1.414364640883978)
  89. ---      1024 => 724        (1.414364640883978)
  90. ---      2048 => 1449       (1.413388543823326)
  91. ---      4096 => 2897       (1.413876423886779)
  92. ---      8192 => 5793       (1.414120490246850)
  93. ---     16384 => 11586      (1.414120490246850)
  94. ---     32768 => 23171      (1.414181520003453)
  95. ---     65536 => 46341      (1.414212036857211)
  96. ---    131072 => 92682      (1.414212036857211)
  97. ---    262144 => 185364     (1.414212036857211)
  98. ---    524288 => 370728     (1.414212036857211)
  99. ---   1048576 => 741456     (1.414212036857211)
  100. ---   2097152 => 1482911    (1.414212990530113)
  101. ---   4194304 => 2965821    (1.414213467367046)
  102. ---   8388608 => 5931642    (1.414213467367046)
  103. ---  16777216 => 11863284   (1.414213467367046)
  104. ---  33554432 => 23726567   (1.414213526971685)
  105. -----------------------------------------------
  106.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement