【sql查询与优化】4.删除插入与更新

注:以下所有sql案例均取自"oracle查询优化改写技巧与案例"丛书。

案例中可能会用到的表:

EMP表的详细:

查询所有信息,

SQL> select * from emp;

EMPNO ENAME                JOB                       MGR HIREDATE       SAL        COMM       DEPTNO

---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ----------

1110 张三                 主管                     3322 12-3月 -14      5200                    20

1111 李四                 销售                     3321 03-11月-15      3400        500         30

1112 王五                 销售                     3321 25-4月 -12      4400        800         30

1113 赵二                 后勤                     3320 30-5月 -11      3450                    40

1114 李磊磊               会计                     3319 22-12月-15      2500                    50

1115 张少丽               销售                     3321 11-3月 -16      3400       1400         30

1116 林建国               主管                     3322 22-1月 -16      5700                    20

1117 马富邦               后勤                     3320 22-7月 -13      2800                    40

1118 沈倩                 会计                     3319 06-5月 -10      2100                    50

已选择9行。

DEPT表的详细:

查询所有信息

SQL> select * from dept t;

DEPTNO    DNAME

--------         --------

3322       管理部门

3321       销售部门

3320       后勤部门

3319       金融部门

1.插入新纪录

我们先建立测试表,各列都有默认值

create table test(
 c1 varchar2(10) default '默认1',
 c2 varchar2(10) default '默认2',
 c3 varchar2(10) default '默认3',
 c4 date default sysdate
);

新增数据如下:

insert into test(c1,c2,c3) values(default,null,'手输值');

然后查询test表所有数据:

SQL> select * from test;

C1           C2          C3           C4

------------ ----------- ----------- --------------

默认1                    手输值      07-4月 -16

注意:

(1)如果insert语句中没有含默认值的列,则会添加默认值,如c4列。

(2)如果包含有默认值的列,需要用default关键字,才会添加默认列,如c1列。

(3)如果已显示设定了NULL或其他值,则不会在生成默认值,如c2、c3列。

建立表时,有时明明设定了默认值,可生成的数据还是NULL,原因在于我们在代码中不知不觉地加入了NULL。

2.阻止对某几列插入

我们建立的表中c4列默认为sysdate,这种列一般是为了记录数据生成的时间,不允许手动录入。那么系统控制不到位,或者管理不到位,经常会有手动录入的情况发生,怎么办?

我们可以建立一个不包含c4的列的view,新增数据时通过这个view就可以。

create or replace view v_test as select c1,c2,c3 from test;

视图已创建。

insert into v_test(c1,c2,c3) values('手输c1',NULL,'勿改c4');

已创建一行。

查看数据:

SQL> select * from test;

C1                   C2                   C3                   C4

-------------------- -------------------- -------------------- --------------

默认1                                     手输值               07-4月 -16

手输c1                                    勿改c4               07-4月 -16

注意:通过view新增数据,不能再使用关键字default。

3.复制表的定义及数据

我们可以用以下语句复制表test:

create table test2 as select * from test;

也可以先复制表的定义,再新增数据:

create table test2 as select * from test where 1=2;

注意:复制的表不包含默认值等约束信息,使用这种方式复制表后,需要重建默认值以及索引和约束等信息。

看看我们复制的test2:

SQL> desc test2

名称         否为空? 类型

------------ -------- -------------

C1                     VARCHAR2(10)

C2                     VARCHAR2(10)

C3                     VARCHAR2(10)

C4                     DATE

复制表之后就可以新增数据了:

SQL> insert into test2 select * from test;

已创建2行。

SQL> select * from test2;

C1                   C2                   C3                   C4

-------------------- -------------------- -------------------- --------------

默认1                                     手输值               07-4月 -16

手输c1                                    勿改c4               07-4月 -16

4.用with check option限制数据录入

当约束条件比较简单时,可以直接加在表中,如工资必须大于0:

SQL> alter table emp add constraints ch_sal check(sal>0);

表已更改。

但是有一些复杂或者特殊的约束条件是不能这样放在表里的,如雇用日期大于当前日期:

SQL> alter table emp add constraints ch_hiredate check(hiredate>=sysdate);
alter table emp add constraints ch_hiredate check(hiredate>=sysdate)
                                                            *
第 1 行出现错误:
ORA-02436: 日期或系统变量在 CHECK 约束条件中指定错误

这时我们可以使用加了with check option 关键字的view来达到目的。

下面的实例中,我们限制了不符合内联视图条件的数据(sysdate+1):

SQL> insert into
         (select empno,ename,hiredate
           from emp
          where hiredate <= sysdate with check option)
          values
          (9999,'test',sysdate+1);
      from emp
           *
第 3 行出现错误:
ORA-01402: 视图 WITH CHECK OPTIDN where 子句违规

发现报错了,因为里面有关键字“with check option”,所以insert的数据不符合其中的条件(hiredate<=sysdate)时,不就允许使用insert。

我们通过报错信息可以看到,语句(select empno,ename,hiredate from emp where hiredate <= sysdate with check option)被当做一个视图处理。

当我们插入的数据符合条件(hiredate<=sysdate)时,就允许使用insert。

SQL> insert into
         (select empno,ename,hiredate
           from emp
          where hiredate <= sysdate with check option)
          values
          (9999,'test',sysdate-1);

已创建 1 行。

查看一下:

SQL> select * from emp e where e.hiredate like sysdate-1;

EMPNO ENAME    JOB        MGR        HIREDATE       SAL         COMM     DEPTNO

---------- -------- ---------- ---------- -------------- ---------- -------   -------

9999 test                           06-4月 -16

5.多表插入语句

多表插入语句分为以下四种:

(1)无条件的insert

(2)有条件insert all

(3)转置insert

(4)有条件insert first

首先建立两个测试用表:

create table emp1 as select empno,ename,job from emp where 1=2;
create table emp2 as select empno,ename,deptno from emp where 1=2;

无条件insert:

SQL> insert all
         into emp1(empno,ename,job) values(empno,ename,job)
         into emp2(empno,ename,deptno) values(empno,ename,deptno)
     select empno,ename,job,deptno from emp where deptno in (20,30);

已创建10行。

查看插入的数据:

SQL> select * from emp1;

EMPNO ENAME                JOB

---------- -------------------- ------------------

1110 张三                 主管

1111 李四                 销售

1112 王五                 销售

1115 张少丽               销售

1116 林建国               主管

SQL> select * from emp2;

EMPNO ENAME                    DEPTNO

---------- -------------------- ----------

1110 张三                         20

1111 李四                         30

1112 王五                         30

1115 张少丽                       30

1116 林建国                       20

因为没有加条件,所以会同时向两个表中插入数据,且两个表中插入的数据条数一样。

有条件insert all:

SQL> delete emp1;
delete emp2;
insert all
    when job in('销售','会计') then
    into emp1(empno,ename,job) values(empno,ename,job)
    when deptno in ('20','30') then
    into emp2(empno,ename,deptno) values(empno,ename,deptno)
select empno,ename,job,deptno from emp;

已创建10行。

我们查看两表的数据:

SQL> select * from emp1;

EMPNO ENAME                JOB

---------- -------------------- ------------------

1111 李四                 销售

1112 王五                 销售

1114 李磊磊               会计

1115 张少丽               销售

1118 沈倩                 会计

SQL> select * from emp2;

EMPNO ENAME                    DEPTNO

---------- -------------------- ----------

1110 张三                         20

1111 李四                         30

1112 王五                         30

1115 张少丽                       30

1116 林建国                       20

当增加条件后,就会按照条件插入。如“李四”、“王五”、“张少丽”数据在两表中都有。

insert first就不一样:

SQL> delete emp1;
delete emp2;
/*有条件 insert first*/
insert first
    when job in('销售','会计') then
    into emp1(empno,ename,job) values(empno,ename,job)
    when deptno in ('20','30') then
    into emp2(empno,ename,deptno) values(empno,ename,deptno)
select empno,ename,job,deptno from emp;

已创建10行。

我们查看两表的数据:

SQL> select * from emp1;

EMPNO ENAME                JOB

---------- -------------------- ------------------

1111 李四                 销售

1112 王五                 销售

1114 李磊磊               会计

1115 张少丽               销售

1118 沈倩                 会计

SQL> select * from emp2;

EMPNO ENAME                    DEPTNO

---------- -------------------- ----------

1110 张三                         20

1116 林建国                       20

insert first语句中,当地一个表符合条件后,第二个表将不再插入对应的行,表emp2中不再有与表emp1相同的数据“李四”、“王五”、“张少丽”,这就是insert first与insert all的不同之处。

