14.0、注释:
1、单行注释:--
2、多行注释:/* */
14.1、sqlplus中的set指令:
1、设置每行显示的数据长度:
SET LINESIZE 500;
#有效范围是1-32767,默认情况下显示80个字符
2、设置每页显示的数据行数:
SET PAGESIZE 50;
#最大值为50000,默认值为14,到了14行会把标题再次打出,下面跟数据
3、说明:
这两个指令称为格式化指令
SQL> SET LINESIZE 500
SQL> SET PAGESIZE 50
#这样设置是单次有效的,下次再用Oracle的时候又要重新设置,修改$ORACLE_HOME/sqlplus/admin/glogin.sql配置文件,增加这两个指令即可
[oracle@slave-node2 ~]$ vim $ORACLE_HOME/sqlplus/admin/glogin.sql
SET LINESIZE 500
SET PAGESIZE 50
14.2、sqlplus 编辑器配置:
1、在没有图形界面的情况下,要想编写程序代码,那么就必须启动本地的记事本程序;
2、在Linux下更改SQLPlus ed默认打开程序,修改$ORACLE_HOME/sqlplus/admin/glogin.sql配置文件,增加define _editor=vim即可,sqlplus启动
的时候会读取glogin.sql脚本;
[oracle@slave-node2 ~]$ vim $ORACLE_HOME/sqlplus/admin/glogin.sql
define_editor = vim
#这样我们在使用SQLPlus的ed命令就可以调用vi或vim编辑器了;
3、使用:
SQL> ed
select * from dba_data_files
/
#退出编辑器后,输入"/"即可运行之前编辑的sql语句,注意,一次只能编辑一条sql语句,且末尾不能有";"号结尾,否则会报
"ORA-00911: invalid character"的错误,在sqlplus中输入sql语句会替换编辑的sql语句,文件默认保存在"/home/oracle/afiedt.buf"文件中;
14.3、SQL语言:
1、SQL语句语法没有这么复杂;
2、SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY、INSERT、UPDATE、DELETE、CREATE、DROP、ALTER、GRANT、REVOKE;
3、严格来讲SQL会分为三种类型:
(1)DML(数据操作语言):主要指的是数据库的查询与更新操作;
(2)DDL(数据定义语言):主要指的是数据对象的创建(表、用户),例如:CREATE、DROP、ALTER,这部分的操作需要使用到相关的设计范式;
(3)DCL(数据库控制语言):主要是进行权限的管理操作;
14.4、SQL限定查询:
1、语法结构:
在很多时候并不需要查询所有数据行内容,此时就可以通过WHERE子句筛选要显示的数据行,语法结构:
SELECT [DISTINCT] * | 列名称 [别名],列名称 [别名],...
FROM 表名称[别名]
[WHERE 过滤条件(s)]
2、如果要想实现限定查询,那么需要掌握一些列的限定查询的符号,有如下几种:
关系运算符:>、<、>=、<=、<>(!=)
逻辑运算符:AND、OR、NOT
范围运算符:BETWEEN...AND...
谓词运算符:IN、NOT IN
空判断:IS NULL、IS NOT NULL
模糊查询:LIKE
3、关系运算符:
(1)要求查询出所有基本工资高于1500的雇员信息:
SQL> SELECT * FROM emp WHERE sal > 1500;
(2)查询Smith的基本信息:
SQL> SELECT * FROM emp WHERE ename='SMITH';
#在Oracle数据库之中,所有数据需要区分大小写
(3)查询职位不是销售人员的雇员编号、姓名、职位:
SQL> SELECT empno,ename,job FROM emp WHERE job != 'SALESMAN';
4、逻辑运算符:
(1)查询出工资范围在1500-3000之间的雇员信息:
SQL> SELECT * FROM emp WHERE sal>=1500 AND sal<=3000;
(2)查询工资大于2000或者职位是办事员的所有雇员信息:
SQL> SELECT * FROM emp WHERE sal>2000 OR job='CLERK';
#不要写过于复杂的逻辑操作
5、范围查询:
(1)
在进行查询条件过滤的时候可以针对某一个范围的数据进行过滤,使用BETWEEN...AND,语法如下:
BETWEEN 最小值(数字、日期) AND 最大值——》闭区间操作
#此最小值(包含最小值)与最大值(包含最大值)之间的内容都满足条件;
(2)查询工资在1500到2000之间的雇员(包含1500,2000):
SQL> SELECT * FROM emp WHERE sal>=1500 AND sal<=2000;
SQL> SELECT * FROM emp WHERE sal BETWEEN 1500 AND 2000;
#说明:第一个查询需要匹配两个条件,而第二个查询只需要匹配一个条件;
(3)查询所有在1981年雇佣的雇员:
SQL> SELECT * FROM emp WHERE hiredate BETWEEN '01-JAN-1981' AND '31-DEC-1981';
6、空判断:
(1)查询领取佣金的雇员信息(佣金不为空):
SQL> SELECT * FROM emp WHERE comm IS NOT NULL;
SQL> SELECT * FROM emp WHERE NOT comm IS NULL;
7、IN操作符:
(1)IN操作符类似于BETWEEN...AND,但是BETWEEN...AND是给了一个大的范围,而IN给出的是一个指定的可选范围;
(2)要求查询出雇员编号是7369、7566、7788、9999的雇员:
SELECT * FROM emp WHERE empno=7369 OR empno=7566 OR empno=7788 OR empno=9999;
SELECT * FROM emp WHERE empno IN(7369,7566,7788,9999);
#在指定值查询的过程之中,IN的操作是最简短的;
(3)要求查询出雇员编号不是7369、7566、7788、9999的雇员操作:
SELECT * FROM emp WHERE empno NOT IN(7369,7566,7788,9999);
SELECT * FROM emp WHERE NOT empno IN(7369,7566,7788,9999);
#注意:关于NOT IN与NULL的问题,在使用NOT IN进行范围判断的时候,如果范围里面包含有NULL,那么不会有任何的
#结果返回,不允许在使用NOT IN查询时,查询内容为NULL;因为not in要与集合中每条记录比较,每条记录都不相同才
#返回true。当集合中包含null时,每条记录与之比较都会退出,所以无任何结果返回;
#in 判断时,每条记录与集合里的每一个记录比较,只要有一条记录比较相同,就返回true。所以,即使这个集合里有null,
#也不影响in的结果;
#in是"或",not in 是"且",null永远不等于null,任何值和null进行四则运算结果都是null,任何值和null进行比较都不会返
#回任何的结果;
(4)使用IN操作中包含有NULL:
SQL>SELECT * FROM emp WHERE empno IN(7369,7566,7788,NULL);
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- -------------------- ------------------ ---------- ------------ ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7566 JONES MANAGER 7839 02-APR-81 2975 20
7788 SCOTT ANALYST 7566 1 9-APR-87 3000 20
(5)使用NOT IN操作中包含有NULL:
SQL>SELECT * FROM emp WHERE empno NOT IN(7369,7566,7788,NULL);
no rows selected
8、模糊查询(LIKE):
(1)可以在数据库之中执行数据的模糊查询,在使用LIKE的时候可以使用两个通配符:
"_":只匹配任意一个字符;
"%":匹配任意0或多个字符;
(2)查询姓名是以字母以A开头的雇员信息:
SQL> SELECT * FROM emp WHERE ename LIKE 'A%';
(3)查询姓名的第二个字母是以A开头的雇员信息:
SQL> SELECT * FROM emp WHERE ename LIKE '_A%';
like 'h\_%':表示匹配以"h_"为开头的字符;
(4)查询姓名中任意位置包含字母A的雇员信息:
SQL> SELECT * FROM emp WHERE ename LIKE '%A%';
#说明一:LIKE可以应用在各种数据类型上,不一定非要是字符串;
#说明二:在以LIKE模糊查询的时候,如果不设置查询关键字,那么表示查询全部;
14.5、查询排序(ORDER BY):
1、如果需要针对查询后的结果按照指定的数据列进行排序操作,就必须使用ORDER BY子句;
2、当使用查询操作的时候,默认情况下它会按照数据的插入顺序进行数据排序显示;
3、如果要针对指定的列内容实现排序,就必须采用ORDER BY子句完成;
4、语法结构:
SELECT [DISTINCT] * | 列名称 [别名],列名称 [别名],...
FROM 表名称[别名]
[WHERE 过滤条件(s)]
[ORDER BY 字段 [ASC | DESC],字段 [ASC | DESC],...];
5、对于排序的方式有两种:
ASC:默认,不写也是ASC:按照升序的方式排列,从小到大;
DESC:按照降序的方式排列,从大到小;
6、查询范例:
(1)查询所有的雇员信息,要求按照工资由高到低排序:
SQL> SELECT * FROM emp ORDER BY sal DESC;
(2)查询所有销售人员的信息,要求按照雇佣日期由早到晚排序:
SQL> SELECT * FROM emp WHERE job='SALESMAN' ORDER BY hiredate ASC;
(3)要求按照工资由高到低排序,如果工资相同,则按照雇佣日期由早到晚排序:
SQL> SELECT * FROM emp ORDER BY sal DESC,hiredate ASC;
(4)查询每个雇员的编号、姓名、年薪,按照年薪由高到低排序:
SQL> SELECT empno,ename,sal*12 AS income FROM emp ORDER BY income DESC;
#此时的程序可以正确调用SELECT定义的别名,在整个SQL查询结构之中,只有ORDER BY子句可以调用SELECT定义的别名;
7、总结:
(1)使用ORDER BY查询的时候,排序是在最后完成的
(2)ORDER BY子句是最后一个执行的子句
(3)在ORDER BY之中可以设置多个排序的字段
(4)ORDER BY有两种排序模式:ASC(升序,默认)、DESC(降序)
(5)ORDER BY是唯一一个可以使用SELECT子句定义别名的子句
(6)如果有空值的话,降序在最上,升序在最下,因为null是无穷大的值
14.6、单行函数:
1、所谓的单行函数指的就是完成某一具体功能的操作函数,例如:转大小写等,一般而言,单行函数的格式:返回值 函数名称(参数);
2、单行函数分为以下几种:字符串函数、数值函数、日期函数、转换函数、通用函数;
3、字符串函数:
字符串函数是处理字符串数据的(对于字符串的数据有可能是从列上找到的,或者是直接设置的字符串常量),包含的函数有如下几种:
No 函数名称 返回类型 描述
1 UPPER(<字符串列>|<字符串>) 字符串 将传入的字符串变成大写形式
2 LOWER(<字符串列>|<字符串>) 字符串 将传入的字符串变成小写形式
3 INITCAP(<字符串列>|<字符串>) 字符串 开头首字母大写,其他的字母变为小写
4 LENGTH(<字符串列>|<字符串>) 数字 取得指定字符串的长度
5 SUBSTR(<字符串列>|<字符串>,<开始索引>,<长度>) 字符串 进行字符串的截取,如果没有设置长度,表示从截取全部
6 REPLACE(<字符串列>|<字符串>,<旧内容>,<新内容>) 字符串 将指定的字符串数据以新数据替换旧数据
(1)在Oracle里面,所有的函数如果要想进行验证,也必须编写SQL语句,为了方便用户进行一些验证或者是一些不需要查询
表的查询操作,专门提供了一个dual的虚拟表;
(2)转大写小写操作:
1)转大写:
SELECT UPPER('hello') FROM dual;
2)将所有姓名转小写
SELECT LOWER(ename) FROM emp;
#一般在一些不区分数据大小写的情况都会同意的将所有的内容转成大写或小写的形式处理;
(3)首字母大写其他字母小写:
1)将所有的雇员姓名以首字母大写的形式保存:
SELECT INITCAP(ename) FROM emp;
(4)取得字符串的长度:
1)
SELECT LENGTH('ddffgg') FROM dual;
2)查询雇员姓名长度为5的全部雇员信息:
SELECT * FROM emp WHERE LENGTH(ename)=5;
(5)字符串截取:
1)
SQL> SELECT SUBSTR('helloworld',6) FROM dual;
SUBSTR('HE
----------
world
2)
SQL> SELECT SUBSTR('helloworld',1,5) FROM dual;
SUBSTR('HE
----------
hello
#在程序之中所有的字符串的首字母的索引都是0,但是在Oracle里面,所有的字符串的首字母索引都是1,如果设置的是0 ,那么它也会按照1的方式进行处理;
3)要求截取每个雇员姓名的前三个字母:
SELECT SUBSTR(ename,1,3) FROM emp;
4)取每个雇员姓名的后三位:
SELECT SUBSTR(ename,-3) FROM emp;
(6)字符串替换:
SELECT REPLACE('我们','我','你') FROM dual;
#将"我们"中的"我"替换为"你"
4、数值函数:
数值函数主要是进行数字的处理,最为核心的数值函数一共有3个;
No 函数名称 返回类型 描述
1 ROUND(<数字列>|<数字>,<小数位>) 数字 实现数据的四舍五入,可以设置保留小数位
2 TRUNC(<数字列><数字>,<小数位>) 数字 实现数据的截取(不进位)
3 MOD(<数字列>|<数字>,<小数位>) 数字 求模(求余数)
(1)ROUND()函数:
1)
SQL> SELECT ROUND(545,2) FROM dual;
ROUND(545,2)
-------------
545
2)
SQL> SELECT ROUND(1.2357,2) FROM dual;
ROUND(1.2357,2)
---------------
1.24
3)
SQL> SELECT ROUND(769.2,-2) FROM dual;
ROUND(769.2,-2)
---------------
800
#从小数点往前
4)
SQL> SELECT ROUND(732,-2) FROM dual;
ROUND(732,-2)
-------------
700
(2)TRUNC()函数:
SQL> SELECT TRUNC(532.4567),TRUNC(532.4567,2),TRUNC(552.4567,-2) FROM dual;
TRUNC(532.4567),TRUNC(532.4567,2),TRUNC(552.4567,-2)
--------------- ----------------- ------------------
532 532.45 500
(3)MOD()函数(取余):
SQL> SELECT MOD(17,5) FROM dual;
MOD(17,5)
----------
2
5、日期函数:
如果要想处理任何日期,那么都有一个基本的前提,必须知道当前的日期是什么,如果要想取得当前日期时间,在
Oracle中专门提供了一个伪列:SYSDATE(SYSTIMESTAMP);
(1)日期查询:
1)
SQL> SELECT SYSDATE FROM dual;
07-8月 -19
SQL> SELECT SYSTIMESTAMP FROM dual;
07-8月 -19 06.02.48.420416000 下午 +08:00
2)
SYSDATE伪列里面包含有日期时间的内容,只不过现在只显示了日期数据,如果清楚了当前日期,还需要清楚三个日
期的操作公式;
日期 + 数字 = 日期(表示若干天之后的日期)
日期 - 数字 = 日期(表示若干天之前的日期)
日期 - 日期 = 数字(天数)
3)
SELECT SYSDATE-20,SYSDATE+250 FROM dual;
对于日期而言,由于每个月的天数是不同的,所以直接进行天数加法计算月数是不准确的;
4)要求查询出每个雇员的编号、姓名、职位、已经被雇佣的天数;
SELECT empno,ename,job,SYSDATE-hiredate FROM emp;
(2)如果直接使用天数来实现年或月的计算,那么最终的结果一定是不准确的,因为每个月的天数不一致,为了准确的
进行日期操作,在Oracle里面提供有四个日期处理函数:
No 函数名称 返回类型 描述
1 ADD_MONTHS(<日期列>|<日期>,<月数>) 日期 在指定日期上增加若干个月之后的日期
2 MONTHS_BETWEEN(<日期列>|<日期>,<日期列列>|<日期>) 数字 返回两个日期之间的所经历的月数
3 LAST_DAY(<日期列>|<日期>) 日期 取得指定日期所在月的最后一天
4 NEXT_DAY(<日期列>|<日期>,<星期X>) 日期 返回下一个指定的一周时间数对应的日期
1)在当前日期下增加指定的月份:
SELECT ADD_MONTHS(SYSDATE,4) FROM dual;
2)计算所有雇员到今天为止雇佣的月数:
SELECT MONTHS_BETWEEN(SYSDATE,hiredate) FROM emp;
3)计算当前时间所在月的最后一天的日期:
SELECT LAST_DAY(SYSDATE) FROM dual;
4)要求查询出所有在雇佣所在月倒数第三天雇佣的雇员信息:
SELECT * FROM emp WHERE (LAST_DAY(hiredate)-hiredate)=2;
5)NEXT_DAY()函数:
SELECT NEXT_DAY(SYSDATE,'星期二') FROM dual;
6、转换函数:
到现在位置已经接触过了数字型、字符串型、日期型三类数据,它们可以互相转换,则需要使用如下的转换函数完成:
No 函数名称 返回类型 描述
1 TO_CHAR(<日期列>|<日期>|<数字列>|<数字>,<转换格式>) 字符串 将日期或数字格式化为指定结构的字符串
2 TO_DATE(<日期字符串列>|<日期字符串>,<转换格式>) 日期 按照指定的转换格式编写字符串后将其变为日期型数据
3 TO_NUMBER(<数字字符串列>|<数字字符串>,<转换格式>) 数字 将字符串变为数字
(1)转字符串函数,TO_CHAR():
如果要想将一个日期或数字变为字符串,那么首先必须清楚转换格式;
日期:年(yyyy)、月(mm)、日(dd)
时间:时(hh、hh24)、分(mi)、秒(ss)
数字:任意数字(9)、本地货币符号(L)
1)将日期显示格式化:
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;
2)实现日期数据的拆分:
SELECT TO_CHAR(SYSDATE,'yyyy'),TO_CHAR(SYSDATE,'mm'),TO_CHAR(SYSDATE,'dd') FROM dual;
3)要求查询出所有在2月份雇佣的雇员:
SELECT * FROM emp WHERE TO_CHAR(hiredate,'mm')='02';
SELECT * FROM emp WHERE TO_CHAR(hiredate,'mm')=2;
#Oracle之中提供有自动的转型操作,如果发现类型不匹配会自动完成类型匹配后再进行比较。
4)将数字格式化:
SELECT TO_CHAR(123456,'L999,999,999,999,999.99') FROM dual;
(2)转日期函数,TO_DATA(),(很少用):
1)将日期字符串转化为日期:
SELECT TO_DATE('1889-10-19','yyyy-mm-dd') FROM dual;
SELECT TO_CHAR((SELECT TO_DATE('1889-10-19','yyyy-mm-dd') FROM dual),'yyyy-mm-dd hh24:mi:ss') FROM dual;
(3)转数字函数,TO_NUMBER,(很少用):
1)将数字字符串转化为数字:
SELECT TO_NUMBER('1')+TO_NUMBER('1') FROM dual;
SELECT '1'+'1' FROM dual;
(4)Oracle里面已经默认提供了许多的自动转换机制,所以针对与转换函数而言,唯一重要的也就在于TO_CHAR()函数;
7、特殊函数NVL()函数:
(1)格式:
NVL(<数字、字符串、日期列>|<数字、字符串、日期>,<指定值>)
返回类型是数字,如果传入的内容是null,则使用指定值处理,如果不是null,使用原始数据处理;
(2)查询出每个雇员的编号、姓名、基本工资、佣金、年薪:
SELECT empno,ename,sal,comm,(sal+comm)*12 FROM emp;
#发现计算之后,没有佣金的雇员就没有年薪存在了,因为佣金为null的时候所做的任何计算结果都是null,为了保证计算结果
#的准确性,必须将null替换为0,那么这就属于NVL()函数作用了,正确的sql语句如下:
SELECT empno,ename,sal,comm,(sal+NVL(comm,0))*12 FROM emp;
14.7、数据集合查询:
1、数据的集合操作主要负责连接的是查询结果,对于查询结果的连接提供有四种操作符,UNION、UNION ALL、INTERSECT、
MINUS,利用这几个符号可以实现多个查询语句的连接,就相当于将多个查询结果连接为一个查询结果返
回,那么也就有一个明确的要求,多个查询结果返回的列的结构必须全部相同;
2、UNION操作(并集):
SELECT empno,ename,job FROM emp WHERE deptno=10 UNION SELECT empno,ename,job FROM emp;
此时是将两个查询结果合并在了一起,但是UNION的操作特点是如果遇见了有相同的内容,那么不会重复显示;
3、验证UNION ALL操作(并集显示重复):
SELECT empno,ename,job,deptno FROM emp WHERE deptno=10 UNION ALL SELECT empno,ename,job,deptno FROM emp;
所有的重复数据都会进行显示;
4、验证INTERSECT操作(交集):
SELECT empno,ename,job,deptno FROM emp WHERE deptno=10 INTERSECT SELECT empno,ename,job,deptno FROM emp;
此处返回了相同的数据部分,属于交集的操作;
5、验证MINUS操作(差集):
SELECT empno,ename,job,deptno FROM emp MINUS SELECT empno,ename,job,deptno FROM emp WHERE deptno=10;
此处返回了差集,但是差的计算是利用了第一个集合减去第二个集合;
6、小结:
(1)可以使用集合操作将多个查询结果合并到一起显示,但是要求,多个查询结果返回的结构必须相同;
(2)UNION(并集)、UNION ALL(有重复项的并集)、INTERSETC(交集)、MINUS(差集);
14.8、分组统计查询:
1、统计函数:
(1)常用统计函数:
ORACLE中有一个COUNT()函数,此函数的功能是统计表中的数据量,那么在SQL之中,定义了5个常用的统计函数:
1)COUNT(<*>|<列>|
2)MAX(<数字列>|<日期列>)
3)MIN(<数字列>|<日期列>)
4)SUM(<数字列>)
5)AVG(<数字列>)
#默认情况下,avg、sum、count<列>、max、min、对列进行统计时忽略null;
#当组函数要处理的所有的值都为null,count(<列>)函数返回的是0,avg sum max min函数返回的是null;
(2)统计函数查询:
1)查询所有雇员之中最高和最低工资:
SELECT MAX(sal),MIN(sal) FROM emp;
2)查询出所有雇员最早雇佣和最晚雇佣的雇佣日期:
SELECT MIN(hiredate),MAX(hiredate) FROM emp;
3)统计出所有雇员的总工资以及平均工资:
SELECT SUM(sal),AVG(sal) FROM emp;
对于SUM()和AVG()函数来说,只要是数值型数据都可以使用;
4)要求统计出雇员的平均服务年限:
SELECT TRUNC(AVG(MONTHS_BETWEEN(SYSDATE,hiredate)/12)) FROM emp;
(3)统计函数面试题:
请解释COUNT(*)、COUNT(字段)、COUNT(DISTINCT 字段)的区别:
COUNT(*):明确的返回表中的数据个数,是最准确的;
COUNT(<列>):不统计为null的数据个数,如果某一列的数据不可能为null,那么结果与COUNT(*)相同;
COUNT(DISTINCT <列>):不统计null且统计消除掉重复数据后的数据个数;
2、分组统计查询:
(1)分组统计格式:
能够分组的时候往往指的是部分数据不被某些共性,要想分组,使用GROUP BY子句完成;
SELECT [DISTINCT] 分组字段 [别名],... | 统计函数
FROM 表名称[别名]
[WHERE 过滤条件(s)]
[GROUP BY 分组字段,分组字段,分组字段,...]
[ORDER BY 字段 [ASC|DESC]];
(2)单表分组统计查询:
1)按照职位分组,统计出每个职位的名称、人数、平均工资:
SELECT job,COUNT(*),AVG(sal) FROM emp GROUP BY job;
2)要求查询出每个部门编号,以及每个部门的人数、最高与最低工资:
SELECT deptno,COUNT(*),MAX(sal),MIN(sal) FROM emp GROUP BY deptno;
以上实现了分组的操作,但是对于分组操作严格来讲还是存在一些使用上的限制:
(3)分组统计限制:
1)限制一,在没有编写GROUP BY子句的时候(全表做为一组),那么SELECT子句之中只允许出现统计函数,不允许出现任何的其他字段:
错误的的代码:SELECT COUNT(empno),ename FROM emp;
正确的代码:SELECT COUNT(empno) FROM emp;
2)限制二,在使用GROUP BY子句分组的时候,SELECT子句之中只允许出现分组字段与统计函数,其他字段不允许出现:
错误的代码:SELECT job,COUNT(*),ename FROM emp GROUP BY job;
正确的代码:SELECT job,COUNT(*) FROM emp GROUP BY job;
3)限制三,统计函数允许嵌套查询,但是嵌套后的统计查询中,SELECT子句里面不允许再出现任何的字段,包括分组字段,只能够使用嵌套的统计函数:
错误的代码:SELECT deptno,MAX(AVG(sal)) FROM emp GROUP BY deptno;
正确的代码:SELECT MAX(AVG(sal)) FROM emp GROUP BY deptno;
(4)多表分组统计查询:
1)查询每个部门的名称、人数、平均工资:
SELECT d.dname,COUNT(e.empno),AVG(e.sal) FROM emp e,dept d WHERE e.deptno(+)=d.deptno GROUP BY d.dname;
以上程序最大的特点在于是针对于一个多表查询的结果进行分组,查询结果由于是行列的组成,所以可以理解为一张临时数据表;
2)查询出每个部门的编号、名称、位置、部门人数、平均服务年限;
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),AVG(TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12)) AS YEAR FROM emp e,dept d WHERE e.deptno(+)=d.deptno GROUP BY d.deptno,d.dname,d.loc;
多字段分组的时候只有多个列的数据完全重复的时候才可以使用;
(5)HAVING子句:
1)语法格式:
1.SELECT [DISTINCT] 分组字段 [别名],... | 统计函数
2.FROM 表名称[别名]
3.[WHERE 过滤条件(s)]
4.[GROUP BY 分组字段,分组字段,分组字段,...]
5.[HAVING 分组后的过滤条件]
6.[ORDER BY 字段 [ASC|DESC]];
2)查询出平均工资高于2000的职位名称以及平均工资:
SELECT job,AVG(sal) FROM emp WHERE AVG(sal)>2000 GROUP BY job;
ERROR at line 1:
ORA-00934: group function is not allowed here
运行之后会发现给出的错误提示:WHERE子句上不允许使用分组函数,之所以不能够使用是因为统计的操作属于GROUP BY之后的范畴了,而
WHERE是在GROUP BY操作之前使用的,所以此时如果要想针对分组后的数据进行过滤,那么只能够使用HAVING子句,正确的sql写法如下:
SELECT job,AVG(sal) FROM emp GROUP BY job HAVING AVG(sal)>2000;
3)WHERE与HAVING的区别:
WHERE发生在GROUP BY操作之前,属于分组前的数据筛选,是从所有的数据之中筛选出可以分组的数据,WHERE子句不允许使用统计函数;
HAVING发生在GROUP BY操作之后,是针对于分组后的数据进行筛选,HAVING子句可以使用统计函数;
4)显示非销售人员工作名称以及从事同一工作雇员的月工资的总和,并且要求满足从事同一工作的雇员的月工资合计大于$5000,输出结果按月工资的合计升序排列:
SELECT job,SUM(sal) AS sum FROM emp WHERE NOT job='SALESMAN' GROUP BY job HAVING SUM(sal)>5000 ORDER BY sum;
3、小结:
(1)默认情况下,avg、sum、count、max、min、对列进行统计时忽略null:
1)COUNT(*):明确的返回表中的数据个数,是最准确的;
2)COUNT(<列>):不统计为null的数据个数,如果某一列的数据不可能为null,那么结果与COUNT(*)相同;
3)COUNT(DISTINCT <列>):不统计null且统计消除掉重复数据后的数据个数;
4)当组函数要处理的所有的值都为null,count(<列>)函数返回的是0,avg sum max min函数返回的是null;
5)avg默认的条件下是不统计null的,这就导致平均数变大,所以求准确的平均数时需要使用如下方法进行计算:
SELECT SUM(<数字列>)/count(*) AS "avg" FROM <表名>;
(2)不管是单表分组还是多表分组,重点先看重复的列,如果是一个重复列,那么就在GROUP BY后面写一个字段,如果是多个,就写多个字段;
(3)分组中的使用限制:
1)在没有编写GROUP BY子句的时候(全表做为一组),那么SELECT子句之中只允许出现统计函数,不允许出现任何的其他字段(因为默认统计函数使用了group by);
select count(mail) from user_table group by mail; 等价于 select count(name) from user_table;
2)在使用GROUP BY子句分组的时候,SELECT子句之中只允许出现分组字段与统计函数,其他字段不允许出现;
3)统计函数允许嵌套查询,但是嵌套后的统计查询中,SELECT子句里面不允许再出现任何的字段,包括分组字段,只能够使用嵌套的统计函数;
4)WHERE子句不允许使用统计函数,HAVING子句可以使用统计函数;
5)多字段分组的时候只有多个列的数据完全重复的时候才可以使用;
(4)分组统计的时候,查询结果相当于是一张临时表,所有的分组是在临时表里完成的;
14.9、补充:
1、拼接运算符:
(1)|| 可以将某几列或者某列与字符串拼接在一起;
(2)单引号代表的是字符的常量,双引号代表的是标识的名字;
(3)SELECT <列名1> || 'String is' || <列名2> '.' AS <"标识名"> FROM <表名>;
2、distinct实现对select查询语句后的结果去重:
SELECT DISTINCT <列名> FROM <表名>;
#遇到空值时返回只保留一个空值;
3、varchar和varchar2的区别:
(1)varchar2对大小写和空格敏感;
(2)char对大小写敏感,对空格不敏感;
(3)varchar2 必须定义长度,最大长度4000字节,char可以不定义长度,缺省值是1,最大长2000字节;
(4)varchar2按照字符串的实际长度存,char按照定义的长度存;
(5)列的取值是定长,定义成char类型;列的取值长度不固定,定义成varchar2;
(6)VARCHAR2比CHAR节省空间,在效率上比CHAR会稍微差一些,即要想获得效率,就必须牺牲一定的空间,
这也就是我们在数据库设计上常说的'以空间换效率';
(7)VARCHAR2虽然比CHAR节省空间,但是如果一个VARCHAR2列经常被修改,而且每次被修改的数据的长度
不同,这会引起'行迁移'(Row Migration)现象,而这造成多余的I/O,是数据库设计和调整中要尽力避免的,在这
种情况下用CHAR代替VARCHAR2会更好一些;
4、拼接运算(CONCAT):
SELECT CONCAT(<列名1>, ',' ,<列名2>) AS <"标识名"> FROM <表名>;
5、谨记 null!=null;
6、子查询:
(1)子查询就是在一条sql语句中嵌入select语句;
(2)在主查询中嵌入子查询
(3)子查询的结果作为主查询的条件
(4)若子查询的返回结果有多个值,会去掉重复之后,再将结果返回给主查询;
(5)子查询:
select <列名1>,<列名2> from <表名> where salary=(select min(<列名2>) from <表名>);
(6)先排序后过滤:
select <列名1> from (select <列名1> from <表名> order by <列名1> desc) where <列名1>=1;
7、数字类型:
(1)oracle本来就没有int类型,为了与别的数据库兼容,新增了int类型作为number类型的子集;
(2)int类型只能存储整数;
(3)number可以存储浮点数,也可以存储整数;
(4)number(8,1)存储小数位为1位,总长度为8的浮点数,如果小数位数不足,则用0补全,number(8)存储总长度为8的整数;
(5)int(int(11))相当于number(number(22)),括号里面的数字代表的是存储整数的最大长度;
(6)NUMBER(precision,scale),precision表示数字中的有效位,如果没有指定precision的话,Oracle将使用38 作为精度,
scale表示小数点右边的位数,scale默认设置为0,如果把scale设成负数,Oracle将把该数字取舍到小数点左边的指定位数。
8、ORACLE数据库字段类型说明: