美文网首页
sqlP39-P48

sqlP39-P48

作者: kevinXiao | 来源:发表于2021-06-30 14:05 被阅读0次

#进阶3:排序查询

/*

#引入

SELECT * FROM employees

语法:执行顺序

select 查询列表 3

from 表 1

【where 筛选条件】 2

order by  排序列表 【asc | desc】 4

特点:

1.asc代表的是升序,desc代表的是降序,如果不写默认是升序

2.order by子句中可以支持单个字段、多个字段、表达式、函数、别名

(多个字段排序,先根据xx进行排序,当xx相同的时候根据yy进行排序)

3.order by 子句一般是放在查询语句的最后面,limit子句除外

*/

#案例:查询员工信息、要求工资从高到低排序

SELECT * FROM employees ORDER BY salary DESC

SELECT * FROM employees ORDER BY salary

#案例2 查询部门编号 >= 90 的员工信息,按入职时间的先后进行排序 【添加筛选条件】

SELECT * FROM employees WHERE department_id >= 90 ORDER BY hiredate ASC

#案例3 按年薪的高低显示员工的信息和年薪【按表达式排序】

SELECT *, salary *12 * (1 + IFNULL(commission_pct,0)) AS 年薪

FROM employees

ORDER BY salary * 12 * (1 + IFNULL(commission_pct,0)) DESC

#案例4 按年薪的高低显示员工的信息和年薪【按表达式排序】

SELECT *, salary *12 * (1 + IFNULL(commission_pct,0)) AS 年薪

FROM employees

ORDER BY 年薪 DESC

#案例5 按姓名的长度显示员工的姓名和工资【按函数排序】

SELECT LENGTH(last_name) AS 字节长度, last_name, salary FROM employees ORDER BY 字节长度 DESC

#案例6 查询员工信息,要求先按工资排序,再按员工编号排序【按多个字段排序】

SELECT * FROM employees

ORDER BY salary ASC , employee_id DESC

#案例 7 查询员工的姓名和部门好和年薪,按年薪降序,按姓名升序

SELECT last_name, department_id, salary * 12 * (1 + IFNULL(commission_pct,0)) AS 年薪

FROM employees

ORDER BY 年薪 DESC, last_name ASC

#案例8 选择工资不在8000到17000的员工的姓名和工资,按工资降序

SELECT last_name, salary            3

FROM employees                        1

WHERE salary NOT BETWEEN 8000 AND 17000    2

ORDER BY salary DESC                  4

#案例9 查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序

SELECT *,LENGTH(email)

FROM employees

WHERE email LIKE '%e%'

ORDER BY LENGTH(email) DESC, department_id ASC

#进阶4 常见函数

/*

概念:类似于java的方法,将一组逻辑语句封装再方法体中,对外暴露方法名

好处:1.隐藏了实现细节 2、提高代码的重用性

调用 select 函数名(实参列表) 【from 表】

特点:

1、叫什么(函数名)

2、干什么(函数功能)

分类:

1、单行函数

如concat、length、ifnull等

2、分组函数

功能:做统计使用,又称为统计函数、组函数

*/

#一、字符函数

#length 获取参数值的字节个数

SELECT LENGTH('john')

SELECT LENGTH('张山峰hahaha')

#2.concat 拼接字符串

SELECT CONCAT(last_name, '_', first_name) AS 姓名 FROM employees

#3.upper、lower 大小写

SELECT UPPER ('john')

SELECT LOWER ('joHn')

#示例:将姓名大写、名变小写,然后拼接

SELECT CONCAT (UPPER(last_name), '_', LOWER(first_name)) AS 姓名 FROM employees

#4.substr、substring 截图字符

#注意:索引从1开始

SELECT SUBSTR('李莫愁爱上了陆展元', 7) out_put

#截取从指定索引处指定字符长度

SELECT SUBSTR('李莫愁爱上了陆展元', 1, 3) out_put

#案例:姓名中首字符大写,其他字符小写,然后用_拼接,显示出来

SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)), '_', LOWER(SUBSTR(last_name,2))) AS out_put

FROM employees

#5.instr 返回子串第一次出现的索引,如果找不到返回0

SELECT INSTR('杨不悔爱上了殷六侠', '殷六侠') AS out_put

#6/trim 去掉前后空格

SELECT LENGTH(TRIM('    张翠山    ')) AS out_put

SELECT TRIM('aa' FROM 'aaaaa张翠山aaaaaaaa') AS out_put

#7. lpad 用指定的字符实现左填充指定长度

SELECT LPAD('殷素素', 10, '*') AS out_put

#8. rpad 用指定的字符实现右填充指定长度

SELECT RPAD('殷素素', 10, 'ab') AS out_put

#9. replace

SELECT REPLACE('张无忌爱上了周芷若', '周芷若', '赵敏') AS out_put

#二、数学函数

#round 四舍五入

SELECT ROUND(1.65)

SELECT ROUND(-1.65)

SELECT ROUND(-1.657,2)

#ceil 向上取整,返回 >=该参数的最小整数 2-1

SELECT CEIL(1.002)

SELECT CEIL(1.00)

#floor 向下取整,返回小于等于该参数的最大整数 9

SELECT FLOOR(9.99)

#truncate 截断,后面是什么,都不要1.6

SELECT TRUNCATE(1.65,1)

#mod 取余 1 -1

/*

mod(a,b) : a-a/b*b

mod(-10,-3) : -10-(-3) * (-3) = -1

*/

SELECT MOD(10,3)

SELECT MOD(-10,-3)

SELECT 10%3

#三、日期函数

#now 返回当前系统日期时间

SELECT NOW()

#curdate 返回当前系统日期,不包含其他

SELECT CURDATE()

#curtime 返回当前时间,不包含日期

SELECT CURTIME()

#可以获取指定的部分、年、月、日、小时、分钟、秒

SELECT YEAR(NOW()) AS 年

SELECT YEAR('1998-01-02') AS 年

SELECT YEAR (hiredate) AS 年 FROM employees

SELECT MONTH(NOW()) AS 月

SELECT MONTHNAME(NOW()) AS 月

#str_to_date:将日期格式的字符转换成指定格式的日期

#select datediff(year, '1991-05-31', getdate())   计算两个日期的差值

#获取日期的某部分的值

datepart(year, getdate())

year(getdate())

#返回日期的某部分,字符串表示形式,datepart()是数字表示形式

/*

%Y      四位的年份

%y 两位的年份

%m 月份(01,02..)

%c 月份(1,2...)

%d 日(01,02...)

%H 小时(24小时)

%h 小时(12小时)

%i 分钟(00,01,,,59)

%s 秒(00,01...59)

*/

SELECT STR_TO_DATE('1998-03-02', '%Y-%c-%d') AS out_put

#查询入职日期为1992-4-3的员工信息

SELECT * FROM employees WHERE hiredate = '1992-4-3'

SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y')

#案例:查询出入职n年的人的个数

select 工龄=datediff(year, JoinDate, getdate()), count(*) as 人数 

from exam_emps

group by datediff(year, JoinDate, getdate())

select 工龄=datediff(day, JoinDate, getdate()) / 365,

 count(*) as 人数 

from exam_emps

group by datediff(year, JoinDate, getdate())

#案例:查询出,不同年份入职的员工的个数

select 

入职年份=year(JoinDate),

count(*) as 人数 

from exam_emps

group by year(JoinDate)

#案例:输出所有数据中通话时间最长的5条记录。

select top 5

* , '通话时长(秒)'= datediff(second, StartDateTime, EndDateTime)

from CallRecouds

order by  '通话时长(秒)' desc

#案例:输出所有数据中拨打长途号码(对方号码0开头)的总时长

select 

总时长 = sum(datediff(second, StartDateTime, EndDateTime))

from CallRecouds

where TelNum like '0%'

#案例:输出本月通话时长最多的前三个呼叫员的编号

select top (3) 

呼叫员编号 = callerNumber,

通话时长=sum(datediff(second, StartDateTime, EndDateTime))

from CallRecouds

where datediff(month, StartDateTime, '2010-07-31') = 0 --表示是本月

group by  CallerNumber

order by 通话时长 desc

#案例:输出本月拨打电话次数最多的前三个呼叫员的编号

select top (3) 

callerNumber,

拨打电话次数=count(*)

from CallRecouds

where datediff(month, StartDateTime, '2010-07-31') = 0 --表示是本月

group by  CallerNumber

order by 拨打电话次数 desc

# date_format 将日期转换成字符

SELECT DATE_FORMAT(NOW(), '%y年%m月%d日') AS out_out

#查询有奖金的员工名和入职日期(xx月/xx日 xx年)

SELECT last_name, DATE_FORMAT(hiredate, '%m月/%d日 %Y年') FROM employees WHERE commission_pct IS NOT NULL

#四、其他函数 P48

#版本号

SELECT VERSION()

#查看当前的数据库

SELECT DATABASE()

#查看当前用户

SELECT USE()

#分组函数

/* 

聚合函数一般结合分组使用,进行分组内的数据进行统计,根据指定列进行分组

分组后条件筛选:having...

当使用了分组语句(group by)或者是聚合函数的时候,再select的查询列表中不能再包含其它的列名,除非该列同时也出现再了 group by 字句中,或者该列也包含在了某个聚合函数中

select distinct top n *                    5

from t1 join t2 on ... join t3 on ...   1

where ...                                        2   where是对每条进行筛选,不能使用聚合函数

group by ...                                     3

 having...                                           4   having是对分组后的每一组数据进行筛选

order by ...                                        6

*/

select  班级id = scalssid, 

           男同学人数=conut(*) 

            年龄 = tsage 【这个没出现在group by 子句中,所以不能显示】

from tblStudent 

WHERE tsgender = '男' 

group by tsclassid

# having 对分组以后的数据进行筛选

--having与where都是对数据进行筛选,where是对分组前的每一行数据进行筛选,而having是对分组后的每一组数据进行筛选

SELECT 语句的处理顺序

1    FROM    

2    JOIN

3   ON

4    WHERE

5    GROUP BY

6    WITH CUBE 或 WITH ROLLUP

7    HAVING

8    SELECT

9    DISTINCT

10    ORDER BY

11    TOP

#案例:请统计销售总价超过3000元的商品名称和销售总价,并按照销售总价降序排序

select

商品名称

销售总价 = sun(销售数量 * 销售价格)

FROM MyOrders

group by 商品名称

having sun(销售数量 * 销售价格) 》 3000

order by 销售总价 desc

# 案例:统计各个客户对‘可口可乐’的喜爱度(即统计每个购买人对‘可口可乐’的购买量)

select 

购买人,

购买可口可乐数量=sum(销售数量)

FROM MyOrders

where 商品名称 = '可口可乐'

group by 购买人

order by 购买可口可乐数量 dese

子查询

select 

sum(T.销售数量) 喜爱度,

T.购买人 客户

from (select * from MyOrders where 商品名称 '可口可乐') as T

group by T.购买人

order by 喜爱度 desc

#类型转换

--cast (表达式 as 数据类型)

--convert(数据类型, 表达式)

select 100.0 + cast('1000' as int)

select 100.0 + convert(int, '1000')

#联合,union

/*

连接(join)是连接列,联合(union)是把行连接起来

基本原则:每个结果集必须有相同的列数,每个结果集的列必须类型兼容

联合:将多个结果集合并成一个结果集,union去除重复,相当于默认应用了distinct,union all

*/

#使用union all联合结果集

select tsname, tsgender, tsage from TblStudent

union all

select fname, fgender, fage from MyStudent

--使用union和union all 都能进行联合,区别在于:使用union联合会去除重复、重新排列数据,而union all不会去除重复,也不会重新排列

#使用union联合结果集

select tsname, tsgender, tsage from TblStudent

union 

select fname, fgender, fage from MyStudent

--大多数情况下,联合的时候不需要去除重复,同时要保持数据的顺序,所以一般建议使用union

#案例:从MyOders表中统计每种商品的销售总价,并且底部做汇总

select

商品名称,

销售总价 = sum(销售价格*销售数量)

from MyOrders

group by 商品名称

union all

select `总销售价格`, sum(销售价格 * 销售数量) from MyOrders

order by 销售总价 asc

使用union向表中插入多条数据

insert into TblStudent

select '赵宇星', '男', '北京', '13168887498', 18, '1990-09-19', '1111223435435435435',3

union all  不会去除重复

select '赵宇星1', '男', '北京', '13168887498', 18, '1990-09-19', '1111223435435435435',3

内连接:只列出匹配的记录,只显示那些两张表中可以匹配的数据

左外连接:左表列出全部,右表只列出匹配的记录。出现在left join左边的表就叫左表,出现在left join右边的表就叫右表

右外连接:右表列出全部,左表只列出匹配的记录。

--查询数据时,当需要将多个表中的列共同显示到一个结果集中的时候,可以使用连接查询

select  * from PhoneNum(外键) inner join PhoneType(主键) or  PhoneNum.pTypeId=PhoneType.ptid

CASE

表中有ABC三列,用SQL语句实现:当A列大于B列时选择A列否则选择B列,当B列大于C列时选择B列,否则选择C列

--在订单列表中,统计每个销售员的总销售金额,列出销售员名、总销售金额、称号(>6000金牌,>5500银牌,>4500铜牌,否则普通)

select 

X=case

        when A>B then A

        else B

end,

    Y=case

        when B > C then B

        else C

   end,

from TestA

索引Index

--1.索引的目的:提高查询效率

--2.索引分两种

--2.1聚集索引(物理),一个表中只能有一个聚集索引

--2.2非聚集索引(逻辑),一个表中可以有多个非聚集索引。

--3.增加索引后,会增加额外的存储空间。同时降低了增加新记录,修改,删除的效率。

预读取、物理读取、逻辑读取

    -Sql Server 存取数据都是以页为单位(每页总大小8KB)

    -逻辑读取:从缓存中读取数据

    -物理读取:从磁盘中读取数据

    -预读取:一种性能优化机制(在执行查询时先预测执行“查询计划”所需的数据和索引页。然后在查询实际使用这些页之前将它们读入缓冲区高速缓存)

---------------------子查询----------------

--查询高二二班的所有学生,所有子查询都可以改成相关子查询实现

select * from TblStudent where tSClassId=

(select tClassId from TblClass where tClassName='高二二班')

// 相关子查询

select * from TblStudent as ts where 

exists(select * from TblClass as tc where ts.tSClassId=tc.tClassId and tc.tClassName='高二二班')

exists(BOOL值),只要这个里面是否查到了数据,就为真,返回这条数据 select * from tStudeng where 1=1

SELECT * FROM (SELECT * FROM Student where sAge < 30) as t

SELECT 

    (select max(tenglish) from tblscore),

    (select min(tenglish) from tblscore),

    (select avg(tenglish) from tblscore)

select * from TblStudent where tsclassid in【=】 (select tclassid from TblClass),=只能有一个值

select * from student where sClassId in (select cId from class where cName='高一一班' or cName='高二二班') 

select * from student where exist (select * from where (cName='高一一班') or cName='高二二班') and class.cid=student.sclassid)

----------- 分页查询 -----------

每页显示7条数据,首先要确认按照什么排序,然后才能确定哪些记录应该再第一页,哪些记录应该再第二页

select top 7 * from Customers order by CustomerID asc  // 第一页

--第二页:先查询出已经看过前一页的数据(2-1)也的数据的customerID,在查询出不在没看过的记录,排序,多少条

select top 7 * from Customers where CustomerID not in

(select top (7 * 2-1) CustomerID from Customers order by CustomerID asc)

order by CustomerID asc

---------- 使用row_number()实现分页 ----------

--1.为数据排序,然后编号,编号的时候,按照指定的规则编号

select *, Rn=row_number() over(order by CustomerId asc) from Customers

--2.根据用户要查看的每页记录条数,以及要查看第几页,确定应该查询第几条到第几条

--每页显示7条,要查看第8页  (8-1) * 7 + 1... 8*7

select * from (elect *, Rn=row_number() over(order by CustomerId asc) from Customers) as t

where t.Rn between (8-1) * 7 + 1 and 8*7

操作符 BETWEEN ... AND 会选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。

--请查询出那些所有没有参加考试的同学的tsid姓名和年龄

通过子查询实现:

select * from student where tsid not in (select tsid from soure)

--通过连接查询实现,应该通过左外联实现

select t1,* t2,* from Tstudent as t1 left join Tscore as t2 on t1.tsid = t2.tsid where t2.tscoureId is null

--通过右外联来实现

select t1,* t2,* from Tscore as t2 right join Tstudent as t1  on t1.tsid = t2.tsid where t2.tscoureId is null

----------使用外连接时需要注意的一个问题 --------------

select t1,* t2,* from Tstudent as t1 left join Tscore as t2 on t1.tsid = t2.tsid 

where t1.tsgender='男'  --筛选条件要放到where字句当中

--下面的这个写法是有问题的

select t1,* t2,* from Tstudent as t1 left join Tscore as t2 on t1.tsid = t2.tsid and t1.tsgender='男'【false】

--下面这种写法是可以正常显示要查询的数据

select t1,* t2,* from Tstudent as t1 inner join Tscore as t2 on t1.tsid = t2.tsid and t1.tsgender='男'

--当使用连接查询的时候,如果同时要指定查询的条件,那么一定要使用where语句,不要直接在on条件后面跟and来编写其他查询条件

-------------------------

视图概述

--视图是一张虚拟表,它表示一张表的部分数据或多张表的综合数据,其结构和数据是建立在对表的查询基础上

--视图在操作上和数据表没有什么区别,但两者的差异是其本质不同;数据表是实际存储记录的地方,然而视图并不保存任何记录

--相同的数据表,根据不同用户的不同需求,可以创建不同的视图(不同的查询语句)

--视图的目的是方便查询,所以一般情况下不能对视图进行增删改

优点:    

        --筛选表中的行/降低数据库的复杂程度

        --防止未经许可的用户访问敏感数据

create View vw_TblArea

as

select t1.AreaId 城市编号,t2.AreaName 城市名称,t1.AreaName 省份名称 from TblArea as t1

inner join TblArea as t2 on t1.AreaId=t2.AreaPid

--如果视图中的查询语句中包含了重名的列,此时必须为重名的列起别名

select * from vw_TblArea

------------------T-SQL编程------------------

--1、声明变量

declare @name nvarchar(50)

declare @age int 

declare @name nvarchar(50), @age int

--2、为变量复制

set @name = '刘亚龙'

--3、输出

select '姓名', @name

select '年龄', @age

--vhile循环

declare @i int = 1 --声明变量的同时赋值

while @I <= 100

begin

    print 'Hello'

set @I=@I + 1

end

--计算1-100之间的所有整数的和

declare @i int = 1 --声明变量的同时赋值

declare @sum int = 1 --声明变量的同时赋值

while @i <= 100

begin

set @sum=@sum + @i

set @i=@i+1

end

--两个@@符号开头的一般都是系统变量

print @@version

事务-为什么需要事务

如,转账问题:假定钱从A转到B,至少需要两步:

    -A的资金减少

    -然后B的资金相应增加

--怎么保证两条SQL语句同时执行成功或者同事执行失败呢?

--使用事务来保证

update bank set balance=balance-100 where cid = '0002'

update bank set balance=balance+100 where cid = '0001'

--通过事务执行转账

--1.打开一个事务

begin transaction

declare @sum int = 0