转置insert与其说是一个分类,不如算作“insert all”的一个用法。

SQL> create table t2 (d varchar(10),des varchar(50));

表已创建。

SQL> create table t1 as
          select '熊样,精神不佳' as d1,
                 '猫样,温驯听话' as d2,
                 '狗样,神气活现' as d3,
              '鸟样,向往明天' as d4,
              '花样,愿你快乐像花儿一样' as d5
           from dual;

表已创建。

看一下t1现在的数据:

SQL> select * from t1;

D1        D2              D3                D4                D5

--------------  --------------- ----------------  ----------------- -------------------------------

熊样,精神不佳  猫样,温驯听话  狗样,神气活现    鸟样,向往明天    花样,愿你快乐像花儿一样

/*转置insert*/

SQL> insert all
     into t2(d,des) values('周一',d1)
     into t2(d,des) values('周二',d2)
     into t2(d,des) values('周三',d3)
     into t2(d,des) values('周四',d4)
     into t2(d,des) values('周五',d5)
     select d1,d2,d3,d4,d5 from t1;

已创建5行。

SQL> select * from t2;

D    DES

------- -----------------------

周一    熊样,精神不佳

周二    猫样,温驯听话

周三    狗样,神气活现

周四    鸟样,向往明天

周五    花样,愿你快乐像花儿一样

可以看到,转置insert的实质就是把不同列的数据插入到同一表的不同行中。

6.用其他表中的值更新

我们对表emp新增字段dname,然后把dept.dname更新至emp中:

SQL> alter table emp add dname varchar2(50) default 'noname';

表已更改。

我们这里只更新(20:管理部门,30:销售部门)的数据。其他未更新的部门(如40:后勤部门)名称应该保持为‘noname‘不变。

出学oracle的人常把语句直接写为:

SQL> update emp p
         set p.dname =
         (select dname
          from dept d where p.mgr=d.deptno
          and d.dname in ('管理部门','销售部门'));

已更新10行。

SQL> select empno,ename,deptno,dname from emp;

EMPNO ENAME                JOB                       MGR HIREDATE       SAL        COMM       DEPTNO      DNAME

---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ----------  ----------

1110 张三                 主管                     3322 12-3月 -14      5200                    20       管理部门

1111 李四                 销售                     3321 03-11月-15      3400        500         30       销售部门

1112 王五                 销售                     3321 25-4月 -12      4400        800         30       销售部门

1113 赵二                 后勤                     3320 30-5月 -11      3450                    40

1114 李磊磊               会计                     3319 22-12月-15      2500                    50

1115 张少丽               销售                     3321 11-3月 -16      3400       1400         30       销售部门

1116 林建国               主管                     3322 22-1月 -16      5700                    20       管理部门

1117 马富邦               后勤                     3320 22-7月 -13      2800                    40

1118 沈倩                 会计                     3319 06-5月 -10      2100                    50

已选择9行。

可以看到,这个语句是对 全表做更新,而不是需求所说的部门(20:管理部门,30:销售部门),而且因为部门(40:后勤部门)等没有匹配到数据,danme均被更新为NULL值。

可以想象,在生产环境中,大量的数据被清空或改错是多么严重的行为!原因在于该语句中少了必要的过滤条件。

正确的思路是要加上限定条件:

先回滚一下

SQL> roll back;

回退已完成。

然后加限定条件

SQL> update emp p
         set p.dname =
         (select dname
          from dept d where p.mgr=d.deptno
          and d.dname in ('管理部门','销售部门'))
     where exists
         (select d.dname from dept d
            where d.deptno = p.mgr
            and d.dname in ('管理部门','销售部门'));

EMPNO ENAME                JOB                       MGR HIREDATE       SAL        COMM       DEPTNO      DNAME

---------- -------------------- ------------------ ---------- -------------- ---------- ---------- ----------  ----------

1110 张三                 主管                     3322 12-3月 -14      5200                    20       管理部门

1111 李四                 销售                     3321 03-11月-15      3400        500         30       销售部门

1112 王五                 销售                     3321 25-4月 -12      4400        800         30       销售部门

1113 赵二                 后勤                     3320 30-5月 -11      3450                    40       noname

1114 李磊磊               会计                     3319 22-12月-15      2500                    50       noname

1115 张少丽               销售                     3321 11-3月 -16      3400       1400         30       销售部门

1116 林建国               主管                     3322 22-1月 -16      5700                    20       管理部门

