一、数据库是如何解决数据存储问题的
1
从三个方面来学习数据库
a) 数据库是如何存储的
i.
字段,记录,表,约束 ( 主键,外键,唯一键,非空, check, default, 触发器 )
b) 数据库是如何操作数据的
i.
Insert, update, delete, T-SQL, 存储过程,函数, 触发器
c) 数据库是如何显示数据的
i.
select( 重点中的重点 )
必备的一些操作
d) 如何建数据库
i.
.mdf 是数据文件
.LDF 是日志文件
ii.
右键 -> 新建数据库 -> 输入名字 -> 确定
e) 如何删除一个库;
如何附加和分离数据库
f) 设置登录用户名和密码;
建用户
用鼠标建立的第一个数据库 Test
g) nvarchar()
国际化编码的可变的字符串。 n 表示国际化,对汉字也支持; var 是
变量,字符的长度是可变化的
h)
,其中的 dbo 表示一个系统的身份
i) update emp set emp_id = 1001
j)
存在外键的表叫外键表
k) 当要选择是否可以为空时,用空格键来进行控制,即: 可以用空格来选择
l) PK , primary 主要的;
FK, foreign 外部的
通过 sql 命令建表和主外键约束
m) 在 sql server 里最后一句写不写逗号都可以,但是在 oracle 里不可以写逗号,因此
在写命令时,最后一个字段的后面就不要写逗号了
n) create table dept
(
dept_id int primary key,
-- 在此处设置了主键
dept_name nvarchar(100) not null ,
-- 设置了非空
dept_address nvarchar(100)
)
create table emp
(
-- 这个是注释
emp_id int constraint PK_emp_id_haah primary key, -- 此处给主键起了
-- 一个名字,红字部分可以不写
emp_name nvarchar(20) not null,
emp_sex nchar(1),
dept_id int constraint FK_dept_id_hd foreign key references dept(dept_id) 2
-- 在此处建立了一个外键,使用了 references
)
约束
o) 约束的分类
i.
主键约束
不允许重复元素,避免了数据的冗余(实体完整性)
ii.
外键约束
通过外键约束从语法上保证了本事物所关联的其他事物一定是
存在的(引用完整性)
事物和事物之间的关系是通过外键来体现的
p) check 约束
i.
create table student
{
stu_id int primary key,
stu_sal int check (stu_sal >= 1000 and stu_sal <= 8000)
)
在此处使用了 check 约束,限定了 stu_sal 的值在 1000 与 8000 之间
ii.
保证事物属性的取值在合法的范围之内
q) default 约束
i.
数据库中的 单引号用来标识一个字符串,双引号用来模拟一个数据的名字
ii.
保证事物的属性一定有一个值
iii.
create table student2
(
stu_id int primary key,
stu_sal int check (stu_sal >= 1000 and stu_sal <= 8000),
stu_sex nchar(1) default(' 男 ') --() 是可以省略的,在数据库中字符串是
-- 必须用 ' ' 括起来
)
insert into student2(stu_id, stu_sal) values (1, 1000)
insert into student2 values (2, 5000, ' 女 ')
--insert into student2 values (3, 10000) 这样写是不对的
在此处,声明了三个属性,在第一个命令中,指定了二个属性,所以填入了两
个值;第二个命令中,没有指定要填写几个属性,所以要填写全部的三个值;
第三个命令中,没有指定要填写几个属性,不过只写了两个值,这是不对的
r) unique 约束(唯一约束)
i.
保证了事物的取值不允许重复,
ii.
create table student5
(
stu_id int primary key,
stu_sal int check (stu_sal >= 1000 and stu_sal <= 8000),
stu_sex nchar(1) default' 男 ',
stu_name nvarchar(200) unique
)
insert into student5 values (1, 6000, ' 男 ', ' 张三 ' ) -- 此句话正确
insert into student5 values (2, 6000, ' 男 ', ' 张三 ' ) -- 违反了唯一键约束,不正确
insert into student5 values (null, 6000, ' 男 ', ' 王五 ')
-- 主键不能为空,不正确 insert into student5 values (3, 6000, '
3
男 ', null )
-- 唯一键可以为空
iii.
unique 键是否允许多列为空?
- Sql Server 2012 只允许一个 unique 列为空
- Oracle11G 允许多个 unique 列为空
s) 主键和唯一键区别及其两者配合使用举例
i.
不要用业务逻辑作主键(编号不是一个事物的属性)
ii.
把编号(代理主键)当主键,
iii.
create table student6
(
stu_id int primary key identity ,
stu_name nvarchar(50) not null unique,
stu_email nvarchar(50) not null,
stu_address nvarchar(50)
)
drop table student6
-- 这一句是删除一个表
在此处, identity 是自增的意思;设定了一个编号当作主键,把用户名作
为唯一键;通常不会删除一个用户,一般每个用户都有一个标识位, 1 或者 0 ,
如果注消一个用户的话可以把他的标识位改一个,就不能使用此帐户了
t) not null (可以当作一个约束)和 default 的区别
i.
not null 要求用户必须为该属性赋一个值,否则语法出错
ii.
如果有默认值,如果不指定要为哪一个属性赋值的话,必须为全部属性赋值
iii.
如果指定了为哪些属性赋值,有一个属性没在括号内且可以为空的话,这个值
就是 NULL ;如果指定了为哪些属性赋值,有一个属性没在括号内且有默认值,
那么这个值就是默认值
表和约束的区别(他自己的理解)
u) 数据库是通过表来解决事物的存储问题的
v) 数据库是通过约束来解决事物取值的有效性和合法性的问题
w) 建表的过程就是指定事物属性及其事物属性各种约束的过程
什么是关系
x) 定义:表和表之间的联系
y) 实现方式:通过设置不同形式的外键来体现表和表的不同关系
z) 分类:一对一,一对多,多对多
aa) 一对一:
i.
可以把表 A 的主键充当表 B 的外键,也可以把表 B 的主键当充当表 A 的主键
bb) 一对多:
i.
在多的一方添加外键
cc) 多对多:
i.
任何一个表都有一个主键,但不一定都有外键
ii.
多对多的关系需要另外一张表来表示(通常至少有两列)
iii.
-- 班级表
create table banji
( 4
banji_id int primary key,
banji_num int not null,
banji_name nvarchar(100)
)
-- 教师表
create table jiaoshi
(
jiaoshi_id int primary key,
jiaoshi_name nvarchar(200)
)
-- 第三张表 用来模拟班级和教师的关系
create table banji_jiaoshi_mapping
(
banji_id int constraint fk_banji_id foreign key references banji(banji_id) ,
jiaoshi_id int foreign key references jiaoshi(jiaoshi_id) ,
kecheng nvarchar(20),
constraint pk_banji_id_jiao_id primary key(banji_id, jiaoshi_id, kecheng)
)
-- 删除表
drop table banji_jiaoshi_mapping
在第三张表里面, constraint fk_banji_id 为 banji_id 这个外键设置了一个别
名; foreign key references banji(banji_id) 这句话,设置了一个外键,其中"一"
的一方是 banji 里的 banji_id ;下面一条命令同样设置了一外键,不过是省略了
外 键 的 名 字 ; 最 后 一 条 命 令 设 置 了 一 个 联 合 主 键 , 设 其 名 字 为
pk_banji_id_jiao_id ,只能用这种方法来设置联合主键,不能一个一个地单独写
主键;在第三张表里面,删除了这个关系表
dd) 数据库关系图的使用
i.
在一个库里右击数据库关系图,新建数据库关系图,选中要选择的表
ii.
在删除一个关系时,不会把属性同时删除
iii.
要放大和缩小关系图, Ctrl+ 鼠标滚轮
iv.
利用
可以快速改变视图的位置
主键的具体定义及注意事项
ee) 主键定义:能够唯一标识一个事物的一个字段或者多个字段的组合
ff) 主键特征:
i.
含有主键的表叫做主键表
ii.
主键通常都是整数,不建议使用字符串当主键(如果主键是用于集群式服务,
才可以考虑用字符串当主键)
iii.
主键的值通常都不允许修改,除非本记录被删除
iv.
主键不要定义成 id ,而要定义成"表名 id "或者"表名 _id "
v.
要用代理主键,不要用业务主键 - 任何一张表,强烈建议不要使用有业务含义的字段充当主键
- 我们通常都是在表中单独添加一个整型的编号充当主键字段 5
外键的具体定义及要注意的问题
定义:如果一个表中的若干个字段是来自另外若干个表的主键或唯一键,则这若干
个字段就是外键
注意:
vi.
外键通常是来自另外表的主键而不是唯一键,因为唯一键可能为 null
vii.
外键不一定是来自另外的表,也可能来自本表的主键
viii.
含有外键的表叫外键表,外键字段来自的那一张表叫做主键表
gg) 问题:先删主键表还是外键表?
先删外键表
二、查询(顺序)
计算列(scott 库)
select * from emp;
select empno, ename from emp;
select ename, sal*12 as "年薪" from emp;
这里所写的 sal*12 是指把一个月的工资乘以 12 之后的结果; as 在这里写不写
都可以;年薪这里要写的是 双引号 ,不要写其他的符号,值是 5
select 888 from emp;
这里写 888 ,查询出来的没有列名,输出的行数是 emp 表的行,每行只有一
个字段
select 5;
这里写的 5 ,查询出来的是没有列名,只有一个记录(不推荐这样写)
在 Oracle 里不能通过
注意:在 Oracle 中字段的别名不允许用单引号括起来,但是 SqlServer2012 却允许,
因此为了兼容性,最好 将字段的别名用双引号括起来
distinct(不允许重复的)的用法
select distinct deptno from emp; 这里, distinct deptno 会过滤掉重复的 deptno
select distinct comm from emp; distinct 也可以过滤掉重复的 null ,或者说如果有多
个 null ,只输出一个
select distinct comm, deptno from emp;
把 comm 和 deptnor 的组合进行过滤
select deptno, distinct comm from emp;
逻辑上有冲突
select ename, * from emp; 这个在 SqlServer 里正确,在 Oracle 里不正确
between(在某个范围)的用法
select * from emp;
在这里,先执行 from emp ,知道要在哪个表里查找,然后
执行 select * ,知道要找哪些数据
查找工资在 1500 和 3000 之间(包括 1500 和 3000 )的所有的员工的信息
select * from emp where sal >= 1500 and sal <= 3000 ;
等价于:
select * from emp where sal between 1500 and 3000 ;
6
查找工资小于 1500 或大于 3000 的所有的员工的信息
select * from emp where sal < 1500 or sal > 3000;
等价于:
select * from emp where sal not between 1500 and 3000
in(属于若干个孤立的值)
select * from emp where sal in (1500, 3000) ;
在 emp 表中取出 sal 为 1500 和 3000
的所有记录,等价于:
select * from emp where sal = 1500 or sal = 3000 ;
select * from emp where sal not in (1500, 3000) ; 在 emp 表中取出 sal 既不是 1500 也
不是 3000 的记录,等价于:
select * from emp where sal <> 1500 and sal <> 3000 ;
在数据库中不等于有两种表示方式: != <> 推荐使用第二种
对或取反是并且,对并且取反是或
top(最前面的若干个记录,专属于 SqlServer 的语法,不可移
植到其他数据库中)
select top 2 * from emp;
选出 emp 表中的前两个记录
select top 15 percent * from emp;
输出 emp 表中的前 15% 个,如果不是整数的
话,就进位。如, 14 * 15% = 2.1 ,则输出 3 个值
select top 2 from emp;
这里没有 * 会出错
把工资在 1500 和 3000 之间(包括 1500 和 3000 )的员工中工资最高的 4 个人的
信息输出
select top 5 * from emp where sal between 1500 and 3000 order by sal desc ;
其中 desc 是降序的意思,默认情况下是升序
null(没有值 空值)
零和 null 是不一样的, null 表示空值,没有值;零表示一个确定的值
null 不能参与如下运算: <>
!= =
null 可以参与如下运算: is
is not
例如:
select * from emp where comm <> null;
-- 错误
select * from emp where comm != null;
-- 错误
select * from emp where comm = null;
-- 错误
select * from emp where comm is null ;
-- 输出奖金为空的员工的信息
select * from emp where comm is not null ; -- 输出奖金不为空的员工的信息
任何类型的数据都允许为 null , 如:
create table t1(name nvarchar(20), cnt int , riqi datetime),
insert into t1 values (null, null, null);
任何数字与 null 参与数学运算的结果永远是 null ,例如:
-- 输出第个员工的姓名年薪(包含了资金), comm 假设是一年的资金
select empno, ename, sal * 12 +comm " 年薪 " from emp;
如果有 null 参与运算的话,结果全部为 null
正确的写法是:
7
select ename, sal * 12 + isnull(comm, 0) " 年薪 " from emp ;
isnull(comm, 0) 表示:如果 comm 是 null ,就返回零否则返回 comm 的值
order by 的用法(以某个字段排序)
order by a, b;
a 和 b 都是升序
order by a, b desc; a 升序, b 降序
order by a desc, b; a 降序, b 升序
order by a desc, b desc;
a 降序, b 降序
文字描述:
如果不指定排序的标准,则默认是升序, 升序用 asc 表示 ,默认可以省略不写,
降序用 desc 表示
为一个字段指定的排序标准并不会对另一个字段产生影响
强烈建议为第一个字段都指定排序的标准
例子:
select * from emp order by sal; 如果不加什么排序标准,默认是按照升序排列
select * from emp order by deptno, sal ;
先 按 照 deptno 排 序 , 如 果
deptno 相同,则再按照 sal 来排序,默认都是升序
select * from emp order by deptno desc, sal ;
这里 deptno 里面写了 desc ,则
deptno 按照降序来排列;如果 deptno 相同,后面的 sal 没有写排序类型,则
sal 按照升序来排序( Oracle 也是这样)
select * from emp order by deptno, sal desc;
与上相同,先按 deptno 升序,
如果 deptno 相同,则再按 sal 的降序来排列;不会对 deptno 产生影响
模糊查询[搜索时经常使用]
格式: select 字段的集合 from 表名 where 某个字段的名字 like 匹配条件
匹配的条件通常含有 通配符
% (百分号)表示任意 0 个 或 多个 字符
select * from emp where ename like '%A%' ; --ename 只要含有 A 就输出
select * from emp where ename like 'A%'' ; --ename 只要首字母为 A 就输出
_ (下划线)表示任意 单个字符
select * from emp where ename like 'A%'; --ename 第二个字母是 A 就输出
[a-f] a 到 f 中的任意 单个字符 ,只能是 a, b, c, d, e, f 中的任意一个字符
select * from emp where ename like '[A-F]%';-- 把 ename 中第二个字母是
A, B, C, D, E, F 中的任意一个字符的记录输出
[a, f] a 或 f
[^a-c] 不是 a ,也不是 b ,也不是 c 的记录
select * from emp where ename like '[^A-C]%';-- 把 ename 中第二个字母不
是 A ,不是 B ,也不是 C 的记录输出
注意:匹配的条件必须用单引号括起来,不能省略,也不能改用双引号
单引号:字符串
双引号:对象的名字 ( 自己的理解 )
模糊查询中通配符作为普通字符使用的问题
select * from student where name like '%\%' escape '\'
这表示把所有记录中 name 列包含下划线 '_' 字符的记录输出
escape '\' 表示把 '\' 字符当做转义字符的标志
8
在 sql 中可以把任意的字符当做转义字符的标志,具体是把哪个字符当做转义
字符,这是由 escape ' 要制定为转义字符的字符 ' 来决定的
聚合函数[多行记录返回一个值,通常用于统计分组的信息]
函数的分类:
单行函数, 每一行返回一个值
多行函数, 多行返回记录一个值 ;聚合函数是多行函数
例子: select lower(ename) from emp;
-- 最终返回的是 14 行 lower() 是单行
函数( lower() 是将大写的转换成小写的; upper() 将小写的转换成大写的)
select max(sal) from emp;
返回行 max() 是多行函数
聚合函数的分类: (当聚合函数作用某个字段时,会忽略所有的 NULL 行)
常用的聚合函数:
AVG(ALL|DISTINCT expression) 数字表达式中所有值的平均值
SUM(ALL|DISTINCT expression) 表达式中所有值的平均值
COUNT(ALL|DISTINCT expression) 表达式中值的个数
COUNT(*)
选定的行数
MAX(expression)
表达式中的最高值
MIN(expression)
表达式中的最低值
max()
适用于数值型,字符型和日期型的列,对于列值为 NULL 的列, MAX
不将其列为对比的对象
min()
avg() (平均值)
count() (求个数)
count(*) -- 返回 emp 表所有记录的个数
a) select count(*) from emp;
-- 返回 emp 表所有记录的个数
count ( 字段名 )
-- 返回字段值非空的记录的个数,重复的记录也会被当
做有效的记录
a) select count(deptno) from emp; -- 返回值是 14 ,说明 deptno 重复的
记录也被当做有效值
b) select count(comm) from emp;
-- 返回值是 4 ,说明 comm 为 null 的
记录不会被当做有效的记录
count (distinct 字段名 )
-- 返回字段不重复并且非空的记录的个数
a) select count(distinct deptno) from emp;
-- 返回值是 3 ,
统计 deptno
中不重复的个数
b) select count (distinct comm) from emp;
-- 返回值是 4 ,这说明 comm
为 null 的记录不会被当做有效的记录
注意的问题:
select max(sal) " 最高工资 ", min(sal) " 最低工资 ", count(*) " 员工人数 " from emp;
-- 这样写没有冲突
select max(sal), lower(sal) from emp; -- 错误,单行函数和多行函数不能混用
grout by 用法[ 分组统计查询 ][重难点]
从一个数据库切换到另一个数据库,可以使用 use scott;
格式: group by 字段的集合
功能:把表中的记录按照字段分成不同的组
9
例子:
查询不同部门的平均工资
select deptno, avg(sal) as " 部门平均工资 "
from emp
group by deptno
理解: group by a, b, c 的用法
先按 a 分组, 如果 a 相同,再按 b 分组;如果 b 相同, 再按 c 分组;最终统
计的是最小分组的信息
下面几行语句是错误的:
select deptno, avg(sal) as" 部门平均工资 ", ename
from emp
group by deptno
在这里按照部门 deptno 来分组, 只能写组的信息,不能写组内部的信息。这
样写部门分组只有三组,名字却有 14 个,所以不行
select deptno, ename
from emp
group by deptno
select deptno, job, sal
from emp
group by deptno, job
使用了 group by 之后 select 中 只能出现分组后每组的整体信息,不能出现组内
的详细信息
使用 group by 后 select 后面的字段只能写可以描述每条记录属性的字段
use scott ;
select *
from emp
-- 输出每个部门的编号和该部门的平均工资
select deptno as " 部门编号 " , avg ( sal ) as " 部门平均工资 "
from emp
group by deptno -- 按部门编号分组 , 统计分组每组整体信息
select deptno , ename --error 不能写组内部信息 ,ename 不能写
from emp
group by deptno
select deptno" 部门编号 " , job" 职称 "
from emp
group by deptno , job -- 先按部门编号分组,部门编号相同,按职称分组
select deptno" 部门号 " , job" 职称 " , avg ( sal ) " 同一部门相同职位的平均工资 "
from emp
group by deptno , job --
10
对上面进行排序输出
select deptno" 部门编号 " , job" 职称 " , avg ( sal ) " 同一部门相同职称的平均工资 "
from emp
group by deptno , job
order by deptno desc , avg ( sal )
--select 后面的字段只能写可以描述每条记录属性的字段
select deptno" 部门编号 " , job" 职称 " , count (*) " 同一部门相同职称员工数 "
from emp
group by deptno , job
order by deptno desc , avg ( sal )
select deptno , avg ( sal ), count (*) --count(*) 为相同组的员工人数
from emp
group by deptno
order by deptno
谨记:使用 group by 后 select 后面的字段只能写可以描述每条记
录属性的字段
出现在 SELECT 列表中的字段,如果不是包含在分组函数中,那么该字段必须同时
在 GROUP BY 子句中出现。但是在 GROUP BY 子句中的字段则不必须出现在 SELECT
列表中 ,例如:
select avg(sal)
from emp
group by deptno;
--OK
select deptno, avg(sal)
from emp
group by deptno; --OK
select ename, avg(sal)
from emp
group by deptno --error 一定要明白该句为什么是错误的
having( 用来对分组之后的信息进行过滤 )
having 子句是用来对分组之后的数据进行过滤,
因此使用 having 时通常都会先使用 group by
如果没有使用 group by 但使用了 having ,则意味着把所有的记录当做一组来进行
过滤。一般很少这样用 select count(*) from emp having avg(sal) > 1000;
having 子句出现的字段必须得是分组之后的 组的整体信息, having 不允许出现 组
内的详细的信息
尽管 select 字段中可以出现别名,但是 having 子句中不能出现字段的别名 ,只能
使用字段最原始的名字
having 和 where 的异同
相同点:
都是对数据过滤,只保留有效的数据
11
where 和 having 一样, 都不允许出现字段的别名 ,只允许出现最原始的字
段名字,在 SqlServer2012 和 Oracle 中都成立
不同点:
where 是对原始的记录过滤, having 是对分组之后的记录过滤
where 必须得写在 having 的前面,顺序不可颠倒,否则运行出错
where 是对 原始的数据 过滤 ,不能使用聚合函数, 因为还没有分组
例子:
select deptno, avg(sal)
from emp
where sal > 2000
group by deptno
having avg(sal) > 3000; (正确)
select deptno, avg(sal)
from emp
group by deptno
having avg(sal) > 3000;
where sal > 2000; (不正确)
-- having-- 对分组后进行过滤
use scott ;
select * from emp ;
-- 输出部门平均工资大于的部门的编号 , 以及该部门的平均工资
select deptno , avg ( sal )
from emp
group by deptno
having avg ( sal )> 2000 ; -- 对分组后的记录过滤
select deptno" 编号大于的部门 " , avg ( sal ) " 该部门的平均工资 "
from emp
group by deptno
having deptno > 10 ; -- 对分组后的记录过滤
select deptno , ename
from emp
group by deptno , ename
order by deptno
select deptno , avg ( sal ), count (*) --count(*) 为相同组的员工人数
from emp
group by deptno 12
order by deptno
select deptno , avg ( sal )
from emp
group by deptno
having count (*)>= 5 -- 对分组后的记录过滤
--having 与 where 的区别
-- 把姓名不包含 A 的所有员工按部门编号分组
-- 统计输出平均工资大于的部门的编号以及该部门的平均工资
select deptno" 部门编号 " , ename" 名字不含 A 的员工 "
from emp
where ename not like '%A%' -- 对原始记录过滤 -- 排除部门中名字中含 A 的员工
group by deptno , ename
select deptno" 部门编号 " , ename" 名字不含 A 的员工 " , sal
from emp
where ename not like '%A%' -- 对原始记录过滤 -- 排除部门中名字中含 A 的员工
group by deptno , ename , sal
having sal > 1500 -- 对分组后的记录过滤
select deptno" 部门编号 " , count (*) " 部门人数 " , avg ( sal ) " 部门平均工资 " ,
min ( sal ) " 最少工资 " , max ( sal ) " 最多工资 "
from emp
where ename not like '%A%' -- 排除部门中名字中含 A 的员工
group by deptno
select deptno" 部门编号 " , count (*) " 部门人数 " , avg ( sal ) " 部门平均工资 " ,
min ( sal ) " 最少工资 " , max ( sal ) " 最多工资 "
from emp
where ename not like '%A%' -- 排除部门中名字中含 A 的员工
group by deptno
having avg ( sal )> 2000 -- 对分组后的记录过滤
-------------------------------------------- 13
总结:
select list
into new_table_name -- 把查询结果放入新表
from tablename
where search conditions
group by list
having search conditions
order by list
union 运算符可以合并多个查询结果
连接查询( 多表查询 )
定义: 将两个或者两个以上的表以一定的连接条件连接起来,
从中检索出满足条件的数据
分类: 内连接,外连接,完全连接,交叉连接,自连接,联合
内连接(重点,但不难)
select ...from A, B 的用法
产生的结果:行数是 A 和 B 的乘积,列数是 A 和 B 之和;或者说:把 A
(或 B )表的每一条记录都和 B (或 A )表的每一条记录组合在一起,形
成的是个 笛卡尔积
注意: select * from A, B 输出结果和 select * from B, A 的结果是一样的
例子: 14
--emp 14 行列 dept 5 行列
select * from emp , dept -- 输出行列 70=14×5 11=8+3
select ...from A, B where ... 的用法
对 select ... from A, B 产生的笛卡尔积用 where 中的条件进行过滤 ( A 和 B
互换输出结果一样)
例子:
select *
from emp , dept
where empno = 7369 -- 输出行列 5=1×11=8+3
这条语句可以理解成:将 emp 中的 empno 为 7369 的记录分别与 dept
中的每一条记录相连,其中 dept 表中只有 5 行,所以结果只有 5 行记录
select ...from A join B on ... 的用法 ( 有 join, 必须有 on)
A 和 B 互换输出结果不变
on 中是连接条件
select *
from emp "E" -- 为 emp 表起个别名 E
join dept "D" -- 把 emp 表和 dept 表连接起来
on 1 = 1 --on 表示连接条件"真"
输出行列 -- 因为连接条件永远成立就和没有一样了
select "E" . ename " 员工姓名 " , "D" . dname " 部门姓名 "
from emp "E"
join dept "D"
on 1 = 1 ; -- 输出行列
select deptno -- 俩张表中都有 deptno 就 error
from emp "E"
join dept "D"
on 1 = 1 ;
select "E" . deptno --ok
from emp "E"
join dept "D"
on 1 = 1 ; -- 输出 70 行 1 列
15
这样俩张表中相同部门就对着连接起来了
select *
from emp "E"
推荐使用
join dept "D"
on "E" . deptno = "D" . deptno -- 输出 14 行 11 列
-- 与上面输出结果一样
select *
from emp , dept
where emp . deptno = dept . deptno -- 输出行列
select ...from A, B where ... 与 select ...from A join B on ... 的比较
-- 把工资大于的员工的姓名和部门的编号 , 部门名称输出
select "E" . ename , "D" . deptno , "D" . dname , "E" . sal
from emp "E" , dept "D"
where "E" . sal > 2000 and "E" . deptno = "D" . deptno
select "E" . ename , "E" . deptno , "D" . dname , "E" . sal
from emp "E"
join dept "D"
on "E" . deptno = "D" . deptno
where "E" . sal > 2000
-- 把工资大于的员工的姓名 , 部门的名称和工资的等级输出
-- 涉及 3 个表 emp dept SALGRADE
select *
from emp "E"
join dept "D"
on "E" . deptno = "D" . deptno --emp 和 dept 成一张临时表了
join SALGRADE "G" -- 与第三张表连接
on "E" . sal >= "G" . LOSAL and "E" . sal <= "G" . HISAL -- 三表
成一表
select "E" . ename , "D" . deptno , "E" . sal , "G" . GRADE
from emp "E"
join dept "D"
on "E" . deptno = "D" . deptno
join SALGRADE "G"
on "E" . sal >= "G" . LOSAL and "E" . sal <= "G" . HISAL
where "E" . sal > 2000
select *
from emp "E" , dept "D" , SALGRADE "G" -- 迪卡尔积 16
where "E" . deptno = "D" . deptno and
( "E" . sal >= "G" . LOSAL and "E" . sal <= "G" . HISAL ) and
"E" . sal > 2000
-
- 输出姓名不包含 A 的工资前三名的员工的姓名工资工资等级部门名称
select top 3 "E" . ename , "E" . sal , "G" . GRADE , "D" . dname
from emp "E"
join dept "D"
on "E" . deptno = "D" . deptno
join SALGRADE "G"
on "E" . sal between "G" . LOSAL and "G" . HISAL
where "E" . ename not like '%A%' -- 只能写这
order by "E" . sal desc
select 、 from 、 where 、 join 、 on 、 group by 、 order by 、 top 、
having 的混合使用 (having 里面只能有原始字段的名字 )
查询的顺序:
select top ...
from A
join B
on ...
join C
on ...
where ...
group by ...
having ...
order by...
例子: -- 把工资大于 1500 的所有的员工按部门分组 把部门平均工资大于
2000 的最高前 2 个的部门的编号 部门的名称 部门平均工资的等级
- 输出姓名不包含 A 的工资前三名的员工的姓名工资工资等级部门名称
select top 2 "E" . deptno , avg ( sal ) "avg_sal"
from emp "E"
join dept "D"
on "E" . deptno = "D" . deptno
join salgrade "S"
on "E" . sal between "S" . losal and "S" . hisal
where "E" . sal > 1500
group by "E" . deptno
having avg ( "E" . sal ) > 2000
order by avg ( "E" . sal ) desc
Select "D" . deptno , "D" . dname , "S" . grade" 部平工资等级 "
from dept"D"
join(
select top 2 "E" . deptno , avg ( sal ) "avg_sal" 17
from emp "E"
join dept "D"
on "E" . deptno = "D" . deptno
join salgrade "S"
on "E" . sal between "S" . losal and
"S" . hisal
where "E" . sal > 1500
group by "E" . deptno
having avg ( "E" . sal ) > 2000
order by avg ( "E" . sal ) desc
) "T"
on "D" . deptno = "T" . deptno
join salgrade "S"
on "T" . avg_sal between "S" . losal and "S" . hisal
select "T" .*, "D" . dname , "S" . grade
from dept "D"
inner join (
select top 2 deptno , avg ( sal ) as
"avg_sal"
from emp
where sal > 1500
group by deptno
having avg ( sal ) > 2000
order by "avg_sal" desc
) "T"
on "D" . deptno = "T" . deptno
inner join salgrade "S"
on "T" . "avg_sal" between "S" . losal and
"S" . hisal
习题:
判断以下语句输出是几行:
a) select * from emp, dept where emp.deptno = 10
--15 行
b) select * from emp, dept where dept.deptno = 10
--14 行这里是过
-- 滤条件不是连接条件
如何把 select * from emp, dept where dept.deptno = 10 以 sql99 标准来完成
select * from emp join dept on 1 = 1 where dept. deptno = 10;
求出每个员工的姓名、部门编号、薪水和薪水的等级
select "E" . ename , "E" . deptno , "E" . sal , "G" . GRADE
from emp "E"
join SALGRADE "G"
on "E" . sal between "G" . LOSAL and "G" . HISAL ----------------------------------------------
18
查找每个部门的编号、该部门所有员工的平均工资、平均工资的等级
select "T" . deptno , "T" . avg_sal , "G" . GRADE
from (
select "E" . deptno , avg ( sal ) "avg_sal"
from emp "E"
group by "E" . deptno
--order by "E".deptno--error , 不能加
) "T" -- 这是一张临时表
join SALGRADE "G"
on "T" . avg_sal between "G" . LOSAL and "G" . HISAL
-- 等价于 ---------------------------------------
select "T" . deptno , "T" . avg_sal , "G" . grade
from SALGRADE "G"
join(
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
) "T"
on "T" . avg_sal between "G" . losal and "G" . hisal
-- 等价于 ---------------------------------------
select "T" . deptno , "T" . avg_sal , "G" . grade
from salgrade "G" , (
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
) "T"
where "T" . avg_sal between "G" . losal and "G" . hisal
查找每个部门的编号、部门名称、该部门所有员工的平均工资、平均工
资的等级
select "T" . deptno , "T" . avg_sal , "G" . grade , "D" . dname
from SALGRADE "G"
join(
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
) "T"
on "T" . avg_sal between "G" . losal and "G" . hisal 19
join dept "D"
on "T" . deptno = "D" . deptno
求出 emp 表中所有领导的信息
select *
from emp
where empno in ( select mgr from emp )
select *
from emp
where empno not in( from mgr from emp ) ---error
-- 因为里面有 null ,属于 in 与 null 的组合带来的问题
求出平均薪水最高的部门的编号和部门的平均工资
a) 第一种写法:
select top 1 deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
order by avg ( sal ) desc
b) 第二种写法: ( 可以跨数据库 )
select "E" .*
from (
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
) "E"
where "E" . "avg_sal" = (
select max ( "avg_sal" )
from (
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
) "T"
)
把工资大于所有员工中工资最低的人中,前 3 个人的姓名、工资、部门
编号、部门名称、工资等级输出
select min ( sal ) from emp 20
select *
from emp
where sal > ( select min ( sal ) from emp )
-- 到此生成一个临时表
-- 不能 join on
select TOP 3 "T" . ename , "T" . sal , "T" . deptno ,
"D" . dname , "S" . GRADE
from (
select *
from emp "E"
where sal > ( select min ( sal ) from emp )
) "T"
join dept "D"
on "T" . deptno = "D" . deptno
join SALGRADE "S"
on "T" . sal between "S" . losal and "S" . hisal
order by "T" . sal asc
外连接
不但返回 满足连接条件的所有记录,
定义:
而且会返回 部分不满足条件的记录
分类:左(右)外连接:不但返回满足连接条件的所有记录,而且
会返回左(右)表不满足连接条件的记录
左外连接运行原理:
select * from emp "E"
left join dept "D"
on E.deptno = D.deptno
用左表的第一行分别和右表的所有行进行连接 ,
如果有匹配的行,则一起输出,
如果右表有多行匹配,则结果集输出多行,
如果没有匹配行,则结果集中只输出一行,该输出行左边为左表第一行内
容,右边全部输出 null (内连接不输出)
然后再用左表第二行和右边所有行进行连接,
如果有匹配的行,则一起输出,
如果右表有多行匹配,则结果集输出多行,
如果没有匹配行,则结果集中只输出一行,该输出行左边为左表第二行内
容,右边全部输出 null
以此类推,直至左边所有行连接完毕
因为右边很可能出现有多行和左边的某一行匹配,所以左连接产生的结果
集的行数 至少大于 left join 左边表的记录的总数
21
帮助文档:左向外连接的结果集包括 LEFT OUTER 子句中指定的左表的所
有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配
行,则在相关联的结果集行中右表的所有选择列表列均为空值
实际上左连接产生的结果集的行数很至少大于左边表的记录的总数,不
能理解为:左边表有 n 行,最终产生的记录也是 n 行。实际上左连接产
生的结果集的行数至少大于左边表的记录的总数
select *
from dept"D"
left join emp "E"
on E . deptno = D . deptno
结果有 16 行,左面的 dept 有 5 行( 10 , 20 , 30 , 40 , 50 ),前三行可以
与 emp 表中的值进行连接,将有 14 行,其中 40 与 50 与右面的表没有
连接成功,分别输出 dept 表中的第 15 , 16 行,右面都为 null
左外连接的实际意义
一个事物 及其 该事物的相关信息 , 如果该事物 没相关信息 ,则 输出 null
例子:
a) 已知条件:
productStocks 货物库存表
orderForm 订单表
pId 产品的编号
sql 语句:
a) select productStocks.*, orderForm.*
from productStocks
left join orderForm
on productStocks.pId = orderForm.pId
实际意义:
返回仓库中现存 货物的信息表 及其该 货物的订单信息表 ,如果货物
没有订单信息 ,在把该 货物的订单信息全部输出为 null
完全连接( 左外连接与右外连接的结合 )
例子:
select * from productStocks
full join orderForm
on productStocks.pId = orderForm.pId
结果集中包含二部分内容
两个表匹配的所有行记录
左表中那些在右表中找不到匹配的行的记录,这些记录的右边全为 null
右表中那些在左表中找不到匹配的行的几率,这些记录的左边全为 null
交叉连接
select * from emp cross join dept
等价于
select * from emp, dept
22
自连接
定义:一张表 自己 和 自己连接 起来查询数据
例子:不准用聚合函数,求薪水最高的员工的信息
-- 用聚合函数,求薪水最高的员工的信息
select *
from emp
where sal = ( select max ( sal ) from emp ) - - 注意 =
-- 不用聚合函数,求薪水最高的员工的信息
select "E1" . empno , "E1" . sal , "E2" . sal
from emp "E1"
join emp "E2" -- 自连接
on "E1" . sal < "E2" . sal
-- 输出不包含那一行
select "E1" . empno , "E2" . sal , "E2" . sal
from emp "E1"
left join emp "E2" -- 自连接
on "E1" . sal < "E2" . sal
-- 输出包含那一行,且右边为 null
-- 所以
select *
from emp
where empno not in ( -- 注意不能用 =
select distinct "E1" . empno
from emp "E1"
join emp "E2" -- 自连接
on "E1" . sal < "E2" . sal
)
联合( 纵向 连接 )
定义:表和表之间的数据以 纵向的方式 连接在一起
我们以前讲的所有的连接是以横向的方式连接在一起的
例子:输出每个员工的姓名,工资,上司的姓名
select *
from emp "E1"
join emp "E2"
on "E1" . mgr = "E2" . empno
-- 输出 13 行,因为有一行 king 没有上司为 null
23
此时使用联合
select "E1" . ename , "E1" . sal , "E2" . ename
from emp "E1"
join emp "E2"
on "E1" . mgr = "E2" . empno
union -- 最后添加行
select ename , sal , ' 老板 '
from emp
where mgr is null
注意:若干个 select 子句要联合成功的话,必须得满足两个条件,
这若干个 select 子句输出的列数必须是相等的
这若干个 select 子句输出列的数据类型至少是兼容的
identity
identity【 主键自动增长 ,用户不需要为 identity 修饰的主键赋值】
identity 表示该字段的值会自动更新,不需要我们维护,通常
情况下我们不可以直接给 identity 修饰的字符赋值,否则编译
时会报错
语法格式为 :
identity(m, n)
m 表示的是初始值, n 表示的是每次自动增加的值
要么同时指定 m 和 n 的值,要么 m 和 n 都不指定,不能只写其中一个值;如
果 m 和 n 都未指定,则取默认值 (1, 1)
create table student2
(
student_id int primary key , - - 必须手动为主键赋值
student_name nvarchar ( 200 ) not null
)
insert into student2 values ( 1 , ' 张三 ' )
insert into student2 values ( 2 , ' 李四 ' )
insert into student2 values ( 3 , ' 王五 ' )
select * from student2
create table student3
(
student_id int primary key identity , - - 从增长自动
--indentity(100, 5) 从开始自增 24
student_name nvarchar ( 200 ) not null
)
insert into student3 ( student_name ) values (' 赵六 ')
select * from student3
insert into student3 values (' 李四 ') -- 可以忽略主键
select * from student3
delete from student3 where student_name = ' 李四 '
select * from student3
insert into student3 values (' 张三 ')
select * from student3 -- 主键值为 3 ,
数据类型是整型的列才能被定义成标识列
int, bigint, smallint 列都可以被定义成 identity
不含有小数位的 decimal 和 numeric 也可以被标记为 identity, 如: decimal,
decimal(6, 0) 字段都可以被标记为 identity ,但是 decimal(6, 2) 字段就不能被标
记为 identity
标识列通常与 primary key 约束一起用作表的唯一行标识符(非
主键也是可以被定义为 identity 的,但不推荐)
如何重新设置 identity 字段的值
create table emp(
empid identity(1, 1),
ename nvarchar(20) not null,
);
insert into emp values('aaaa');
insert into emp values('bbbb');
insert into emp values('cccc');
insert into emp values('dddd');
--8 行
select * from emp
delete from emp where empid = 4
-- 删除 empid 为 4 的记录
select * from emp
insert into emp values('eeee')
-- 因为执行 8 行时 empid 为 4 ,所以执行
-- 本句时, empid 为 5
select * from emp
delete from emp where empid = 5
dbcc checkident('emp', reseed, 3)
--16 行,把 emp 表中 identity 字段的初始
-- 值重新设置为 3
insert into emp values('eeee')
-- 此时插入记录时, empid 为 4 ,因为 16
行代码已经把 empid 设置成了 3
select * from emp
dbcc checkident('emp', reseed, 0)
25
种子的值也可以是零,这样设置的话,用户插入值时,种子的初始值将从 1
开始
如何向 identity 字段插入数据(不是重点)
通常 identity 标记的字段我们是不需要插入数据的,即我们不需要维护 identity
字段的值,它会自动更新,如果我们需要向 identity 修饰的字段插入值,则必
须满足如下两点:
先得执行 setidentity_insert[database.[owner]] {bable} {on|off}
插入数据时必须得指定 identity 修饰的字段的名字
向 identity 字段插入数据示例:
create database Test
use Test
create bable dept(
a) deptid decimal(6, 0) identity,
b) deptname varchar(20)
);
set identity_insert test.dbo.dept on
i.
-- 执行本句的目的是:希望可以各 identity 修饰的字段插入值
ii.
-- 不可以改为 set identity _insert dept on
iii.
-- 不可以改为 set identity _insert dbo.test.dept on
iv.
-- 不可以改为 set identity _insert dbo.test.dept.on
insert into dept(deptid, deptname) values (1, 'zhangsan')
i.
-- 不能改为: insert into dept values(1, 'zhangsan')
视图
为什么需要视图:简化查询(避免了代码的冗余,改名了书写
大量重复的 sql 语句)
-- 求出平均工资最高的部门的编号和部门的平均工资
select *
from (
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
) "T"
where "T" . "avg_sal" = (
select max ( "E" . "avg_sal" )
from (
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno 26
) "E"
)
-- 使用视图避免了代码的冗余
create view v_emp_1
as
select deptno , avg ( sal ) "avg_sal"
from emp
group by deptno
select \* from v_emp_1
select * from v_emp_1 where avg_sal = ( select max ( avg_sal ) from v_emp_1 )
什么是视图:
视图 从代码上看是一个 select 语句
视图 从逻辑上看被当做一个 虚拟表 看待
如何创建视图:
create view 视图的名字
as
--select 的前面不能添加 begin
select 语句
--select 的后面不能添加 end
视图的优点:简化查询,增加数据的保密性(隐藏)
-- 隐藏工资 , 入职年费
create view v_emp2
as
select empno , ename , job , mgr , comm , deptno from emp
select \* from v_emp2
视图的缺点:增加了数据库维护的成本
只是简化了查询,但是并不能加快查询的速度
注意的问题:
27
创建视图的 select 语句必须为所有的计算列指定别名
create view v$_a
as
select avg ( sal ) from emp --error
create view v$_a
as
select avg ( sal ) "avg_sal" from emp --OK
-- 不使用分组默认全部为一组
视图不是物理表,是虚拟表
不建议通过视图更新视图所依附的原始表的数据或结构
事务【重要,初学者重在理解概念】
初学者必须理解的三个概念:
事务是用来研究什么的 , 为什么需要事物
保证数据的合理性( 要么成功要么失败 )
事务主要用来
并发处理的能力( 多个用户访问怎么办 )
通俗点说:
a) 事务可以保证避免数据处于一种不合理的中间状态
要么全部执行失败,
要么全部执行成功,
不能出现半对半错的情况。
例如 银行转帐
b) 利用事务可以实现 多个用户 对共享资源的 同时访问
事务和线程的关系:
事务 是 通过 锁 来解决并发访问的
线程同步 也是 通过锁 来解决并发访问的 synchronized
所谓并发访问是指:多用户同时访问同一个数据
事务和第三方插件的关系
直接使用事务库技术难度大,很多人是借助第三方插件来实现,因
此一般人不需要细细研究数据库中事务的语法细节
第三方插件要想完成预期的功能,一般必须得借助数据库中的事物
机制来实现
28
T-SQL 使用下列语句来管理事务(不重要) " 异常机制 "
开始事务: begin transaction
提交事务: commit transaction
回滚(撤销)事务: rollback transaction
一旦事务提交或回滚,则事务结束
事务三种运行模式:
显式事务 :(一般使用)
每个事务均以 begin transaction 语句显式开始
以 commit 或 rollback 语句显式结束
自动提交事务:
第条单独的语句都是一个事务。如果成功执行,则自动提交;如果错误,
则自动回滚;这是 sql server 2005 默认的模式
隐性事务:
在前一个事务完成时 新事务隐式启动 ,但每个事务仍以 commit 或 rollback
语句
事务的四大特性(简称 ACID 属性,一般在面试的时候能用到):
原子性:事务是一个完整的操作。事务的各步操作是不可分的;
要么都执行,要么都不执行
一致性:当事务完成时,数据必须处于一致状态,
要么处于开始状态
要么处于结束状态,
不允许出现中间状态
隔离性:指当前的事务与其他未完成的事务是隔离的。在不同的隔
离级别下,事务的为读取操作,可以得到的结果是不同的
持久性:事务完成后,它对数据库的修改被永久保持,事务日志能
够保持事务的永久性
注意问题:不能在 sql server 中单独使用 commit, rollback 语句
索引(可以加快查询)
类似于字典的目录
存储过程
游标
TL-SQL 29
触发器
分页查询
假设每而显示 n 条记录,当前要显示的是第 m 而,表名是 A ,主键是 A_id :
select top n *
from A
where A_id not in (select top (m - 1) * n A _id from emp )
-- 分页 --- 打开网页下面可以一页一页的看,有下一页上一页按钮等
-- 输出工资最高的前三个
select * from emp order by sal
select top 3 * -- 后执行
from emp
order by sal desc -- 先执行
-- 工资从高到低排序,输出工资排名第 -6 的员工信息
select top 3 *
from emp
where empno not in (
select top 3 empno
from emp
order by sal desc
) -- 找到工资最高的三个排除
order by sal desc
-- 工资从高到低排序,输出工资排名第 -9 的员工信息
select top 3 *
from emp
where empno not in (
select top 6 empno
from emp
order by sal desc
) -- 找到工资最高的六个排除
order by sal desc
-- 工资从高到低排序,输出工资排名第 -12 的员工信息
select top 3 *
from emp
where empno not in ( 30
select top 9 empno
from emp
order by sal desc
) -- 找到工资最高的九个排除
order by sal desc
-- 工资从高到低排序,输出工资排名第 -15 的员工信息
select top 3 *
from emp
where empno not in (
select top 12 empno
from emp
order by sal desc
) -- 找到工资最高的个排除
order by sal desc