`
nikoloss
  • 浏览: 32915 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

oracle过程参数可能为空或多个值的查询(二)

阅读更多
    上一篇,完成了对这些参数的判空查询,比如我要CPU为i3的,屏幕为15寸的,等等。
    这次要解决的问题就比较棘手了,比如我在CPU上面同时勾选了i3,i5,这种情况要怎么查询呢。假设我这四个参数,每个参数有四种选项,那么我就有可能组合256种不同的查询。这么多种查询,我们不可能一个一个的判断然后写出来。
    不过首先,我们要解决的是一个参数怎么传递多个值,最容易让人想到的是分隔字符串。比如从前台拿到了参数“i3”和“i5”,那么我们在后台就把它以某种分隔符(比如“|”)把他们拼接成“i3|i5”然后就可以传递进去了。所以我们要解决的第一个难题就是怎么在存储过程中把这个字符串拆开?拆开了之后保存到哪里?保存了之后,怎么一个一个取出来使用?
    我们首先会想,拆开了之后放在哪的问题,如果拆开了,不知道放在哪,那么拆分这步工作就没有意义。所以我们要建一个全局类型,一维数组。
CREATE OR REPLACE TYPE TYPE_VARCHAR IS TABLE OF VARCHAR2(2000);

然后就要写这个拆分函数,返回这个数组。
我们还是写在PKG_DEMO包里面
    FUNCTION SPLIT(V_SRC IN VARCHAR2,V_CHAR IN VARCHAR2)
      RETURN TYPE_VARCHAR
      AS
      V_ARRAY TYPE_VARCHAR;
      SRC VARCHAR2(30000);
      IDX NUMBER;
      POSITION NUMBER;
      TEMP VARCHAR2(30000);
      BEGIN
    --如果参数为空,那么直接返回空。
        IF V_SRC IS NULL
          THEN RETURN NULL;
        END IF;
        IDX:=1;
        SRC:=V_SRC;
    --初始化数组
        V_ARRAY:=NEW TYPE_VARCHAR();
    --INSTR函数是返回V_CHAR在SRC中第一次出现的位置
        LOOP POSITION:=INSTR(SRC,V_CHAR);
    --如果在SRC中找不到V_CHAR了就要退出循环了
        EXIT WHEN POSITION=0;
    --SRC截取0到POSITION
          TEMP:=SUBSTR(SRC,0,POSITION-1);
    --把截取的这一部分放到数组里面
          V_ARRAY.EXTEND;
          V_ARRAY(IDX):=TEMP;
    --SRC把自己截去掉先前截取的元素和分隔符本身,在下一次循环中便排除掉第一个被截取出来的元素了。
          SRC:=SUBSTR(SRC,POSITION+LENGTH(V_CHAR));
    --测试打印出元素
          DBMS_OUTPUT.PUT_LINE('ARRAY('||IDX||')='||V_ARRAY(IDX));
          IDX:=IDX+1;
        END LOOP;
    --由于在循环里面SRC把自己截断的只剩下最后一个元素了,还没有加入到数组里面,便跳出了循环。所以我们需要在循环外面把最后一个元素补上。
        V_ARRAY.EXTEND;
        V_ARRAY(IDX):=SRC;
        DBMS_OUTPUT.PUT_LINE('ARRAY('||IDX||')='||V_ARRAY(IDX));
        RETURN V_ARRAY;
      END;

接下来,测试一下,看看能否打印出每一个元素。
DECLARE
VY TYPE_VARCHAR;
BEGIN
  VY:=PKG_DEMO.SPLIT('AAA||BBB||CCC||DDD','||');
END;

查看控制台。
ARRAY(1)=AAA
ARRAY(2)=BBB
ARRAY(3)=CCC
ARRAY(4)=DDD
看来达到了我们想要的效果,我们已经实现了拆分函数split了。
接下来我们要考虑怎么使用这个数组去做查询了。
--通常一个参数如果有多个值的查询是这样的
SELECT * FROM TEST WHERE CPU_ IN ('i3','i5');
----------------------------------------------
--那么这个('i3','i5')其实就是我们的数组
--我们不妨大胆的利用table()函数,就来使用我们的数组
DECLARE
RC SYS_REFCURSOR;
REC TEST%ROWTYPE;
VY TYPE_VARCHAR;
BEGIN
  VY:=PKG_DEMO.SPLIT('i3||i5','||');
  --我们之前定义的一维数组其实就是一个table of varchar,
  --他本质上就是一个table,我们可以强转它为table,
  --这样我们就利用到了它里面的所有元素。
  OPEN RC FOR 'SELECT * FROM TEST WHERE CPU_ IN (SELECT * FROM TABLE(:X))' USING VY;
  LOOP FETCH RC INTO REC;
  EXIT WHEN RC%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('CPU='||REC.CPU_||' SCREEN='||REC.SCREEN||' PRICE='||REC.PRICE||' ERA='||REC.ERA);
  END LOOP;
END;

结果:
CPU=i3 SCREEN=14 PRICE=4399 ERA=2012
CPU=i3 SCREEN=13 PRICE=3999 ERA=2011
CPU=i5 SCREEN=13 PRICE=5499 ERA=2012
CPU=i5 SCREEN=15 PRICE=5200 ERA=2011
很显然,我们已经达到了目的了。大功告成还差最后一步。我们需要把它整合到上一篇的判空机制里面去,也就是如果我们什么都不传的话,意味着无条件完全查询。
于是仿照上一篇中的那个TEST_SELECT我们新做了一个过程TEST_SELECT2
  PROCEDURE TEST_SELECT2(V_CPU IN VARCHAR2,
                   V_SCREEN IN VARCHAR2,
                   V_PRICE IN VARCHAR2,
                   V_ERA IN VARCHAR2,
                   CR OUT P_CURSOR)
      AS
      CPU_ARRAY TYPE_VARCHAR;
      SCREEN_ARRAY TYPE_VARCHAR;
      PRICE_ARRAY TYPE_VARCHAR;
      ERA_ARRAY TYPE_VARCHAR;
      BEGIN
        CPU_ARRAY:=SPLIT(V_CPU,'||');
        SCREEN_ARRAY:=SPLIT(V_SCREEN,'||');
        PRICE_ARRAY:=SPLIT(V_PRICE,'||');
        ERA_ARRAY:=SPLIT(V_ERA,'||');
        OPEN CR FOR 'SELECT * FROM TEST WHERE 
                    CPU_ IN COALESCE((SELECT * FROM TABLE(:X)),CPU_) AND 
                    SCREEN IN COALESCE((SELECT * FROM TABLE(:X)),SCREEN) AND 
                    PRICE IN COALESCE((SELECT * FROM TABLE(:X)),PRICE) AND
                    ERA IN COALESCE((SELECT * FROM TABLE(:X)),ERA) '
                    USING 
                    CPU_ARRAY,
                    SCREEN_ARRAY,
                    PRICE_ARRAY,
                    ERA_ARRAY;
      END;
  --于是我们来测试一下
DECLARE
RC SYS_REFCURSOR;
REC TEST%ROWTYPE;
BEGIN
  PKG_DEMO.TEST_SELECT2('i3||i5','','','',RC);
  LOOP FETCH RC INTO REC;
  EXIT WHEN RC%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('CPU='||REC.CPU_||' SCREEN='||REC.SCREEN||' PRICE='||REC.PRICE||' ERA='||REC.ERA);
  END LOOP;
END;
--结果报错了!“一行记录返回了多行数据”,同样尝试了decode,nvl,nvl2函数之后
--还是会报错“类型不匹配”或者“返回了多行数据”。
--其实这都是因为类型不匹配造成的,oracle的函数不支持我们定义的type_varchar这种
--类型,所以无法在我们定义的类型上使用它。

肯定很痛苦,但是这个过程也是探究的乐趣所在,这个过程越痛苦,一旦我们能成功突破
难点的时候,兴奋、开心、自豪就会被放大。如果我们不能在自定义类型上使用oracle函
数,那么我们至少可以在能被他支持的类型上使用。什么意思呢?想想我们条件判断通常
是where cpu_ = coalesce(???,???)我们可以倒过来考虑where coalesce(cpu_,???)=???
这样不就能成功的使用判空函数了吗?
有了这个思路,我们实现它的道路就清晰了,先来测试一条语句看看能不能执行。
SELECT * FROM TEST WHERE -1 IN -1;

轻而易举的看到了结果,证明了我们的想法是可行的,于是我们想到split函数需要改进一
下,在参数为空的时候,我们不能直接返回空了,返回一个只有一个元素的数组,这个元素
是-1,或者任意的,逻辑上达不到那个值的值,你喜欢的都可以。
--改进一下function split
    FUNCTION SPLIT(V_SRC IN VARCHAR2,V_CHAR IN VARCHAR2)
      RETURN TYPE_VARCHAR
      AS
      V_ARRAY TYPE_VARCHAR;
      SRC VARCHAR2(30000);
      IDX NUMBER;
      POSITION NUMBER;
      TEMP VARCHAR2(30000);
      BEGIN
        V_ARRAY:=NEW TYPE_VARCHAR();
    --如果为空,返回一个元素为-1的数组
        IF V_SRC IS NULL THEN
          V_ARRAY.EXTEND;
          V_ARRAY(1):=-1;
          RETURN V_ARRAY;
        END IF;
        IDX:=1;
        SRC:=V_SRC;
        LOOP POSITION:=INSTR(SRC,V_CHAR);
        EXIT WHEN POSITION=0;
          TEMP:=SUBSTR(SRC,0,POSITION-1);
          V_ARRAY.EXTEND;
          V_ARRAY(IDX):=TEMP;
          SRC:=SUBSTR(SRC,POSITION+LENGTH(V_CHAR));
          DBMS_OUTPUT.PUT_LINE('ARRAY('||IDX||')='||V_ARRAY(IDX));
          IDX:=IDX+1;
        END LOOP;
        V_ARRAY.EXTEND;
        V_ARRAY(IDX):=SRC;
        DBMS_OUTPUT.PUT_LINE('ARRAY('||IDX||')='||V_ARRAY(IDX));
        RETURN V_ARRAY;
      END;
--然后重写TEST_SELECT2过程
  PROCEDURE TEST_SELECT2(V_CPU IN VARCHAR2,
                   V_SCREEN IN VARCHAR2,
                   V_PRICE IN VARCHAR2,
                   V_ERA IN VARCHAR2,
                   CR OUT P_CURSOR)
      AS
      CPU_ARRAY TYPE_VARCHAR;
      SCREEN_ARRAY TYPE_VARCHAR;
      PRICE_ARRAY TYPE_VARCHAR;
      ERA_ARRAY TYPE_VARCHAR;
      BEGIN
        CPU_ARRAY:=SPLIT(V_CPU,'||');
        SCREEN_ARRAY:=SPLIT(V_SCREEN,'||');
        PRICE_ARRAY:=SPLIT(V_PRICE,'||');
        ERA_ARRAY:=SPLIT(V_ERA,'||');
    --这样在输入参数为空的时候条件变成了 -1 in ('-1')
        OPEN CR FOR 'SELECT * FROM TEST WHERE 
                    NVL2(:X,CPU_,''-1'') IN (SELECT * FROM TABLE(:X)) AND 
                    NVL2(:X,SCREEN,''-1'') IN (SELECT * FROM TABLE(:X)) AND 
                    NVL2(:X,PRICE,''-1'') IN (SELECT * FROM TABLE(:X)) AND
                    NVL2(:X,ERA,''-1'') IN (SELECT * FROM TABLE(:X)) '
                    USING 
                    V_CPU,CPU_ARRAY,
                    V_SCREEN,SCREEN_ARRAY,
                    V_PRICE,PRICE_ARRAY,
                    V_ERA,ERA_ARRAY;
      END;
--接下来,我们再次进行测试。。。
DECLARE
RC SYS_REFCURSOR;
REC TEST%ROWTYPE;
BEGIN
  PKG_DEMO.TEST_SELECT2('i3||i5','13||15','','',RC);
  LOOP FETCH RC INTO REC;
  EXIT WHEN RC%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('CPU='||REC.CPU_||' SCREEN='||REC.SCREEN||' PRICE='||REC.PRICE||' ERA='||REC.ERA);
  END LOOP;
END;

结果如下
CPU=i3  SCREEN=13  PRICE=3999  ERA=2011
CPU=i5  SCREEN=15  PRICE=5200  ERA=2011
CPU=i5  SCREEN=13  PRICE=5499  ERA=2012
至此,大功告成。

总结:虽然oracle的plsql编程不像java,有那么多的api可供调用,但是也正因为如此,plsql编程才显得比java更加灵活多变也更为透明,好处和坏处都很明显,逻辑、
算法、等都是要考虑仔细的,你不小心就会写出臃肿冗余的代码,但是你也更有可能
写出高性能的存储过程!

15
18
分享到:
评论

相关推荐

    Oracle9i的init.ora参数中文说明

    否则就会启动一个值为双重货币符号的新会话。 值范围: 任何有效的格式名。。 默认值: 双重货币符号 nls_iso_currency: 说明: 为 C 数字格式元素指定用作国际货币符号的字符串。该参数的默认值由 NLS_TERRITORY ...

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    ORACLE数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是目前最流行的客户/服务器(CLIENT/SERVER)或B/S体系结构的数据库之一。  拉里•埃里森  就业前景 从就业与择业的...

    oracle实验报告

    2、 定义一个为修改职工表(emp)中某职工工资的存储过程子程序,职工名作为形参,若该职工名在职工表中查找不到,就在屏幕上提示“查无此人”然后结束子程序的执行;否则若工种为MANAGER的,则工资加$1000;工种为...

    最全的oracle常用命令大全.txt

     很多时候,一般的ORACLE用户不知道如何有效地利用它。  dictionary 全部数据字典表的名称和解释,它有一个同义词dict dict_column 全部数据字典表里字段名称和解释 如果我们想查询跟索引有关的数据字典时,...

    oracle数据库经典题目

    在共享服务器模式中,一个服务器进程可以为多个用户进程服务器。 4.说明数据库内存结构中SGA和PGA的组成,以及这两个内存区存放信息的区别。 答案: SGA区由数据缓冲区、共享池、重做日志缓冲区、大型池、JAVA池...

    ORACLE,mysql,sqlserver,sybase数据库装文本软件

    此值为0则it参数不生效。 1.第一行是逗号分隔的包括列类型的列信息,例如NAME VARCHAR2(30),注意,区分大小写 2.第一行是列分隔符分隔的列名(不包括列字段信息),注意,列名区分大小写 -it insert type 1.导入的...

    Oracle事例

    多个字段时,在最后加上 Foreign Key (字段名) REFERENCE 表名(字段) 连带删除选项 (on delete cascade 当指定时,如果父表中的记录被删除,则依赖于父表的记录也被删除 REFERENCE 表名() on delete cascade; 7...

    orcale常用命令

    要想启动或关闭ORACLE系统必须首先切换到ORACLE用户,如下 su - oracle a、启动ORACLE系统 oracle>sqlplus SQL>connect internal SQL>startup SQL>quit b、关闭ORACLE系统 oracle>sqlplus SQL>connect internal ...

    oracle从入门到精通

    4、定义空(NULL)值 ...................................................................................................................9 5、别名 ......................................................

    mysql数据库my.cnf配置文件

    # 0:如果innodb_flush_log_at_trx_commit的值为0,log buffer每秒就会被刷写日志文件到磁盘,提交事务的时候不做任何操作(执行是由mysql的master thread线程来执行的。 # 主线程中每秒会将重做日志缓冲写入磁盘的...

    Oracle从入门到精通

    7. tablespace 表空间:一个或多个数据文件的逻辑组成。 ..... 8. redo log file 重做日志文件............................. 9. control file 控制文件 ................................. 10. parameter file 初始...

    C#基类库(苏飞版)

    取汉字拼音的首字母,只要你输入一个汉字,或者是多个汉字就会取出相应的道字母,主要是方便查询使用的 复制代码 16.配置文件操作类 ConfigHelper 1.根据Key取Value值 2.根据Key修改Value 3.添加新的Key ,Value...

    jpivot学习总结.doc

    uniqueMembers 该属性用于优化产生的 SQL ,如果你知道这个级别和其父级别交叉后的值或者是维度表中给定的级别所有的值是唯一的,那么就可以设置该值为 true ,否则为 false 。 levelType 该 Level 的类型,默认...

    mysql数据库的基本操作语法

    视图就是一个表或多个表的查询结果,它是一张虚拟的表,因为它并不能存储数据。 视图的作用、优点: 限制对数据的访问 让复杂查询变得简单 提供数据的独立性 可以完成对相同数据的不同显示 创建、修改视图 create or...

    java 面试题 总结

    如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 15、error和exception有什么区别? error 表示恢复不是...

    SQL语法大全

    Source参数可以是一个Command对象名称、一段SQL命令、一个指定的数据表名称或是一个Stored Procedure。假如省略这个参数,系统则采用Recordset对象的Source属性。 ActiveConnection Recordset对象可以通过...

    C#编程经验技巧宝典

    79 <br>0115 如何判断是否为数字 79 <br>0116 如何在字符串中查找指定字符 79 <br>0117 如何在字符串中用一子串替换另一子串 80 <br>0118 将新字符串添加到已有字符串中 80 <br>0119 如何在...

    PL/SQL 基础.doc

    注意:1)有多个BOOLEAN表达式时候 AND(与),OR(或),NOT(非) 2)条件为FALSE与TRUE 相同; 2. 循环语句 1) Loop 循环 Loop ... (循环体) IF boolean_expr(条件) THEN /* 加条件语句,当满足条件时候退出循环*/ ...

Global site tag (gtag.js) - Google Analytics