1117 马富邦               后勤                     3320 22-7月 -13      2800                    40       noname

1118 沈倩                 会计                     3319 06-5月 -10      2100                    50       noname

已选择9行。

除了20和30两个部门之外,其它部门的dname均没有被更新, 还是默认值“noname”。

7.合并记录

我们通过合并的实例来介绍一下merge into的使用。

首先建立测试表:

SQL> create table bonuses (employee_id number,bonus number default 100);

表已创建。

insert into bonuses
    (employee_id)
    (select e.employee_id
        from hr.employees e,oe.orders o
     where e.employee_id = o.sales_rep_id
     group by e.employee_id);
select * from bonuses order by employee_id;
commit;

语句及解释如下:

merge into bonuses d
using (select employee_id,salary,department_id
       from hr.employess
       where department_id=80) s
on (d.employee_id=s.employss_id)
/*匹配条件d.employee_id=s.employee_id*/
when matched then/*当d表中存在与S对应数据时进行更新或删除*/
  update
     set d.bonus = d.bonus + s.salary * 0.01
  /*where只能出现一次,如果在这里加了where,delete后面的where就无效*/
  delete
     where (s.salary > 8000) /*删除时,只更新s.salary>8000时的数据*/
when not matched then/*当表中不存在与s对应的数据时进行新增*/
     insert
        (d.employee_id,d.bonus)
     values
        (s.employee_id,s.salary * 0.01)
     where (s.salary<=8000)/*新增时,只更新s.salary<=8000时的数据,注意这里与以前不同,是d表中不存在对应数据时才新增*/

这里有以下几个要点:

(1)语句是merge into bonuses,所以在这个语句里只能更改bonuses的数据,不能更改using后面那些表的数据

(2)更新/删除/插入这三个操作是同时进行的,不分先后。

(3)在merge into语句里不能更新join列

(4)注意上面的注释:当有delete语句时,update后面不能有where过滤条件。这时update的范围是:匹配到的数据减去删除的数据。在本例中就是范围(d.employee_id=s.employss_id)减去范围(s.salary > 8000)。

8.删除违反参照完整性的记录

删除主表的数据,但是有外键的数据在另一个表中依然存在,这个时候会因为数据违反完整性而报错,这个时候删除主表外键躲在的子表中的数据之后,再去删除主表的数据。

9.删除重复的记录

因为是手动录入程序,所以经常会产生重复的数据,这是就需要删除多余的数据,示例如下:

SQL> create table dupes (id integer,name varchar(10));

表已创建。

然后

insert into dupes values (1,'NBA');
insert into dupes values (2,'DNA');
insert into dupes values (3,'DNA');
insert into dupes values (4,'SINA');
insert into dupes values (5,'SUN');
insert into dupes values (6,'SUN');
insert into dupes values (7,'SUN');

可以看到,(‘DNA‘、‘SUN‘)中这两个人的数据重复,现在要求表中name重复的数据只保留一行,其它的删除。

删除数据有好几种方法,下面只介绍三种方法(处理数据需谨慎,要确认更改结果后再提交):

方法一:通过name相同、id不同的方式来判断

delete
   from dupes a
where exists (select NULL from dupes b where b.name = a.name and b.id > a.id);

利用这种方式删除数据时需要建立组合索引:

create index idx_name_id on dupes(name,id);

方法二:用rowid来代替其中的id

delete
   from dupes a
where exists (select NULL from dupes b where b.name = a.name and b.rowid > a.rowid);

因为不需要关联id列,我们只需要建立单列索引:

create index idx_name on dupes(name);

方法三:通过分析函数根据name分组生成序号,然后删除序号大于1的数据。

我们也可以用分析函数取出重复的数据后删除。下面先看生成的序号:

SQL> select rowid as rid,
         name,
         row_number() over(partition by name order by id) as seq
       from dupes
     order by 2,3;

RID                NAME                        SEQ

------------------ -------------------- ----------

AAADmPAAEAAAAQfAAB DNA                           1

AAADmPAAEAAAAQfAAC DNA                           2

AAADmPAAEAAAAQfAAA NBA                           1

AAADmPAAEAAAAQfAAD SINA                          1

AAADmPAAEAAAAQfAAE SUN                           1

AAADmPAAEAAAAQfAAF SUN                           2

AAADmPAAEAAAAQfAAG SUN                           3

已选择7行。

