第一章 数据查询
1.1 单表查询
1.1.1 数据准备
找到分享的sql文件,选中文件,右键进行复制,选中以wateruser用户登录的连接,右键粘贴,然后ok确认,就可以将两个sql文件添加到了Datagrip的工程中,打开并运行文件可以发现已经创建好了7张表 通过双击表可以查看各个表中的数据。
1.1.2 简单条件查询
1.1.2.1 精确查询
-- todo 需求 :查询水表编号为 30408 的业主记录
查询语句 :
select * from t_owners where watermeter= '30408'; |
查询结果 :
1.1.2.2 模糊查询
-- todo 需求 :查询业主名称包含“刘”的业主记录
查询语句 :
select * from t_owners where name like '%刘%'; |
%(百分号)匹配零个或多个字符的任何字符串。
_(下划线)匹配任何单个字符。
查询结果 :
1.1.2.3 and 运算符
需求 :查询业主名称包含“刘”的并且门牌号包含 5 的业主记录
查询语句 :
select * from t_owners where name like '%刘%' and housenumber like '%5%'; |
查询结果 :
1.1.2.4 or 运算符
需求 :查询业主名称包含“刘”的或者门牌号包含 5 的业主记录
查询语句 :
select * from t_owners where name like '%刘%' or housenumber like '%5%'; |
查询结果 :
1.1.2.5 and 与 or 运算符混合使用
需求 :查询业主名称包含“刘”的或者门牌号包含 5 的业主记录 ,并且地址编号 为 3 的记录。
语句 :
select * from t_owners |
查询结果 :
因为 and 的优先级比 or 大 ,所以我们需要用 ( ) 来改变优先级。
1.1.2.6 范围查询
需求 :查询台账记录中用水字数大于等于 10000 ,并且小于等于 20000 的记录
我们可以用>= 和<=来实现
--todo 思考一下,下面这种数学上的写法是否可以? |
我们也可以用 between .. and ..来实现
select * from t_account |
1.1.2.7 空值查询
需求 :查询 T_PRICETABLE 表中 MAXNUM 为空的记录
语句 :
select * from t_pricetable t where maxnum is null; |
查询结果 :
需求 :查询 T_PRICETABLE 表中 MAXNUM 不为空的记录
语句 :
select * from t_pricetable t where maxnum is not null; |
查询结果 :
在 Oracle 数据库中,NULL 表示空值或缺失的值。它是数据库中的特殊标记,用来表示数据值未知、不存在或不可用。
与空字符串或零值不同,NULL 表示该字段完全没有任何数据。
1.1.3 去掉重复记录
需求 :查询业主表中的地址 ID,不重复显示
语句 :
select distinct addressid from t_owners; |
select addrid from 表 group by addrid ; |
窗口函数:rownumber()over(partitiion by 分组)过滤 rn = 1 |
1.1.4 排序查询
1.升序排序
需求 :对 T_ACCOUNT 表按使用量进行升序排序
语句 :
select * from t_account order by usenum; |
查询结果 :
2.降序排序
需求 :对 T_ACCOUNT 表按使用量进行降序排序
语句 :
select * from t_account order by usenum desc; |
查询结果 :
1.1.5 基于伪列的查询
在 Oracle 的表的使用过程中 ,实际表中还有一些附加的列,称为伪列。
伪列就 像表中的列一样 ,但是在表中并不存储。伪列只能查询,不能进行增删改操作。
接下来学习两个伪列 :ROWID 和 ROWNUM。
1.1.5.1 ROWID
表中的每一行在数据文件中都有一个物理地址 ,ROWID 伪列返回的就是该行的物理地址。
使用 ROWID 可以快速的定位表中的某一行。
ROWID 值可以唯一的标识表中的一行。
由于 ROWID 返回的是该行的物理地址,因此使用 ROWID 可以显示行是如何存储的。
常见的用法:
唯一标识行:可以用来快速定位和访问数据库中的某一行。
高效查询和更新:利用 ROWID 进行查询和更新操作,通常比使用主键或其他索引列更快,因为 ROWID 直接指向数据文件中的物理位置。
select rowid, t.* from t_area t; |
查询结果如下:
我们可以通过指定 ROWID 来查询记录
select rowID,t.* |
查询结果如下 :
1.1.5.2 ROWNUM
在查询的结果集中 ,ROWNUM 为结果集中每一行标识一个行号 ,第一行返回 1 , 第二行返回 2 ,以此类推。通过 ROWNUM 伪列可以限制查询结果集中返回的行数
常见的用法
限制结果集大小:可以用来限制查询返回的行数,如取前 N 行。
分页查询:在分页查询中,通过 ROWNUM 可以实现简单的分页逻辑。:
select rownum,t.* from t_owners t; -- 查询返回的前 5 行 |
查询结果如下 :
简单总结一下就是:
ROWID:唯一标识数据库中每一行的物理位置,常用于快速访问和更新特定行。
ROWNUM:表示查询结果集中每行的行号,常用于限制查询结果集大小和实现分页查询。
1.1.6 聚合统计
ORACLE 的聚合统计是通过分组函数来实现的 ,与 MYSQL 一致。
1. 聚合函数
( 1 ) 求和 sum
需求 :统计 2012 年所有用户的用水量总和
select sum(usenum) from t_account where year= '2012'; |
查询结果如下 :
( 2 ) 求平均 avg
需求 :统计 2012 年所有用水量 (字数) 的平均值
select avg(usenum) from t_account where year= '2012'; |
查询结果如下 :
( 3 ) 求最大值 max
需求 :统计 2012 年最高用水量 (字数)
select max(usenum) from t_account where year= '2012'; ' |
查询结果如下 :
( 4 ) 求最小值 min
需求 :统计 2012 年最低用水量 (字数)
select min(usenum) from t_account where year= '2012'; |
查询结果如下 :
( 5 ) 统计记录个数 count
需求 :统计业主类型 ID 为 1 的业主数量
select count(*) from t_owners t where ownertypeid=1; |
查询结果如下 :
2. 分组聚合 Group by
需求 :按区域分组统计水费合计数
语句 :
select |
查询结果 :
3. 分组后条件查询 having
需求 :查询水费合计大于 16900 的区域及水费合计
语句 :
select |
查询结果 :
1.2 多表查询
1.2.1 多表内连接查询
需求 :查询显示业主编号 ,业主名称 ,业主类型名称
如下图 :
查询语句 :
select ; --todo 如果内连接不加条件 返回的是笛卡尔积结果 称之为交叉连接 cross join |
( 2 ) 需求 :查询显示业主编号 ,业主名称、地址和业主类型 ,如下图
分析 :此查询需要三表关联查询。分别是业主表 ,业主分类表和地址表
语句 :
select |
select |
( 3 ) 需求 :查询显示业主编号、业主名称、地址、所属区域、业主分类 ,如下图 :
分析 :这里需要四个表关联查询 ,比上边多了一个区域表 ( T_AREA )
查询语句 :
select |
( 4 ) 需求 :查询显示业主编号、业主名称、地址、所属区域、 收费员、业主分 类 ,如下图 :
分析 :此查询比上边又多了一个表 T_OPERATOR
语句 :
select |
1.2.2 左外连接查询
需求 :查询业主的账务记录 ,显示业主编号、名称、年、月、金额。如果此业主 没有账务记录也要列出姓名。
分析 :我们要查询这个结果 ,需要用到 T_OWNERS (业主表) ,T_ACCOUNT (台账表) 按照查询结果 ,业主表为左表、账务表为右表。
按照 SQL1999 标准的语法 ,查询语句如下 :
select |
按照 ORACLE 提供的语法 ,就很简单了 :
select |
如果是左外连接,就在右表所在的条件一端填上 (+)
1.2.3 右外连接查询
需求 :查询业主的账务记录 ,显示业主编号、名称、年、月、金额。
如果账务记录没有对应的业主信息 ,也要列出记录。如下图 :
SQL1999 标准的语句
select |
ORACLE 的语法
select |
1.2.4 全外连接
子查询
1.2.5 where 子句中的子查询
1. 单行子查询
只返回一条记录
单行操作符
需求 :查询 2012 年 1 月用水量大于平均值的台账记录
语句 :
select * from t_account |
查询结果 :
2. 多行子查询
返回了多条记录
多行操作符
in 运算符
( 1 ) 需求 :查询地址编号为 1、3、4 的业主记录
分析 :如果我们用 or 运算符编写 ,SQL 非常繁琐 ,所以我们用 in 来进行查询
语句如下 :
select * from t_owners |
查询结果如下 :
( 2 ) 需求 :查询地址含有“花园”的业主的信息
语句 :
select * from t_owners |
查询结果 :
( 3 ) 需求 :查询地址不含有“花园”的业主的信息
语句 :
select * from t_owners |
查询结果 :
1.2.6 from 子句中的子查询
from 子句的子查询为多行子查询
需求 :查询显示业主编号 ,业主名称 ,业主类型名称 ,条件为业主类型为”居民” , 使用子查询实现。
语句 :
-- todo 1 条件为业主类型为”居民” |
查询结果如下:
1.2.7 select 子句中的子查询
select 子句的子查询必须为单行子查询
需求 :列出业主信息 ,包括 ID ,名称 ,所属地址。
语句 :
select id, name, |
查询结果如下 :
1.3 分页查询
1.3.1 简单分页
需求 :分页查询台账表 T_ACCOUNT ,每页 10 条记录
分析 :我们在 ORACLE 进行分页查询 ,需要用到伪列 ROWNUM 和嵌套查询
我们首先显示前 10 条记录 ,语句如下 :
select rownum, t.* from t_account t where rownum<=10; |
显示结果如下 :
那么我们显示第 11 条到第 20 条的记录呢?编写语句 :
select rownum,t .* from t_account t where rownum>10 and rownum<=20; |
查询结果 :
嗯?怎么没有结果?
这是因为 rownum 是在查询语句扫描每条记录时产生的 ,所以不能使用“大于”符号 ,只能使用“小于”或“小于等于”,只用“等于”也不行。
那怎么办呢?我们可以使用子查询来实现
select * |
查询结果如下 :
1.3.2 基于排序的分页
需求 :分页查询台账表 T_ACCOUNT ,每页 10 条记录 ,按使用字数降序排序。
我们查询第 2 页数据 ,如果基于上边的语句添加排序 ,语句如下 :
select * |
查询结果如下 :
经过验证 ,我们看到第 2 页的结果应该是下列记录
所以推断刚才的语句是错误的 !那为什么是错误的呢 ?
我们可以先单独执行嵌套查询里面的那句话
select rownum as rnum, t.* |
你会看到查询结果如下 :
你会发现排序后的 R 是乱的。这是因为 ROWNUM 伪列的产生是在表记录扫描 是产生的 ,而排序是后进行的 ,排序时 R 已经产生了 ,所以排序后 R 是乱的。
那该如何写呢 ?
很简单 ,我们只要再嵌套一层循环( 一共三层) ,让结果先排序 ,然后对排序后 的结果再产生 R ,这样就不会乱了。
语句如下 :
select * from |
结果如下 :
1.4 单行函数
1.4.1 字符函数
函 数 | 说 明 |
ASCII | 返回对应字符的十进制值 |
CHR | 给出十进制返回字符 |
CONCAT | 拼接两个字符串 ,与 || 相同 |
INITCAT | 将字符串的第一个字母变为大写 |
INSTR | 找出某个字符串的位置 |
INSTRB | 找出某个字符串的位置和字节数 |
LENGTH | 以字符给出字符串的长度 |
LENGTHB | 以字节给出字符串的长度 |
LOWER | 将字符串转换成小写 |
LPAD | 使用指定的字符在字符的左边填充 |
LTRIM | 在左边裁剪掉指定的字符 |
RPAD | 使用指定的字符在字符的右边填充 |
RTRIM | 在右边裁剪掉指定的字符 |
REPLACE | 执行字符串搜索和替换 |
SUBSTR | 取字符串的子串 |
SUBSTRB | 取字符串的子串 (以字节) |
SOUNDEX | 返回一个同音字符串 |
TRANSLATE | 执行字符串搜索和替换 |
TRIM | 裁剪掉前面或后面的字符串 |
UPPER | 将字符串变为大写 |
常用字符函数讲解 :
( 1 ) 查询字符串中的字符个数 LENGTH
语句 :
select length('ABCD') from dual; |
显示结果为 :
( 2 ) 求字符串的子串 SUBSTR
语句 :
select substr('hello,oracle!',2,2) from dual; |
显示结果为 :
( 3 ) 字符串拼接 CONCAT
语句 :
select concat('ABC', 'D') from dual; |
查询结果如下 :
我们也可以用 || 对字符串进行拼接
select 'ABC' || 'D' from dual; |
查询结果同上。
1.4.2 数值函数
函数 | 说明 |
ABS(value) | 绝对值 |
CEIL(value) | 大于或等于 value 的最小整数 |
COS(value) | 余弦 |
COSH(value) | 反余弦 |
EXP(value) | e 的 value 次幂 |
FLOOR(value) | 小于或等于 value 的最大整数 |
LN(value) | value 的自然对数 |
LOG(value) | value 的以 10 为底的对数 |
MOD(value,divisor) | 求模 |
POWER(value,exponent) | value 的 exponent 次幂 |
ROUND(value,precision) | 按 precision 精度 4 舍 5 入 |
SIGN(value) | value 为正返回 1;为负返回-1;为 0 返回 0. |
SIN(value) | 余弦 |
SINH(value) | 反余弦 |
SQRT(value) | value 的平方根 |
TAN(value) | 正切 |
TANH(value) | 反正切 |
TRUNC(value,按 precision) | 按照 precision 截取 value |
VSIZE(value) | 返回 value 在 ORACLE 的存储空间大小 |
常用数值函数讲解 :
( 1 ) 四舍五入函数 ROUND
语句 :
select round(100.567) from dual; |
查询结果如下 :
( 2 ) 截取函数 TRUNC
语句 :
select trunc(100.567) from dual; |
查询结果 :
( 3 ) 取模 MOD
语句 :
select mod(10, 3) from dual; |
结果 :
(三) 日期函数
函 数 | 描 述 |
ADD_MONTHS | 在日期 date 上增加 count 个月 |
GREATEST(date1,date2,. . .) | 从日期列表中选出最晚的日期 |
LAST_DAY( date ) | 返回日期 date 所在月的最后一天 |
LEAST( date1, date2, . . .) | 从日期列表中选出最早的日期 |
MONTHS_BETWEEN(date2, date1) | 给出 Date2 - date1 的月数(可以是小数) |
NEXT_DAY( date,’day’) | 给出日期 date 之后下一天的日期 ,这里的 day 为星期 , 如 :MONDAY,Tuesday 等。 |
NEW_TIME(date, ’this’,’other’) | 给出在 this 时区=Other 时区的日期和时间 |
ROUND(date,’format’) | 未指定 format 时 ,如果日期中的时间在中午之前 ,则 将日期中的时间截断为 12 A.M.(午夜 ,一天的开始),否 则进到第二天。 时间截断为 12 A.M.(午夜 ,一天的开始), 否则进到第二天。 |
TRUNC(date,’format’) | 未指定 format 时 ,将日期截为 12 A.M.( 午夜 ,一天的开始). |
我们用 sysdate 这个系统变量来获取当前日期和时间
语句如下 :
select sysdate from dual; |
查询结果如下 :
常用日期函数讲解 :
( 1 ) 加月函数 ADD_MONTHS:在当前日期基础上加指定的月
语句 :
select add_months(sysdate, 2) from dual; |
查询结果如下 :
( 2 )求所在月最后一天 LAST_DAY
语句 :
select last_day(sysdate) from dual; |
查询结果如下 :
( 3 ) 日期截取 TRUNC
语句 :
select trunc(sysdate) from dual |
查询结果如下 :
语句 :
select trunc(sysdate, 'yyyy') from dual |
查询结果如下 :
语句 :
select trunc(sysdate, 'mm') from dual |
查询结果如下 :
1.4.3 转换函数
函 数 | 描 述 |
CHARTOROWID | 将 字符转换到 rowid 类型 |
CONVERT | 转换一个字符节到另外一个字符节 |
HEXTORAW | 转换十六进制到 raw 类型 |
RAWTOHEX | 转换 raw 到十六进制 |
ROWIDTOCHAR | 转换 ROWID 到字符 |
TO_CHAR | 转换日期格式到字符串 |
TO_DATE | 按照指定的格式将字符串转换到日期型 |
TO_MULTIBYTE | 把单字节字符转换到多字节 |
TO_NUMBER | 将数字字串转换到数字 |
TO_SINGLE_BYTE | 转换多字节到单字节 |
常用转换函数讲解 :
( 1 ) 数字转字符串 TO_CHAR
语句 :
select to_char(1024) from dual |
查询结果 :
( 2 ) 日期转字符串 TO_CHAR
语句 :
select to_char(sysdate, 'yyyy-mm-dd') from dual |
查询结果 :
语句 :
select to_char(sysdate, 'yyyy-mm-dd hh:mi:ss') from dual |
查询结果 :
语句 :
select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual |
查询结果 :
( 3 )字符串转日期 TO_DATE
语句 :
select to_date('2017-01-01', 'yyyy-mm-dd') from dual |
查询结果如下 :
( 4 ) 字符串转数字 TO_NUMBER
语句 :
select to_number('100'), '100' from dual |
查询结果如下 :
1.4.4 其它函数
( 1 ) 空值处理函数 NVL
用法 :
NVL (检测的值 ,如果为 null 的值) ;
语句 :
select NVL(NULL, 0) from dual |
查询结果如下 :
需求 :
显示价格表中业主类型 ID 为 1 的价格记录 ,如果上限值为 NULL,则显示 9999999
语句 :
select price, minnum, nvl(maxnum,9999999) from t_pricetable where ownertypeid=1 |
查询结果 :
( 2 ) 空值处理函数 NVL2
用法 :
NVL2 (检测的值,如果不为 null 的值,如果为 null 的值) ;
需求 :显示价格表中业主类型 ID 为 1 的价格记录 ,如果上限值为 NULL,显示“不限“ .
语句 :
select price, minnum, nvl2(maxnum, to_char(maxnum), '不 限') |
( 3 ) 条件取值 decode
语法 :
decode(条件,值 1,翻译值 1,值 2,翻译值 2,...值 n,翻译值 n,缺省值) 【功能】根据条件返回相应值 |
需求 :显示下列信息 (不要关联查询业主类型表 ,直接判断 1 2 3 的值)
语句 :
select name, |
上边的语句也可以用 case when then 语句来实现
select name, |
还有另外一种写法 :
select name, |
1.5 行列转换
需求 :按月份统计 2012 年各个地区的水费 ,如下图
代码如下:
select |
需求 :按季度统计 2012 年各个地区的水费 ,如下图
语句如下 :
select |
1.6 集合运算
1.6.1 什么是集合运算
集合运算,集合运算就是将两个或者多个结果集组合成为一个结果集。集合运算 包括 :
- UNION ALL(并集) ,返回各个查询的所有记录 ,包括重复记录。
- UNION(并集) ,返回各个查询的所有记录 ,不包括重复记录。
- INTERSECT(交集) ,返回两个查询共有的记录。
- MINUS(差集) ,返回第一个查询检索出的记录减去第二个查询检索出的记录之 后剩余的记录。
1.6.2 并集运算
UNION ALL 不去掉重复记录
-- union |
1.6.3 交集运算
select * from t_owners where id<=7 |
结果如下:
1.6.4 差集运算
select * from t_owners where id<=7 |
结果如下: