SQL Server操作进阶
一、创建数据库
create database TestData;
二、将查询编辑器连接到数据库
use TestDB;
三、创建表
create table emp ( empno int primary key not null, ename varchar(16) null, egender nvarchar(2) CHECK(egender='男' or egender='女'), job varchar(16) null, hiredate datetime null, sal int null, comm int null, deptno int null, mgr int null );
-
创建带自增项的表
create table student ( s_id int identity(1001,1) primary key not null, s_name varchar(200) not null, );
-
设置主键(如果主键未设置)
alter table emp add constraint PK_empno primary key (empno);
四、增加/插入数据
insert into emp values (7001,'JUDY', 'Engineer', 1981-1-2,2000, 200, 10, 7006);
insert into student values ('张三');
insert into student (sname,age) values ('Tom',12);
五、修改/更新数据
update emp set ename = 'Jack' where empno = 8975
六、删除
-
删除表中所有行数据(删除后再插入数据,被 identity 修饰的属性将从头开始自增)
truncate table student;
-
删除表中所有行数据(删除后再插入数据,被 identity 修饰的属性将从删除前已经有的值继续自增)
delete from student;
-
删除表
drop table student;
-
删除视图
drop view v$_emp_1;
-
删除存储过程
drop proc pr_Names;
-
删除自定义函数
drop function f_sal_range
七、查询
-
计算列
select ename, sal "月薪", sal*12 as "年薪", job from emp; -- 计算列
-
distinct 【不重复】
select distinct deptno from emp; -- distinct deptno 会过滤掉重复的 deptno select distinct comm from emp; -- distinct 也可以过滤掉重复的 NULL select distinct deptno, comm from emp; -- 把 deptno 和 comm 组合进行过滤
-
查找工资在1500到3000之间(包括1500和3000)的所有员工的信息
select * from emp where sal between 1500 and 3000;
-
in 【属于若干个孤立的值】
select * from emp where sal in (1600, 3000); -- sal 等于1600或3000 select * from emp where sal not in (1600, 3000); -- sal 不等于1600或3000 select * from emp where sal!=1600 and sal!=3000; -- sal 不等于1600且不等于3000 select * from emp where sal<>1600 and sal<>3000; -- sal 不等于1600且不等于3000(“<>”为不等于,推荐使用)
-
top
select top 2 * from emp; -- 筛选前2个 select top 15 percent * from emp; -- 筛选前15% -- 筛选工资在1000到3000之间的工资最高的前两个(asc为升序,默认值可省略;desc为降序) select top 2 * from emp where sal between 1000 and 3000 order by sal desc
-
null 输出奖金非空的员工的信息
select * from emp where comm <> null; -- 错误!输出为空 error select * from emp where comm != null; -- 错误!输出为空 error select * from emp where comm = null; -- 错误!输出为空 error -- 总结:null 不能参与 <>、!=、= 运算 -- null 可以参与 is、not is select * from emp where comm is null; select * from emp where comm is not null; -- 输出每个员工的姓名,年薪(包含奖金),假设comm是一年的奖金 select empno,ename,sal*12+comm from emp; -- comm为空的时候得到的值为null,null不能参与数学运算
-
order by 排序
select * from emp order by sal; -- 按照sal升序排序(等价于:select * from emp order by sal asc) select * from emp order by sal desc; -- 按照sal降序排序 select * from emp order by deptno, sal; -- 先按照deptno升序排序, deptno相同的时候再按照sal升序排序 select * from emp order by deptno, sal desc; -- 先按照deptno升序排序, deptno相同的时候再按照sal降序排序
-
模糊查询("%":匹配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"结尾的 select * from emp where ename like '_A%'; -- 查询 ename 包含"A",且"A"前面有且只有一个字符 select * from emp where ename like '_[A-F]%'; -- 把 ename 中第二个字符为A或B或C或D或E或F的记录输出 select * from emp where ename like '_[A,F]%'; -- 把 ename 中第二个字符为A或F的记录输出 select * from emp where ename like '_[^A-C]%'; -- 把 ename 中第二个字符不是A也不是B也不是C的记录输出 select * from emp where ename like '%\%%' escape '\'; -- 把 ename 包含"%"的记录输出(escape 修饰转义字符) select * from emp where ename like '%a%%' escape 'a'; -- 把 ename 包含"%"的记录输出(escape 修饰转义字符) select * from emp where ename like '%\_%' escape '\'; -- 把 ename 包含"_"的记录输出(escape 修饰转义字符)
-
聚合函数
select lower(ename) from emp; -- 所有 ename 转小写,每行返回一行(单行函数) select max(sal) from emp; -- sal 中的最大值,多行返回一行(多行函数) select count(*) from emp; -- 返回 emp 表所有记录的个数 select count(deptno) from emp; -- 返回 deptno 所有非空的记录的个数 select count(distinct deptno) from emp; -- 返回 deptno 不重复且非空的记录的个数 select max(sal) "最高工资", min(sal) "最低工资", count(*) "总人数" from emp;
-
group by
select deptno,avg(sal) as "部门平均工资" from emp group by deptno; -- 输出每个部门的编号和该部门的平均工资 -- 先按照 deptno 分组,在 deptno 内再按 job 分组 select deptno, job as "岗位", avg(sal) as "小组平均工资", count(*) as "人数" from emp group by deptno, job order by deptno;
-
having
-- 输出部门平均工资大于1500的部门的部门编号和部门平均工资 select deptno, avg(sal) as "平均工资" from emp group by deptno having avg(sal) > 1500;
八、内连接查询【重点难点】
-
select ... from A, B 的用法
select * from emp, dept; -- 结果:得到一个新表,行数是A和B的数量之积,列数是A和B的数量之和(笛卡尔积)
-
select ... from A, B where ... 的用法
select * from emp, dept where empno = 7369; -- 结果:产生的笛卡尔积用 where 中的条件进行过滤 select * from emp, dept where emp.deptno = dept.deptno;
-
select ... from A join B on ... 的用法
select "E".ename '员工姓名', "D".dname '部门名称', "D".loc '地址' from emp "E" inner join dept "D" -- join是连接 inner 可以省略 on "E".deptno="D".deptno; -- on是连接条件 ( on 1=1 等同于省略连接条件) select * from emp "E" join dept "D" -- join是连接 on "E".deptno="D".deptno where "E".sal>1500;
-
多表内连接
select "E".ename "员工", "E".sal "薪水", "S".grade "薪资等级" from emp "E" join dept "D" on "E".deptno = "D".deptno join salgrade "S" on "E".sal >= "S".losal and "E".sal <= "S".hisal;
练习
-- 练习一、求出每个员工的姓名 部门编号 薪水和薪水等级 select "E".ename "姓名", "E".deptno "部门", "E".sal "薪水", "S".grade "薪水等级" from emp "E" join salgrade "S" on "E".sal between "S".losal and "S".hisal; -- 练习二、查找每个部门的编号 该部门的所有员工的平均工资 平均工资等级 select "T".deptno, "T".avg_sal "部门平均工资", "S".grade "平均工资等级","D".dname "部门名称" from ( select "E".deptno, avg("E".sal) as "avg_sal" from emp "E" group by "E".deptno ) "T" join salgrade "S" on "T".avg_sal between "S".losal and "S".hisal join dept "D" on "T".deptno = "D".deptno; -- 练习三 求出平均薪资最高的部门的名称和部门的平均薪资 select "D".dname "部门", "T".avg_sal "平均薪资" from ( select top 1 avg(sal) as "avg_sal", "E".deptno from emp "E" group by deptno order by avg(sal) desc ) "T" join dept "D" on "T".deptno = "D".deptno -- 练习四 把工资大于最低的人的前三个最低工资的人的信息输出 select "T".ename, "T".sal,"T".deptno,"D".dname,"S".grade from( select top 3 * from emp where sal > (select min(sal) from emp) order by sal asc ) "T" join dept "D" on "T".deptno = "D".deptno join salgrade "S" on "T".sal between "S".losal and "S".hisal; -- 或者 select top 3 "T".ename, "T".sal,"T".deptno,"D".dname,"S".grade from( select * from emp 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;
九、外连接查询
-
左连接、左外连接(返回左表的所有行,右表中没有匹配上,对应的列就显示 NULL)
select * from dept "D" left outer join emp "E" -- outer 可以省略 on "E".deptno="D".deptno;
-
右连接、右外连接(返回右表的所有行,左表中没有匹配上,对应的列就显示 NULL)
select * from dept "D" right outer join emp "E" -- outer 可以省略 on "E".deptno="D".deptno;
十、完全连接
结果集中包含三部分内容:
1.两个表中匹配的所有行记录
2.左表中那些在右表中找不到匹配行的记录,这些记录的右边全为null
3.右表中那些在左表中找不到匹配行的记录,这些记录的左边全为null
select * from emp "E" full outer join dept "D" -- outer 可以省略 on "E".deptno = "D".deptno;
十一、交叉连接
如果不带 where 子句,返回两个表的笛卡尔积
带 where 子句等同于 inner join 返回的是匹配的数据
select * from emp E cross join dept D where E.deptno = D.deptno;
十二、自连接
-
用聚合函数 求出薪资最高的员工的信息
select * from emp where sal = (select max(sal) from emp);
-
不用聚合函数 求出薪资最高的员工的信息
select * from emp where empno not in ( select distinct "E1".empno from emp "E1" join emp "E2" on "E1".sal < "E2".sal );
十三、联合(表与表之间以纵向的方式连接在一起)
输出每个员工的姓名 工资 上司的姓名
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 * from ( select deptno, avg(sal) "avg_sal" from emp group by deptno ) "T" where "T"."avg_sal" = ( select min("E"."avg_sal") from( select deptno, avg(sal) "avg_sal" from emp group by deptno ) "E" )
-
使用视图
create view v$_emp_1 as select deptno, avg(sal) "avg_sal" from emp group by deptno select * from v$_emp_1 where avg_sal = (select min(avg_sal) from v$_emp_1)
十五、事务【重点】
事务:主要用来保证数据的合理性和并发处理能力
1.避免数据处于不合理的中间状态(如转账)
2.事务是通过锁来解决很多问题的
create table bank ( customerEname nvarchar(200), currentMoney money ) insert into bank values('Tom', 1000) insert into bank values('Jack', 1) alter table bank add constraint check_currentMoney check(currentMoney>1) update bank set currentMoney = currentMoney-1000 where customerEname = 'Tom' update bank set currentMoney = currentMoney+1000 where customerEname = 'Jack' begin transaction declare @errorSum int set @errorSum = 0 update bank set currentMoney = currentMoney - 1000 where customerEname='Tom' set @errorSum = @errorSum + @@error update bank set currentMoney = currentMoney + 1000 where customerEname='Jack' set @errorSum = @errorSum + @@error if(@errorSum<>0) begin print '转账失败' rollback transaction end
十六、类型转换
select 1+2; -- 3 select 'a'+'b'; -- 'ab' select 'abc' + 2; -- error select 'abc' + convert(varchar,2); -- 'abc2' select convert(int,'abc') + 2; -- error select convert(varchar(10), getdate(), 126); -- 2022-06-25(日期格式, 126代表一种格式) select 'abc' + cast(2 as varchar); -- 'abc2' select cast('2021-1-23' as datetime); -- 2021-01-23 00:00:00.000
十七、字符串操作
select charindex('bc','abcdbc'); -- 2 (返回指定子串第一次出现的位置) select patindex('%bc%','abcdbc'); -- 2 (返回指定子串第一次出现的位置,子串前后必须带%号) select upper('abcdefg'); -- ABCDEFG (小写转大写) select lower('ABCDEFG'); -- abcdefg (大写转小写) select len('abc'); -- 3 (长度) select ltrim(' a bc '); -- 去除左边的空格 select rtrim(' a bc '); -- 去除右边的空格 select left('abcdefg',4); -- abcd (返回字符串从左边开始取的4个字符) select right('abcdefg',4); -- defg (返回字符串从右边开始取的4个字符) select substring('abcdefg', 3,2); -- cd (返回从第3个位置开始的长度为2个的字符串) select replicate('abc',4); -- abcabcabcabc (将指定字符串重复4次数生成新的字符串) select reverse('abcdefg'); -- gfedcba(字符串翻转) select replace('abcdefg','cd','xx'); -- abxxefg (字符串部分替换) select stuff('acccefg', 2,3,'x'); -- axefg (将字符串中从第2个位置开始长度为3个的字符串替换为'x')
十八、索引(默认非聚集索引)
-
聚集索引
create clustered index PK_Bank on bank(currentMoney) with ( drop_existing=on -- 如果这个索引已存在就删除再生成 --drop_existing=off -- 不删除原有的,提示一个错误,索引已存在 )
-
非聚集索引
create unique nonclustered index uq_emp -- 唯一 非聚集索引 on emp(ename) with ( pad_index=on, fillfactor=100, -- 填充因子(指定创建索引时,每个索引页的数据占索引页大小的百分比) ignore_dup_key=on )
-
复合索引:多个列上建立的索引
create nonclustered index index_emp -- 唯一 非聚集索引 on emp(ename,job) with ( drop_existing=on )
十九、存储过程
-
创建一个无参数的存储过程
create proc search_sal as begin select empno,ename,job,sal from emp select * from bank end;
-
删除存储过程
drop proc search_sal
-
创建一个带参数的存储过程
create proc add_emp @empno int, @ename varchar(16), @job varchar(16), @sal int, @deptno int, @mgr int as begin declare @time datetime set @time = getdate() insert into emp (empno,ename,job,sal,deptno,mgr) values(@empno,@ename,@job,@sal,@deptno,@mgr) end;
--调用 exec add_emp 7651,'李飞','售货员', 3000, 20, 7777
二十、触发器(一种特殊类型的存储过程,通过事件触发,自动调用执行)
分类: DDL触发器(create alter drop); DML触发器(insert update delete)
逻辑表是虚表,是系统在内存中创建的,不会存储到数据库中,只读的,读取单不能修改数据。 结构与操作的表相同。
触发器执行过程中存在,并且可以访问,工作完成之后,表会被自动删除
-
after / insert 触发器(alert trigger trigger_emp_insert)
create trigger trigger_emp_insert on emp for insert -- for after insert as declare @id int, @name varchar(16) -- 定义局部变量 select @id=empno,@name=ename from inserted print convert(varchar,@id)+''+@name+'添加成功';
-- 触发 trigger_emp_insert insert into emp (empno,ename,job,sal,comm,deptno,mgr) values (7011,'张三','行政专员',2800,100,10,7001);
-
after / update 触发器
create trigger trigger_emp_update on emp for update as declare @id int, @name varchar(16), @money int -- 定义局部变量 select @id=empno,@name=ename,@money=sal from deleted print '修改前'+convert(varchar,@id)+''+@name+','+convert(varchar,@money); select @id=empno,@name=ename,@money=sal from inserted print '修改后'+convert(varchar,@id)+''+@name+','+convert(varchar,@money);
-- 触发 trigger_emp_update update emp set ename='王五', sal=2999 where empno=7011;
二十一、行列转换
-
行转列
-- 1.先建立一个用于行转列的基础表 create table test ( id int identity(1,1) not null, sname varchar(255) null, course varchar(255) null, score int null );
-- 2.插入一些数据 insert into test (sname, course, score) values ('张三', '语文', 66); insert into test (sname, course, score) values ('张三', '数学', 54); insert into test (sname, course, score) values ('张三', '英语', 63); insert into test (sname, course, score) values ('李四', '语文', 75); insert into test (sname, course, score) values ('李四', '数学', 89); insert into test (sname, course, score) values ('李四', '英语', 88); insert into test (sname, course, score) values ('王五', '语文', 69); insert into test (sname, course, score) values ('王五', '数学', 86); insert into test (sname, course, score) values ('王五', '英语', 98); insert into test (sname, course, score) values ('王五', '物理', 72); insert into test (sname, course, score) values ('王五', '化学', 65);
-- 3.行转列方法一:使用 case when then else, 此处也可以使用 sum select sname, max(case course when '语文' then score else 0 end) as chinese, max(case course when '数学' then score else 0 end) as math, max(case course when '英语' then score else 0 end) as english, max(case course when '物理' then score else 0 end) as wuli, max(case course when '化学' then score else 0 end) as huaxue from test group by sname;
-- 4.行转列方法二:使用 pivot 函数 select sname, max(t.语文) as chinese, max(t.数学) as math, max(t.英语) as english, max(t.物理) as wuli, max(t.化学) as huaxue from test pivot (max(score) for course in(语文,数学,英语,物理,化学)) t group by sname;
-
列转行
-- 1.先建立一个用于列转行的基础表 create table test1 ( id int identity(1,1) not null, sname varchar(255) null, chinese int null, math int null, english int null, wuli int null, huaxue int null )
-- 2.插入一些数据 insert into test1 values ('马六',110,120,85,null,null) insert into test1 values ('陈七',130,88,89,null,null) insert into test1 values ('孙八',93,124,87,98,67)
union: 会对结果集进行去重
union all: 不会进行去重
-- 3.列转行方法一: select row_number() over (order by id desc) as id,sname,t.course,t.score from ( select id,sname,course='语文',score=chinese from test1 union all select id,sname,course='数学',score=math from test1 union all select id,sname,course='英语',score=english from test1 union all select id,sname,course='物理',score=wuli from test1 union all select id,sname,course='化学',score=huaxue from test1 ) t where score is not null order by id asc;
-- 4.列转行方法二: select row_number() over (order by id desc) as id,sname,t.course,t.score from ( select id,sname,'语文' as course,chinese as 'score' from test1 union all select id,sname,'数学' as course,math as 'score' from test1 union all select id,sname,'英语' as course,english as 'score' from test1 union all select id,sname,'物理' as course,wuli as 'score' from test1 union all select id,sname,'化学' as course,huaxue as 'score' from test1 ) t where score is not null order by id asc;
-- 5.列转行方法三: select row_number() over(order by id desc) as id,sname,course,score from test1 unpivot (score for course in (chinese,math,english,wuli,huaxue)) a;
二十二、自定义函数
-
标量函数,返回的是一个标量
create function f__avg_sal( @minSal int ) returns int as begin declare @avgV int set @avgV = (select avg(sal) from emp where sal < @minSal) return @avgV end;
select dbo.f__avg_sal(2000); -- 调用函数
-
内联表值函数, 返回的是一个表
create function f_sal_range (@min_sal int, @max_sal int) returns table as return (select * from emp where sal between @min_sal and @max_sal);
select * from f_sal_range(2000, 4000); -- 调用函数(和查询普通表一样)
-
多语句表值函数(可以看作是标量函数和内联表值函数的结合体)
create function func ( @pJob varchar(50), @pSal int ) returns @copy table ( empno int, ename varchar(50), job varchar(50), sal int, deptno int ) as begin declare @depId int select @depId=deptno from emp where job=@pJob or sal=@pSal if @depId is null insert into @copy select empno,ename,job,sal,deptno from emp else insert into @copy select empno,ename,job,sal,deptno from emp where deptno=@depId return end
select * from func('IT',2800); -- 调用函数
二十三、主键外键
-
在表创建好了之后,再设置主键
alter table 表名 add constraint PK_字段名--"PK"为主键的缩写,字段名为要在其上创建主键的字段名,'PK_字段名'就为约束名 primary key (字段名) --字段名同上
-
唯一约束
alter table 表名
add constraint UQ_字段名
unique (字段名)
-
外键约束
alter table 表名 add constraint FK_字段名--"FK"为外键的缩写 foreign key (字段名) references 关联的表名(关联的字段名) --注意'关联的表名'和'关联的字段名'
-
级联更新,级联删除,这样在删除主表Student时,成绩表中该学生的所有成绩都会删除。
alter table 表A add constraint FK_B foreign key (ticket_no) references 表B(ticket_no)
alter table 表A add constraint FK_C foreign key (person_no) references 表C(person_no)
alter table 成绩表 add constraint FK_StudentNo foreign key (StudentNo) references Student(StudentNo)
ON UPDATE CASCADE ON DELETE CASCADE
-
检查约束
alter table 表名
add constraint CK_字段名
check (条件表达式) --条件表达式中的条件用关系运算符连接
-
默认值约束
alter table 表名 add constraint DF_字段名 default '默认值' for 字段名--其中的'默认值'为你想要默认的值,注意'for'
-
删除创建的约束
alter table 表名 drop constraint 约束名--约束名为你前面创建的如:PK_字段这样的约束名 --注意:如果约束是在创建表的时候创建的,则不能用命令删除 --只能在'企业管理器'里面删除
-
获取SqlServer中表结构
SELECT syscolumns.name,systypes.name,syscolumns.isnullable, syscolumns.length FROM syscolumns,systypes WHERE syscolumns.xusertype = systypes.xusertype AND syscolumns.id = OBJECT_ID('Student')
-
单独查询表递增字段
SELECT [name] FROM syscolumns WHERE id = OBJECT_ID(N'Student') AND COLUMNPROPERTY(id,name,'IsIdentity')=1
-
获取表主外键约束
EXEC sp_helpconstraint 'StuResults'
-
查询表主键外键信息
SELECT sysobjects.id objectId,OBJECT_NAME(sysobjects.parent_obj) tableName, sysobjects.name constraintName, sysobjects.xtype AS constraintType, syscolumns.name AS columnName FROM sysobjects INNER JOIN sysconstraints ON sysobjects.xtype in('C', 'F', 'PK', 'UQ', 'D') AND sysobjects.id = sysconstraints.constid LEFT OUTER JOIN syscolumns ON sysconstraints.id = syscolumns.id WHERE OBJECT_NAME(sysobjects.parent_obj)='StuResults'

浙公网安备 33010602011771号