取出序号后,再删除seq>1的语句即可:

delete
    from dupes
  where rowid in (select rid
                 from (select rowid as rid,
                       row_number() over(partition by name order by id)as seq
                       from dupes)
                  where seq > 1);

当然还有其他写法,大家自己研究。

注:row_number() over()

语法:ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN)

简单的说row_number()从1开始,为每一条分组记录返回一个数字,这里的ROW_NUMBER() OVER (ORDER BY xlh DESC) 是先把xlh列降序,再为降序以后的没条xlh记录返回一个序号。

示例:

xlh           row_num

1700              1

1500              2

1085              3

710               4

row_number() OVER(PARTITION BY COL1 ORDER BY COL2) 表示根据COL1分组,在分组内部根据 COL2排序,而此函数计算的值就表示每组内部排序后的顺序编号(组内连续的唯一的)

转载请注明出处:http://blog.csdn.net/acmman/article/details/51392417

时间: 2024-11-24 20:24:34

【sql查询与优化】4.删除插入与更新的相关文章

SQL查询语句优化方法

以下是网上流传比较广泛的30种SQL查询语句优化方法: 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null可以在num上设置默认值0,确保表中num列没有null值,然后这样查

SQL 查询语句优化

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0 3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放

MySQL 常用30种SQL查询语句优化方法

1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描.如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from

MySQL常用30种SQL查询语句优化方法请慢用!

1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描.如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from

SQL 查询性能优化----解决书签查找

先来看看什么是书签查找: 当优化器所选择的非聚簇索引只包含查询请求的一部分字段时,就需要一个查找(lookup)来检索其他字段来满足请求.对一个有聚簇索引的表来说是一个键查找(key lookup),对一个堆表来说是一个RID查找(RID lookup).这种查找即是——书签查找. 书签查找根据索引的行定位器从表中读取数据.因此,除了索引页面的逻辑读取外,还需要数据页面的逻辑读取. 从索引的行定位器到从表中读取数据这之间会产生一些额外的开销,本文就来解决这个开销. 先看下我的测试表结构: 其中可

mysql经纬度查询并且计算2KM范围内附近用户的sql查询性能优化实例教程

之前很傻很天真地以为无非就是逐个计算距离,然后比较出来就行了,然后当碰到访问用户很多,而且数据库中经纬度信息很多的时候,计算量的迅速增长,能让服务器完全傻逼掉,还是老前辈的经验比我们丰富,给了我很大的启示. MySQL性能调优 – 使用更为快速的算法进行距离计算 最近遇到了一个问题,通过不断的尝试最终将某句原本占据近1秒的查询优化到了0.01秒,效率提高了100倍. 问题是这样的,有一张存放用户居住地点经纬度信息的MySQL数据表,表结构可以简化 为:id(int),longitude(long

sql查询语句优化

http://www.cnblogs.com/dubing/archive/2011/12/09/2278090.html 最近公司来一个非常虎的dba  10几年的经验 这里就称之为蔡老师吧 在征得我们蔡老同意的前提下  我们来分享一下蔡老给我们带来的宝贵财富 欢迎其他的dba来拍砖  目录 1.什么是执行计划?执行计划是依赖于什么信息.2. 统一SQL语句的写法减少解析开销3. 减少SQL语句的嵌套4. 使用“临时表”暂存中间结果5. OLTP系统SQL语句必须采用绑定变量6. 倾斜字段的绑

将Sql查询语句获取的数据插入到List列表里面

Sql查询语句获取的数据是分格式的,我们还用SqlDataReader来做,然后用IDataReader来接收读取,以下是代码: //我想查询一个用户表的信息,该用户有姓名,密码,信息三列 //1.定义一个用户类型的List数组,userInfo类的代码在下方 List<userInfo> userInfo = new List<userInfo>(); //2.我们要读取查询语句的数据,并且保存了.这里我们将使用IDataReader语句 //数据库类的实例,类的代码在下方 DB

【sql查询与优化】5.使用字符串

注:以下所有sql案例均取自"oracle查询优化改写技巧与案例"丛书. 1.遍历字符串 有时候要求把字符串拆分成单个字符,如: create or replace view v as select '天天向上' as 汉字, 'TTXS' as 首拼 from dual; 为了核对表中保存的"首拼"是否正确,需要把字符串拆分成下面的样式: 汉字       首拼 ----   ---- 天          T 天          T 向          X