sql的一些 oracle2

1 批量插入

引用
在oracle中使用DML语言的insert语句来向表格中插入数据,先介绍每次只能插入一条数据的语法
INSERT  INTO   表名(列名列表)   VALUES(值列表);
注意:
当对表中所有的列进行赋值,那么列名列表可以省略,小括号也随之省略
必须对表中的非空字段进行赋值
具有默认值的字段可以不提供值,此时列名列表中的相应的列名也要省略
举例:有如下表格定义
create table book(bookid char(10) not null , name varchar2(60),price number(5,3))
使用下面的语句来插入数据
INSERT INTO BOOK(bookid,name,price)   VALUES('100123','oracle sql',54.70);
INSERT INTO BOOK                         VALUES('100123','oracle sql',54.70);
INSERT INTO BOOK(bookid)           VALUES('100123');
由于bookid是非空,所以,对于book来说,至少要对bookid进行赋值,虽然这样的数据不完整
如果想往一个表格中插入多条数据,那么带有values子句的insert就不行了,这时候必须使用insert语句和select语句进行配合来实现同时插入多条数据:
例如:现在有一个空表a和一个有数据的表格b,他们的结构是一样, 把b表中的所有数据插入到a表中的语句是:
INSERT INTO A (列1,列2,列3) 
            SELECT 列1,列2,列3
            FROM B ;
--查询语句中可以使用任意复杂的条件或者子查询
如果数据的来源不是现存表的数据,也想多条插入那么使用如下的方法:
INSERT INTO  tablename(列1,列2,列3,)
SELECT  值1,值2,值3   FROM  DUAL
UNION
SELECT  值1,值2,值3   FROM  DUAL
UNION
SELECT  值1,值2,值3   FROM  DUAL
 

如果上面的值有字符和日期型数据,那么使用单引号即可,每一个select语句得到一条数据,然后使用集合操作符union把多条数据合并到一个结果集中,来实现一次插入多条数据的功能


 技术水平总能在扯皮和吹毛求疵中得到提高。如果从来不“求疵”,可能就不会知道if(str != "")不如if(str != string.Empty)高效、批量插入和删除的sql语句是要那样写才执行最快、接口和抽象类的区别不仅是语言层面、原来权限管理是要这样设计的、某个类那样设计职责才更单一更易于扩展……

  本来前两篇文章是学习cnblogs编辑控件用的,看到跟贴的朋友询问批量插入和批量删除的问题,决定整理成文和大家分享。

  我们这里讨论的只是普通sql语句如何写更高效,不考虑特殊的用文件中转等导入方式,毕竟在代码中调用sql语句或存储过程才更方便。

  批量删除很简单,大家可能都用过:

  DELETE FROM TestTable WHEREID IN (1, 3, 54, 68) --sql2005下运行通过

  当用户在界面上不连续的选择多项进行删除时,该语句比循环调用多次删除或多条delete语句中间加分号一次调用等方法都高效的多。

  本文重点讲述的是批量插入的写法:

  sql写法:

  INSERT INTO TestTable SELECT1, 'abc'UNION SELECT 2, 'bcd' UNION SELECT3, 'cde'  --TestTable表没有主键,ID不是主键

  oracle写法:

  INSERT INTO TestTable SELECT 1, 'abc' From daul UNION SELECT 2, 'bcd' From daul  --TestTable表没有主键,ID不是主键

  曾经测试过,这种写法插入1000条数据比循环调用1000次insert或1000条insert语句简单叠加一次调用要高效得多,大概快20多倍(调试状态不是太准)。其实很简单,就用了个union(union all 也可以),但当时得出测试结果时还是很惊喜的。

  要得出这个结果需要两个条件:

  1、表不能有主键或者主键是数据库默认的(sql用自动递增列,oracle用序列)

  2、组合sql语句时只能直接用字符串连接,不能用参数化sql语句的写法(就是在组合的sql中用@parm做占位符,再给Command对象添加Parameter)

  以上两条任意一条不满足,效率的提高都不明显。

  另外,sql语句的最大长度有限制,组合几千条数据写成一条insert语句,可能会超过上限,所以如果有5000条数据,可以一次insert 1000条,分5次写完(不一定一次1000条最合适,有兴趣的朋友可以研究)。

  声明:看了几位朋友的评论后发现自己没有叙述清楚

   上面的两个条件并不是说这样做就好,而是说当应用场景满足这两个条件时才适合用union的写法

  当时用这种写法时,是为了解决一个WinForm程序批量导入手机号码的问题,程序给自己人用,excel文件也是可靠的,不需要考虑sql攻击。还有一些场景是大量数据来源于通过Gprs网络连接的专用终端,总之数据来源是可靠的,还有就是数据量很大但不可能巨大。


2 NO_DATA_FOUND
引用
我们知道NO_DATA_FOUND异常情况仅仅被SELECT..INTO语句触发,当该WHERE子句没有找到任何行的时候就会触发它。
解决的方法通常有两种:
一种是我们将查询语句写成游标,这样WHERE子句没有找到任何行的时候, SQL%NOTFOUND就会设置成TRUE,而不会报NO_DATA_FOUND;
还有一种是我们在查询前先SELECT COUNT(*) INTO 一下,看看WHERE子句是否能找到相应的行,如果有再SELECT..INTO。
第二种方法虽然效率上有消耗,但是简单易写,因此我们在写存储过程时经常使用。

但是,大家试着测试下面的存储过程:

create or replace procedure A_TEST1 is
   n_num number;
begin
  select count(*)
    into n_num
    from mascty
   where mascty.mctcty = '123'
   group by mascty.mctcty;
end A_TEST1;

结果令人吃惊啊,这种偏偏是为了避免产生 NO_DATA_FOUND 异常的SELECT COUNT(*) INTO语句也会产生NO_DATA_FOUND 异常。

原因在哪呢?
从代码上看SELECT COUNT(*) INTO语句最后跟了group by。编程人员意思可能是将查询的结果,根据group by的条件分组,然后,再COUNT看一共有多少个组。
其实这种理解是错误的。这个查询真正的顺序是先将查询的结果COUNT,然后再group by。如果查询的结果COUNT为0,即没有符合条件的结果,再group by,那么返回的就不是0,而是空(NULL)。
所以将一个空值放到一个整型的变量中就会产生NO_DATA_FOUND的异常。

如何解决呢?
按照编程者真正的目的将存储过程改为:

create or replace procedure A_TEST1 is
   n_num number;
begin
  select count(*)
    into n_num
    from (select *
            from mascty
           where mascty.mctcty = '123'
           group by mascty.mctcty);
end A_TEST1;

这样COUNT出来的值就是0,也是想要的结果,不会产生NO_DATA_FOUND 异常。
总结:
1.一定要注意在使用group by时,SELECT COUNT(*) INTO语句也会产生NO_DATA_FOUND 异常。
2.一定要理解好group by执行的顺序。

测试代码如下:
create table mascty(mctcty varchar2(10));

declare
n_num number;
begin
select count(*)
into n_num
from mascty
where mascty.mctcty = '123'
group by mascty.mctcty;
exception
when others then
dbms_output.put_line('sqlerrm is '||sqlerrm);
end;

输出结果是:sqlerrm is ORA-01403: 未找到数据


3 TRUNCATE  
引用
TRUNCATE   TABLE   在功能上与不带   WHERE   子句的   DELETE   语句相同:二者均删除表中的全部行。但   TRUNCATE   TABLE   比   DELETE   速度快,且使用的系统和事务日志资源少。    
   
  DELETE   语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE   TABLE   通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。  
   
  TRUNCATE   TABLE   删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用   DELETE。如果要删除表定义及其数据,请使用   DROP   TABLE   语句。  
   
  对于由   FOREIGN   KEY   约束引用的表,不能使用   TRUNCATE   TABLE,而应使用不带   WHERE   子句的   DELETE   语句。由于   TRUNCATE   TABLE   不记录在日志中,所以它不能激活触发器。    
   
  TRUNCATE   TABLE   不能用于参与了索引视图的表。

truncate,delete,drop的异同点  
   
  说明:本文摘自oracle技术用户讨论组  
   
  truncate,delete,drop的异同点      
  注意:这里说的delete是指不带where子句的delete语句    
       
  相同点:truncate和不带where子句的delete,   以及drop都会删除表内的数据      
  不同点:      
  1.   truncate和   delete只删除数据不删除表的结构(定义)      
            drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger),索引(index);   依赖于该表的存储过程/函数将保留,但是变为invalid状态.      
  2.delete语句是dml,这个操作会放到rollback   segement中,事务提交之后才生效;如果有相应的trigger,执行的时候将被触发.      
          truncate,drop是ddl,   操作立即生效,原数据不放到rollback   segment中,不能回滚.   操作不触发trigger.      
  3.delete语句不影响表所占用的extent,   高水线(high   watermark)保持原位置不动      
        显然drop语句将表所占用的空间全部释放      
        truncate   语句缺省情况下将空间释放到   minextents个   extent,除非使用reuse   storage;       truncate会将高水线复位(回到最开始).      
  4.速度,一般来说:   drop>   truncate   >   delete      
  5.安全性:小心使用drop   和truncate,尤其没有备份的时候.否则哭都来不及      
  使用上,想删除部分数据行用delete,注意带上where子句.   回滚段要足够大.      
  想删除表,当然用drop      
  想保留表而将所有数据删除.   如果和事务无关,用truncate即可.   如果和事务有关,或者想触发trigger,还是用delete.      
  如果是整理表内部的碎片,可以用truncate跟上reuse   stroage,再重新导入/插入数据

你可能感兴趣的:(数据结构,oracle,sql,编程,WinForm)