--在转账之前最好通过if-else判断,不要让程序发生异常或者错误!

    update bank set balance=balance-100 wehre cid='0002'

    set @sum = @sum+@@error

    update bank set balance=balance+100 wehre cid='0001'

    set @sum = @sum+@@error

--只要有任何一条SQL语句执行出错,那么最后的@sum就不是0

    if @sum<>0

    begin

    --标识程序执行出错了

    --回滚

    rollback

    end

    else

    begin

    --如果没有出错,则提交该事务

    commit

    end

--自动提交事务

--当执行一条sql语句的时候,数据库自动帮我们打开一个事务,当语句执行成功,数据库自动提交事务,执行失败,数据库自动回访事务

--insert into bbbb values(fsd)

--隐式事务

--隐式事务,每次执行一条sql语句的时候,数据库自动帮我们打开一个事务,但是需要我们手动提交事务,或者回滚事务

SET IMPLICIT_TRANSACTIONS(ON | OFF)  隐式事务

--打开隐式事务

SET IMPLICIT_TRANSACTIONS ON

insert into bank values('0003', '1000')

commit 

--显示事务

begin tran

--...

commit tran

rollback transaction

--------- 存储过程 ---------

1.系统存储过程

--1.返回当前实例中的所有的数据库的基本信息

exec sp_database

----------创建自己的存储过程----------

create proc usp_say_hello

as begin

    print 'HELLO WORLD'

end

exec usp_say_hello

drop proc usp_select_tblStudent

alter proc usp_select_tblStudent

create proc usp_select_tblStudent

as begin

    select * from TblStuent where tsgendr='男'

end

--调用存储过程

exec usp_select_tblStudent

--创建一个带两个参数的存储过程

create proc usp_add_number

@n1 int=100 -- 设置参数的默认值

@n2 int=10

as begin

    select @n1 + @n2

end

exec usp_add_number @n2=80

--设置存储过程的参数的默认值

create proc usp_select_tblstudent_by_condition

@gender char(2)

@age int

as begin

    select * from TblStudent where tsage>=@age and tsgender=@gender

end

exec usp_select_tblstudent_by_condition @gender='男',@age=20

----------带输出参数的存储过程----------

--当在存储过程当中需要返回多个值的时候,就可以使用输出参数来返回这些值

create proc usp_show_students

@gender char(2)

@recordcount int output --输出参数

as

begin

    select * from MyStudent where fgender=@gender

    --把查询语句查询到的记录的条数赋值给变量@recordcount

    set @recordcount=(select count(*) from MyStudent where fgender=@gender)

end

--调用存储过程

--调用带有输出参数的存储过程的时候,需要定义变量,将变量传递给输出参数,在存储过程中使用的输出参数,其实就是你传递进来的变量

declare @rc int

exec usp_show_students @gender='男', @recordcount=@rc output

print @rc

---------使用存储过程编写一个分页查询----------

go 

create proc usp_getMyStudentsDataByPage

@pagesize int = 7, --每页记录条数

@pageindex int = 1, --当前要查看第几页的记录

@recordcount int output, -- 总的记录的条数

@pagecount int out put, --总的页数

as

begin

    --1.编写查询语句,把用户要的数据查询出来

    select t.fid, t.fname,t.gage, t.fmath, t.classid, t.fbirthday

    from(select *, rn=row_number() over(order by fid asc) from MyStudent) as t

    where t.rn between(@pageindex-1)*@pagesize+1 and @pagesize*@pageindex

    --2.计算总的记录条数

    set @recordcount=(select count(*) from MyStudent)

    --3.计算总页数

    set @pagecount=ceiling(@recordcount*1.0/@pagesize)

end

declare @rc int , @pc int

exce usp_getMyStudentsDataByPage @pagesize=7, @recordindex=1, @recordcount=@rc output,

pagecount=@pc output

print @rc

print @pc

相关文章

  • sqlP39-P48

    #进阶3:排序查询 /* #引入 SELECT * FROM employees 语法:执行顺序 select 查...

网友评论

      本文标题:sqlP39-P48

      本文链接:https://www.haomeiwen.com/subject/lzeydltx.html