利用Oracle dbms_pipe实现存储过程之间的通信

63 篇文章 6 订阅

应用程序开发人员的需求是这样的:
1. 根据条件给每一个国家的商品生成唯一7位随机代码,不同国家之间的商品代码可以相同
2. 如果输入标准分隔符的字符串,则解析该字符串作为需要生成的商品ID,为其生成代码,否则为商品表中所有商品ID生成代码
3. 代码的每一位要符合相应的规则,例如第一位的规则是[0123],则这位只能是0、1、2、3中的一个数
4. 由于可能一次生成大量的代码,这个过程需要较长时间,所以需要用进度条提示生成进度
5. 可以在生成过程执行中终止过程
6. 返回需要生成的代码个数和实际生成的代码个数
7. 每次提交的个数可以通过参数定义,例如共要生成10万的代码,每次提交1000个

 

设计思路:
1. 为每个国家预生成0到9999999一千万个随机数作为候选代码池表,每生成一个代码就从代码池中删除一个,避免查重操作
2. 用一个存储过程生成代码,另一个过程用来终止生成过程,两个过程间用dbms_pipe进行通信
3. 用一个函数返回当前已经生成的代码个数,供显示进度条的外部程序调用,生成代码过程与该函数用dbms_pipe进行通信
4. 用bulk collect批量处理提高效率

源代码:

[c-sharp]  view plain  copy
  1. -- 1. 生成可用代码池,重复执行过程为每一个国家生成一个可用代码池表,表名为org_code_加上两位国家代码  
  2. CREATE OR REPLACE PROCEDURE p_org_code  
  3. IS  
  4. BEGIN  
  5.    -- tmp是一个提交后删除行的全局临时表  
  6.    FOR i IN 0 .. 9999999  
  7.    LOOP  
  8.       INSERT INTO tmp  
  9.            VALUES (LPAD (i, 7, '0'));  
  10.    END LOOP;  
  11.   
  12.    INSERT INTO org_code_00  
  13.       SELECT   *  
  14.           FROM tmp  
  15.       ORDER BY DBMS_RANDOM.VALUE;  
  16.   
  17.    COMMIT;  
  18. END;  
  19.   
  20. -- 2. 代码生成过程  
  21. CREATE OR REPLACE PROCEDURE p_gen_code (  
  22.    p_country_code   IN       VARCHAR2,              -- 国家代码  
  23.    p_p1             IN       VARCHAR2,                          -- 第一位规则字符串,如012345  
  24.    p_p2             IN       VARCHAR2,                          -- 第二位规则字符串,如012345  
  25.    p_p3             IN       VARCHAR2,                          -- 第三位规则字符串,如012345  
  26.    p_p4             IN       VARCHAR2,                          -- 第四位规则字符串,如012345  
  27.    p_p5             IN       VARCHAR2,                          -- 第五位规则字符串,如012345  
  28.    p_p6             IN       VARCHAR2,                          -- 第六位规则字符串,如012345  
  29.    p_p7             IN       VARCHAR2,                          -- 第七位规则字符串,如012345  
  30.    p_instr          IN       VARCHAR2,                          -- 商品ID字符串,用,做分隔符   
  31.    p_count          IN       NUMBER DEFAULT 1000,   -- 每次提交的个数  
  32.    r_outstr         OUT      VARCHAR2               -- 输出需要生成的代码个数和实际生成的代码个数  
  33. )  
  34. IS  
  35.    l_id                       DBMS_SQL.varchar2_table;  
  36.    l_mc                       DBMS_SQL.varchar2_table;  
  37.    l_code                     DBMS_SQL.varchar2_table;  
  38.    l_mc_str                   VARCHAR2 (255);  
  39.    l_code_str                 VARCHAR2 (7);  
  40.    l_id_count                 INT                     := 0;  
  41.    l_code_count               INT                     := 0;  
  42.    l_idx                      INT;  
  43.    l_start                    INT                     := 1;  
  44.    l_substr                   VARCHAR2 (32);  
  45.    l_instr                    VARCHAR2 (2000);  
  46.    l_length                   INT;  
  47.    l_tablename                VARCHAR2 (30)  := 'org_code_' || p_country_code;  
  48.    l_sql                      VARCHAR2 (2000);  
  49.    l_count                    INT                     := 0;  
  50.    l_pointer                  INT                     := 0;  
  51.    l_real_count               INT                     := 0;  
  52.    -- IPC  
  53.    l_pipename        CONSTANT VARCHAR2 (12)           := 'mypipe';  
  54.    l_pipe_getcount   CONSTANT VARCHAR2 (12)           := 'getcount';  
  55.    l_pipe_retcount   CONSTANT VARCHAR2 (12)           := 'retcount';  
  56.    l_send_result              INT;  
  57. BEGIN  
  58.    DBMS_PIPE.PURGE (l_pipename);  
  59.   
  60.    IF p_instr IS NULL  
  61.    THEN  
  62.       BEGIN  
  63.          SELECT ID, mc  
  64.          BULK COLLECT INTO l_id, l_mc  
  65.            FROM product;  
  66.   
  67.          l_id_count := l_id.COUNT;  
  68.          l_sql :=  
  69.                'select code1 from (select code1 from '  
  70.             || l_tablename  
  71.             || ' where instr('''  
  72.             || p_p1  
  73.             || ''',substr(code1,1,1)) > 0 and instr('''  
  74.             || p_p2  
  75.             || ''',substr(code1,2,1)) > 0 and instr('''  
  76.             || p_p3  
  77.             || ''',substr(code1,3,1)) > 0 and instr('''  
  78.             || p_p4  
  79.             || ''',substr(code1,4,1)) > 0 and instr('''  
  80.             || p_p5  
  81.             || ''',substr(code1,5,1)) > 0 and instr('''  
  82.             || p_p6  
  83.             || ''',substr(code1,6,1)) > 0 and instr('''  
  84.             || p_p7  
  85.             || ''',substr(code1,7,1)) > 0 where rownum <= '  
  86.             || l_id_count;  
  87.   
  88.          EXECUTE IMMEDIATE l_sql  
  89.          BULK COLLECT INTO l_code;  
  90.   
  91.          l_code_count := l_code.COUNT;  
  92.   
  93.          WHILE (l_count < l_code_count)  
  94.          LOOP  
  95.             IF DBMS_PIPE.receive_message (l_pipename, 0) = 0  
  96.             THEN  
  97.                DBMS_PIPE.unpack_message (l_pipebuf);  
  98.                EXIT WHEN l_pipebuf = 'stop';  
  99.             END IF;  
  100.   
  101.             IF DBMS_PIPE.receive_message (l_pipe_getcount, 0) = 0  
  102.             THEN  
  103.                DBMS_PIPE.PURGE (l_pipe_retcount);  
  104.                dbms_pipe_pack_message (TO_CHAR (l_count) || '|running');  
  105.                l_send_result := DBMS_PIPE.send_message (l_pipe_retcount);  
  106.             END IF;  
  107.   
  108.             l_real_count :=  
  109.                            LEAST (p_count, l_code_count - l_pointer * p_count);  
  110.   
  111.             FOR i IN 1 .. l_real_count  
  112.             LOOP  
  113.                INSERT INTO product_code  
  114.                     VALUES (l_code (l_pointer * p_count + i),  
  115.                             l_mc (l_pointer * p_count + i),  
  116.                             p_country_code);  
  117.   
  118.                EXECUTE IMMEDIATE    'delete from '  
  119.                                  || l_tablename  
  120.                                  || ' where code1=:x'  
  121.                            USING l_code (l_pointer * p_count + i);  
  122.   
  123.                UPDATE product  
  124.                   SET status = 1  
  125.                 WHERE ID = l_id (l_pointer * p_count + i);  
  126.             END LOOP;  
  127.   
  128.             COMMIT;  
  129.             l_pointer := pointer + 1;  
  130.             l_count := l_count + l_real_count;  
  131.          END LOOP;  
  132.   
  133.          DBMS_PIPE.PURGE (l_pipe_retcount);  
  134.          DBMS_PIPE.pack_message (TO_CHAR (l_count) || '|end');  
  135.          l_send_result := DBMS_PIPE.send_message (l_pipe_retcount);  
  136.          r_outstr := l_id_count || '|' || l_count;  
  137.          DBMS_OUTPUT.put_line (r_outstr);  
  138.       EXCEPTION  
  139.          WHEN NO_DATA_FOUND  
  140.          THEN  
  141.             COMMIT;  
  142.          WHEN OTHERS  
  143.          THEN  
  144.             RAISE;  
  145.       END;  
  146.    ELSE  
  147.       IF SUBSTR (p_instr, -1, 1) = ','  
  148.       THEN  
  149.          l_instr := p_instr;  
  150.       ELSE  
  151.          l_instr := p_instr || ',';  
  152.       END IF;  
  153.   
  154.       l_length := LENGTH (l_instr);  
  155.   
  156.       <<outer_loop>>  
  157.       WHILE l_start < l_length  
  158.       LOOP  
  159.          l_id_count := l_id_count + 1;  
  160.          l_idx := INSTR (l_instr, ',', l_start);  
  161.          l_substr := SUBSTR (l_instr, l_start, l_idx - l_start);  
  162.          l_start := l_idx + 1;  
  163.   
  164.          IF DBMS_PIPE.receive_message (l_pipename, 0) = 0  
  165.          THEN  
  166.             DBMS_PIPE.unpack_message (l_pipebuf);  
  167.             EXIT WHEN l_pipebuf = 'stop';  
  168.          END IF;  
  169.   
  170.          IF DBMS_PIPE.receive_message (l_pipe_getcount, 0) = 0  
  171.          THEN  
  172.             DBMS_PIPE.PURGE (l_pipe_retcount);  
  173.             DBMS_PIPE.pack_message (l_code_count || '|running');  
  174.             l_send_result := DBMS_PIPE.send_message (l_pipe_retcount);  
  175.          END IF;  
  176.   
  177.          BEGIN  
  178.             SELECT mc  
  179.               INTO l_mc_str  
  180.               FROM product  
  181.              WHERE ID = l_substr;  
  182.          EXCEPTION  
  183.             WHEN OTHERS  
  184.             THEN  
  185.                GOTO outer_loop;  
  186.          END;  
  187.   
  188.          l_sql :=  
  189.                'select code1 from (select code1 /*+ first_rows */ from '  
  190.             || l_tablename  
  191.             || ' where instr('''  
  192.             || p_p1  
  193.             || ''',  
  194.          substr(code1,1,1)) > 0 and instr('''  
  195.             || p_p2  
  196.             || ''',  
  197.          substr(code1,2,1)) > 0 and instr('''  
  198.             || p_p3  
  199.             || ''',  
  200.          substr(code1,3,1)) > 0 and instr('''  
  201.             || p_p4  
  202.             || ''',  
  203.          substr(code1,4,1)) > 0 and instr('''  
  204.             || p_p5  
  205.             || ''',  
  206.          substr(code1,5,1)) > 0 and instr('''  
  207.             || p_p6  
  208.             || ''',  
  209.          substr(code1,6,1)) > 0 and instr('''  
  210.             || p_p7  
  211.             || ''',  
  212.          substr(code1,1,1)) > 0 ) where rownum=1 ';  
  213.   
  214.          BEGIN  
  215.             EXECUTE IMMEDIATE l_sql  
  216.                          INTO l_code_str;  
  217.          EXCEPTION  
  218.             WHEN NO_DATA_FOUND  
  219.             THEN  
  220.                EXIT;  
  221.          END;  
  222.   
  223.          IF SQL%ROWCOUNT = 1  
  224.          THEN  
  225.             l_code_count := l_code_count + 1;  
  226.          END IF;  
  227.   
  228.          INSERT INTO product_code  
  229.               VALUES (l_code_str, l_wzmc_str, p_country_code);  
  230.   
  231.          EXECUTE IMMEDIATE 'delete from ' || l_tablename || ' where code1=:x'  
  232.                      USING l_code_str;  
  233.   
  234.          UPDATE product  
  235.             SET status = 1  
  236.           WHERE ID = l_substr;  
  237.       END LOOP;  
  238.   
  239.       COMMIT;  
  240.       DBMS_PIPE.PURGE (l_pipe_retcount);  
  241.       DBMS_PIPE.pack_message (TO_CHAR (l_code_count) || '|end');  
  242.       l_send_result := DBMS_PIPE.send_message (l_pipe_retcount);  
  243.       r_outstr :=  
  244.             LENGTH (l_instr)  
  245.          -  LENGTH (REPLACE (l_instr, ','''))  
  246.          || '|'  
  247.          || l_code_count;  
  248.       DBMS_OUTPUT.put_line (r_outstr);  
  249.    END IF;  
  250. END p_gen_code;  
  251.   
  252. -- 3. 终止过程  
  253. CREATE OR REPLACE PROCEDURE p_stop  
  254. IS  
  255.    l_pipename        VARCHAR2 (12) := 'mypipe';  
  256.    l_create_result   INTEGER       := DBMS_PIPE.create_pipe (l_pipename);  
  257.    l_send_result     INTEGER;  
  258. BEGIN  
  259.    DBMS_PIPE.PURGE (l_pipename);  
  260.    DBMS_PIPE.pack_message ('stop');  
  261.    l_send_result := DBMS_PIPE.send_message (l_pipename);  
  262.    DBMS_OUTPUT.put_line ('l_send_result: ' || l_send_result);  
  263. END p_stop;  
  264.   
  265. -- 4. 取得当前已经生成代码的个数  
  266. CREATE OR REPLACE FUNCTION fn_getcount (p_timeout NUMBER)  
  267.    RETURN VARCHAR2  
  268. IS  
  269.    l_pipebuf         VARCHAR2 (20);  
  270.    l_pipe_getcount   VARCHAR2 (12) := 'getcount';  
  271.    l_pipe_retcount   VARCHAR2 (12) := 'retcount';  
  272.    l_status          NUMBER;  
  273.    l_receive         NUMBER;  
  274. BEGIN  
  275.    DBMS_PIPE.PURGE (l_pipe_getcount);  
  276.    DBMS_PIPE.pack_message (l_pipe_getcount);  
  277.    l_status := DBMS_PIPE.send_message (l_pipe_getcount);  
  278.    l_receive :=  
  279.               DBMS_PIPE.receive_message (l_pipe_retcount, NVL (p_timeout, 0));  
  280.   
  281.    IF l_receive = 0  
  282.    THEN  
  283.       DBMS_PIPE.unpack_message (l_pipebuf);  
  284.       RETURN TO_CHAR (l_pipebuf);  
  285.    ELSE  
  286.       RETURN TO_CHAR ('Timed out!');  
  287.    END IF;  
  288. END fn_getcount;  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值