sql 几点记录

 

1       With子句

1.1     学习目标

掌握with子句用法,而且了解with子句可以提升查询效率的缘由。sql

1.2     With子句要点

  1. with子句的返回结果存到用户的临时表空间中,只作一次查询,反复使用,提升效率。
  2. 若是定义了with子句,而在查询中不使用,那么会报ora-32035 错误:未引用在with子句中定义的查询名。
  3. 前面的with子句定义的查询在后面的with子句中可使用。可是一个with子句内部不能

嵌套with子句。oracle

  1. 当一个查询块名字和一个表名或其余的对象相同时,解析器从内向外搜索,优先使用子查询块名字。
  2. with查询的结果列有别名,引用的时候必须使用别名或*。
  3. with有可能影响执行计划。

1.3     with子句语法

 

With alias_name as (select1), --as和select中的括号都不能省略函数

alias_name2 as (select2),--后面的没有with,逗号分割,同一个主查询同级别地方,with子学习

查询只能定义一次测试

优化

alias_namen as (select n) –与下面的实际查询之间没有逗号指针

Select ….code

1.4 with使用例子:

  1. 最简单的使用方法:

如查询部门名称包含“A”的全部员工信息对象

--with clause排序

with a as

(select deptno from dept where dname like '%A%')

select * from emp where deptno in (select * from a);

with a as

(select deptno from dept where dname like '%A%'),--a结果集

a2 as(select * from a where deptno>20)--a1结果集直接从a中筛选

select * from emp where deptno in (select * from a2);

 

  1. 多层同级只能用一个with,而且后面的结果集可使用前面的结果集:

查询部门名称包含“A”而且部门编号大于20的全部员工信息

with a as

(select deptno from dept where dname like '%A%'),--a结果集

a2 as(select * from a where deptno>20)--a1结果集直接从a中筛选

select * from emp where deptno in (select * from a2);

 

  1. 不一样级查询可使用多个with:

查询部门名称包含“A”而且部门编号大于20的全部员工信息的另一种实现方式以下

with a as

(select deptno from dept where dname like '%A%')--a结果集

select * from emp where deptno in (--括号内层做为子查询,为第二级

with a2 as(select * from a where deptno>20)--a1结果集直接从a中筛选

select * from a2

);

1.5     使用场景

那什么状况下能使用到with子句呢?如下我就举几个简单的例子,简单的说明如下:

  1. 我想测试一句sql,而我不想专门创建一个测试表:

我想测试成绩大于90的学生,我不想创建学生表,能够用到with子句

with stu as(

select '张娜' sname,99 score from dual union

select '王杰' ,35  from dual union

select '宋丽' ,85  from dual union

select '陈晓' ,73  from dual union

select '李元' ,100  from dual

)--with 组成一个临时的结果集,存放在用户的临时表空间

select  * from stu where score>90

  1. 当一个sql重复用到某个相同的结果集做为子查询:

--查询销售部工资>1500或者销售部工资小于1000的员工

select * from emp where deptno=(select deptno from dept where dname ='SALES') and sal >1500

union all

select * from emp where deptno=(select deptno from dept where dname ='SALES') and sal <1000

--以上sql select deptno from dept where dname ='SALES'须要执行两次,影响效率

--可使用with优化一下

with salno as(select deptno from dept where dname ='SALES')

select * from emp where deptno=(select * from salno) and sal >1500

union all

select * from emp where deptno=(select * from salno) and sal <1000

2       集合操做

2.1     学习目标

掌握union,union all,minus,intersect的使用,可以描述集合运算,了解内部运行原理。

2.2     要点

 

Union all 效率通常比union高。Union all内部不作排序工做,也不作剔除

重复行工做,而union则作这个工做。因此当数据量比较大的时候,能用union all的时候尽可能用union all。除了union all 默认不作排序和剔除重复行的操做外,

union,minus,intersect都默认按第1个查询结果的第1列进行升序排列,而且

不包含重复行。

2.3     语法

(select  resource 1)

Union/union all/minus/intersect

(select  resource 2)

Union/union all/minus/intersect

(select  resource 3)

……….

其中查询结果集的各个字段的类型可以互相兼容,而且总的结果集字段名与第一个结果集相同。

2.4     使用案例

数据准备:

create table t1 as select rownum rn from dual connect by rownum<7;

create table t2 as select rownum+3 rn from dual connect by rownum<7;

                 

  1. 查询t1和t2表的全部记录,不去除重复。

 

  1. 查询t1和t2表的全部记录,去除重复。

 

 

  1. 查询t1和t2表都存在的记录

 

  1. 查询t1表存在,t2表不存在的记录

 

  1. 排序操做:

 

  1. 除了union all其余的所有会在总的结果集中剔除重复,例如:

insert into t1 values(1);

commit;

如今t1表中有两条相同的记录,其rn的值为1。

在进行集合运算时重复的记录被剔除:

 

2.5     使用场景

当要对多个结果集进行集合操做时,但是使用集合操做。

3       case与decode

3.1     学习目标

会使用case表达式和decode函数,理解各个参数和返回值的含义。

3.2     要点

Case表达式:

  1. When后面的表达式类型应该所有保持一致,返回值类型也必须保持一致,或者可以进行隐式转换。
  2. case 表达式 when 值,若是值是null,就算表达式也是null,结果也是返回false。也就是case 后面的表达式若是值为null,不会与when null 匹配,只会与else 匹配。

Decode函数的使用方法与case when类似,可是decode只能用等号匹配。

3.3     语法

Case表达式第一种:

case exp when comexp then returnvalue

..when comexp then returnvalue

Else

Returnvalue

End

Case表达式第二种:

case when Boolean then returnvalue

..when Boolean then return value

Else

Returnvalue

End

Decode函数:

decode(exp,

value1,res1,

value2,res2,….,

valuen resn,

elsevalue)。

3.4     使用案例

Case 第一种用法:

 

Case 第二种用法:

 

Decode用法:

 

上文提到过null,碰到null的时候要注意,好比:

 

这种状况能够这样处理:

 

若是用decode函数:

 

3.5     使用场景

当咱们的sql要求根据不一样的条件返回不一样的值时,可使用。

4       exists与in、not exists与not in

4.1     学习目标

掌握exists与in的、not exists与not in的用法,了解其内部的执行顺序 与执行原理,知道什么状况下用exists,什么状况下用in。

4.2     要点

  1. Exists 用于只能用于子查询,能够替代in,若匹配到结果,则退出内部

查询,并将条件标志为true,传回所有结果资料。

  1. 若子查询结果集比较小,优先使用in,若外层查询比子查询小,优先使用exists。由于若用in,则oracle 会优先查询子查询,而后匹配外层查询,若使用exists,则oracle 会优先查询外层表,而后再与内层表匹配。最优化匹配原则,拿最小记录匹配大记录。

4.3     语法

In:Select select_fields from table_name where field_name in(select clause);

Exists:Select select_fields from table_name exists (select clause)

4.4     使用案例

查询员工部门编号在部门表中存在的员工记录:

 

以上语句能够用Exist替换:

 

另外not in和not exists在某些状况下也能够相互转换,可是要注意一点,not in中的子查询返回的结果集包含null值的时候,查询会失效。例如我想查询对应员工记录数为0的部门。以下:

 

用not exists:

 

以上语句不能用not in替换:

 

查询失效无记录返回。注意这并非oracle的bug,由于在oracle中null不表示空,而是表示未知,当使用not in的时候,若是子查询返回的结果集中包含null值,咱们并不知道外层查询的记录在不在子查询返回的结果集以内,因此无记录返回。虽然这样,可是并不表示not in和not exists是彻底不能够转换的,好比子查询所选的字段在对应的表中没有null值,这时not in和not exists是能够相互转换的。或者在某些状况下内层子查询加上field_name is not null限制条件也是能够的。

4.5     使用场景

当内层查询返回的结果集较小时,用in 或者not in效率较高。当内层子查询返回的结果集比较大时,用exists或者not exists执行的效率较高。

5       行列互换

5.1     学习目标

掌握列转行技术和经常使用的行专列技术。

5.2     要点

行专列的状况有多种,不一样的状况侧重点也不同。

5.3     语法

5.4     使用案例

  1. 列转行

第一种方法:须要用到union或者union all:

 

第二种方法:用到model

 

  1. 行专列,如我有escore表用来记录每一个学生每一个科目的成绩,以下:

 

若是我想将每一个学生的成绩统计在一行上,如:

3 语文 11 数学 55 英语 66

则我可使用以下sql:

 

这个sql表面上看没什么问题,可是仔细看一下三个结果集es、ys和ss,他们来源于同一个表,并且查询方法也相似,都是根据type的值去筛选的,这样就会对escore表查询三遍,严重影响查询速率,那这个sql咱们如何去优化呢!

首先在你的脑海里面要有一种思路,根据需求,原先每一个学生成绩有多行记录,如今要显示到一行上,那通常状况下咱们是须要根据学生分组的。因此group by sid 这个是必定要有的,既然分组那咱们但是使用oracle的聚合函数去求其余行的数据。至于科目字段目前都是已知的,也就是第2,4,6列显示的分别是英语、语文、数学这几个字,是常量,咱们不用去考虑,那剩下的也就是最关键的,咱们去求三科的成绩就能够了。
让咱们再看一下escore表,当指针移到某一行数据时,当type=e时,咱们就取到score,加到第三列上,那第五列和第七列就加0,也就是sum(decode(type,’e’,score,0)),其余列相似,这样group by时用到的聚合函数还有decode结合在一块儿使用,就能够完成咱们的要求了,sql写出来时这样的:

 

  1. 字符串组合的多行转一列,例如我有一张测试表以下:

 

我想根据id分组,将每一行的name链接起来,以下图是我想要的结果:

 

这种行转列不是真正意义的行转列,是多行数据的值拼接后显示到一列上,那这种状况怎么处理呢,首先分析一下:多行id相同的值转换成一行,通常状况下须要用到group by,可是对于字符串,oracle中没有一个聚合函数适合用到此处的字符串链接,那该怎么办呢?

在oracle中,有sys_connect_by_path(field_name,concat_value)函数,能够经过connect by来依次链接每一行的数据,connect by 的语法是这样的:

start with field1=1--以当前表达式返回true的行开始

connect by prior field2=field3--经过当前行查找下一行,也就是说某一行数据的field3字段等于当前行的field2,那就把这行数据做为下一行

    有了这个思路,咱们就能够用connect by 经过使用sys_connect_by_path(field_name,concat_value)这个函数,而且根据id分组,将字符串链接在一块儿,而后经过max聚合函数,选出每组最长的字符转就能够了,那剩下的也就是最关键的问题就是我怎样去使用connect by,经过当前行找到下一行呢?充分发散一下你的思惟,看一下以下结果集:

 

那我下一步用以下思路使用connect by将所要的结果查询上来:

start with  lg is null--lgnull的行做为起始行

connect by prior rn=lg and prior id=id --当前行与其余行比较,知足这个条件的就做为下一行数据     

总的查询结果以下:

 

其实怎么使用connect by 方法不少,例如以下sql也能完成:

相关文章
相关标签/搜索