create table productCategory(
cid int identity(1,1) primary key, --自增,主键约束
cname varchar(50) not null -- 非空约束
)
create table product(
pid int identity(10000,1) primary key, --自增,主键约束
pname nvarchar(max) not null, -- 非空约束
barCode char(13) unique, --唯一约束
canUse char(2) default '是', --默认值约束
unitPrice money check(unitPrice>=0), --检查约束
pcid int,
foreign key(pcid) references productCategory(cid) --外键约束
)
添加:
完整写法:
insert into Product(pname,barCode,unitPrice,pcid)
values('macBook','4949389483923',8888,2),
('iPhone11','4949389183223',7888,1),
('三星耳机','4939383483923',288,3)
省略写法:
insert into Product
values('红米笔记本','459998974533','否',4999,2)
--或者
insert into ProductCategory
values('平板'),('耳机')
省略写法即省去“列名”,但必须保证 values 中的值和表中的列一一对应起来
获取自增值:
insert into Product
values('红米笔记本','459998974533','否',4999,2)
select @@IDENTITY -- 获取自增值
修改:
删除:
内连接
-- 内连接
select * from Product p inner join ProductCategory pc on p.pcid=pc.cid
外连接
完全连接
交叉连接(笛卡尔乘积)
声明变量:declare @变量名 数据类型 declare @i int
分支结构:判断@i的值
declare @i int
set @i=10
if(@i>10)
begin
print 'i的值大于10'
end
else
begin
print 'i的值小于等于10'
end
循环结构:求1-100之间所有数字之和
declare @i int, @sum int
set @i=1;
set @sum=0
while(@i<=100)
begin
set @sum = @sum + @i;
set @i = @i + 1;
end
print @sum
--判断为null函数
declare @abc varchar(5)
set @abc='mnw'
select isnull(@abc,0); --如果变量abc值为null,则取0,否则取被赋的值
--查找索引函数
select charindex('abc','bac abc fmd abc'); --获取字符串abc在长字符串bac abc fmd abc中的索引位置,索引默认从1开始找
select charindex('abc','bac abc fmd abc',9); --索引从9开始找
--重复获取函数
select replicate('m',3); --按指定次数重复字符表达式,将m字符重复三次
select replicate('abc',2);--将字符串abc重复2次
--大小写转换函数
select len('datazsrt'); --获取字符串长度
select lower('ADRTddd'); --转小写
select upper('data');--转大写
--替换截取函数
select substring('abcdef111',2,3); --截取字符串
select replace('dadt123','t1','AA'); --替换字符串
--类型转换函数
select cast('2015-08-02' as datetime) as 日期类型; --数据类型转换,CAST是ANSI兼容的,而CONVERT则不是
select 'ab'+cast(1 as varchar);-- 将1转化为字符串与ab相加
select convert(datetime,'2015-08-02'); --数据类型转换,与cast相同
select convert(varchar(19),getdate(),113); --不同的是convert还提供一些特别的日期格式转换,而cast没有这个功能
select format(getdate(),'yyyy/mm/dd'); --将日期转化为规定的格式
--数学函数
select round(2.15,1); --四舍五入,第二个参数索引从0开始,即:0表示小数后第一位进行判断
select ceiling(2.158); --向上取整
select floor(2.713); --向下取整
--日期函数
select getdate();
select datepart(mm,'2015-08-04 15:28:26'); --返回日期的某一部分 yy,mm,dd,hh,mi,ss,
select dateadd(dd,-25,'2015-08-04 15:28:26'); --在日期中加上或减去制定的时间间隔,给天数减25天
select datediff(day,'2008-06-05','2008-08-05') as 天数; --返回两个日期之间的天数
--聚合函数
count(),min(),max(),avg(),sum()
标量值函数:返回值为一个数据点的函数被称为标量值函数
表值函数:返回值为一个结果集的函数
内连表值函数:只有一个查询语句
表变量:将查询的结果集临时存起来可以使用表变量
-- 三个一起运行才行,否则@i没声明
declare @t table(id int identity(1,1),name varchar(50))
insert into @t values('张三'),('李四')
select * from @t
多语句表值函数:多个语句
create function GetPrice(@cid int)
returns @t table(pid int, productName varchar(50), price money)
as
begin
insert into @t select pid, pname, unitPrice from product where pcid=@cid
update @t set price=price+1000
return
end
select * from GetPrice(5)
小节:
任务:创建一个简单的视图
create view product_category_view
as
select * from product p join productCategory pc on p.pcid=pc.cid
select * from product_category_view
任务:带别名的视图(别名要和查到的列一一对应)
create view product_category_view2(商品编号,商品名称,条形码,是否上架,单价,分类)
as
select p.pid,p.pname,p.barCode,p.canUse,p.unitPrice,p.pcid from product p join productCategory pc on p.pcid=pc.cid
使用视图更新数据
定义视图的 SELECT 语句不能包含以下任何元素:
聚合函数; distinct 子句;
group by 子句; having 子句;
union 和 union all 子句; 外连接
任务:将product_category_view视图里面的单价都全部+100
-- 有别名的视图
update product_category_view2 set 单价=单价+100 where 分类=5
-- 没有别名的视图
update product_category_view set unitPrice=unitPrice+100 where pcid=5
当视图里面的数据发生改变以后,原数据也会改变
使用视图删除数据
查看视图
-- 查看所有表
select * from sys.tables
-- 查看所有视图
select * from sysobjects where xtype='V'
删除视图
drop view product_category_view2
优点:
简单性
安全性
逻辑数据独立性
本表修改后, 视图的使用者不需要关心(甚至根本不知道)
视图或者原表进行修改,都会修改原数据
缺点:
create proc proc_product_insert
@pname varchar(50), @bcode char(13), @uprice money, @cid int, -- 输入参数
@id int output -- 输出参数
as
insert into product(pname,barCode,unitPrice,pcid) values(@pname,@bcode,@uprice,@cid)
set @id=SCOPE_IDENTITY() -- 获得自增值,等同于:set @id=@@identity
declare @id int
exec proc_product_insert '小米13','2938293849382',4999,1,@id output
print @id
alter proc proc_product_insert
@pname varchar(50), @bcode char(13), @uprice money, @cid int, --输入参数
@num int output -- 输出参数
as
insert into product(pname,barCode,unitPrice,pcid) values(@pname,@bcode,@uprice,@cid)
set @num=@@ROWCOUNT -- 获取受影响行数
drop proc proc_product_insert
每个事务以 begin tran 打头,以 commit 或 rollback 结束。
任务:了解事务保存点的使用
-- 开启事务 一行一行执行
begin tran myTran
insert into productCategory values('鼠标'),('键盘')
save tran sr -- 设置一个保存点sr
delete from productCategory -- 删除表中所有的数据
select * from productCategory
rollback tran sr -- 回滚到sr保存点
commit tran myTran -- 提交整个事务
go
select * from productCategory
go
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | 可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
可串行化 | 不可能 | 不可能 | 不可能 |
表结构和数据:
create table tb_test(
id int identity(1,1) not null primary key,
text varchar(200) default null,
)
insert tb_test values('first row'),('second row'),('third row')
-------------------------------------------------------------------------------
未提交读(READ UNCOMMITED):
A会话:
begin tran
update tb_test set text='1 row' where id=1
B会话:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRAN;
select * from tb_test where id=1;
A会话:
rollback
B会话:
select * from tb_test where id=1;
此时会发现B会话可以拿到A未提交的数据,即“脏数据”,如果拿着脏数据去参与运算,肯定会发生错误。
并且发现在B会话中两次查询的结果不一致,即“不可重复读”
接下来演示“幻读”,幻读即“两次查询的结果的行数不一致”
B会话:
commit;
A会话:
begin tran
insert tb_test values('forth row')
B会话:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRAN;
select * from tb_test;
A会话:
rollback
B会话:
select * from tb_test;
commit;
此时发现B会话拿到了A会话中添加的行,但是A会话最终回滚了该行数据,此类虚幻的数据即“幻读”
-------------------------------------------------------------------------------
已提交读(READ COMMITTED):
B会话:
select * from tb_test; -- 观测数据
B会话:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRAN
SELECT * FROM tb_test
A会话:
BEGIN TRAN
UPDATE tb_test SET TEXT='1 row' where id=1;
B会话:
SELECT * FROM tb_test
此时会发现查询阻塞
A会话:
commit
此时B会话查询OK
B会话
commit;
-------------------------------------------------------------------------------
重复读(REPEATABLE READ):
A会话:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN
SELECT * FROM tb_test
B会话:
update tb_test set text='3 row' where id=3
此时发现B会话阻塞,原因是A会话使用了REPEATABLE READ,致使tb_test在提交事务之前被独占。
A会话:
commit
此时观察B会话,提示修改成功
----------------------------------------------------------------------------
可串行化(SERIALIZEABLE):
可串行化隔离级别最高,不会出现脏读、幻读、不可重复读,但是由于独占性太高,会显著阻塞数据库并发性能。
DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR
FOR select_statement
[FOR {READ ONLY | UPDATE [OF column_name [,…n]]}]
INSENSITIVE:如果指定此值,则会把查询结果集存放在 tempdb 数据库的临时表中,通过游标的操作对应的便是临时表中
的结果集而非源表的内容,其他用户对源表中数据的变更无法反映出来。
SCROLL:使用此值定义一个可在结果集中滚动的游标,具有以下功能:first:取首行、last:取尾行、prior:取前一行、next:取后一行、relative:按相对位置取、absolute:按绝对位取。如果没有指名 scroll 则游标是只进的,也就是说只能通过 next 来向后取下一行数据。
select_statement:一些标准的 Sql 语句,但不允许使用 compute 和 into 等关键字。
READ ONLY:只读,不允许通过游标更新数据。
UPDATE [ OF column_name [ ,…n ] ]:定义在这个游标里可以更新的列。
游标通常情况下,只能一次从 DB 中提取一条记录,我们最常见的操作就是从第一条记录开始提取,直到结束。如下示例代码所示:
DECLARE Employee_Cursor CURSOR
……
FETCH NEXT FROM Employee_Cursor;
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM Employee_Cursor;
END;
……
GO
@@FETCH_STATUS 是一个全局变量,当其值如下:
任务:
5000元以下的iPhone,每件商品提价 10%;
5000元以上的iPhone,每件商品提价 5%
declare @price money, @name varchar(20) -- 定义变量
declare cur_ChangePrice cursor -- 定义游标
for select pname, unitPrice from Product -- 定义游标中的SQL语句
for update of unitPrice -- 修改
open cur_ChangePrice -- 打开游标
fetch next from cur_ChangePrice into @name, @price -- 游标移动到下一行
while @@FETCH_STATUS=0 -- 读取下一行是否成功,0是成功的状态码
begin
if(@price<5000 and Charindex('iPhone',@name)>0) -- 逻辑判断
update Product set unitPrice=unitPrice*1.1 where current of cur_ChangePrice
if(@price>=5000 and Charindex('iPhone',@name)>0) -- 逻辑判断
update Product set unitPrice=unitPrice*1.05 where current of cur_ChangePrice
fetch next from cur_ChangePrice into @name, @price -- 游标移动到下一行
end
close cur_ChangePrice -- 关闭游标
deallocate cur_ChangePrice -- 释放游标
select * from product
触发器触发时:
1.系统自动在内存中创建deleted表或inserted表;
2.只读,不允许修改,触发器执行完成后,自动删除。
inserted表:
1.临时保存了插入或更新后的记录行;
2.可以从inserted表中检查插入的数据是否满足业务需求;
3.如果不满足,则向用户发送报告错误消息,并回滚插入操作。
deleted表:
1.临时保存了删除或更新前的记录行;
2.可以从deleted表中检查被删除的数据是否满足业务需求;
3.如果不满足,则向用户报告错误消息,并回滚插入操作。
如果被修改的表的列为null值的话则不执行修改语句
CREATE TRIGGER trigger_name
ON table_name
[WITH ENCRYPTION]
FOR | AFTER | INSTEAD OF [DELETE, INSERT, UPDATE]
AS
T-SQL语句
GO
--with encryption 表示加密触发器定义的sql文本
--delete,insert,update指定触发器的类型
表结构:
--创建学生表
create table student(
stu_id int identity(1,1) primary key,
stu_name varchar(10),
stu_gender char(2),
stu_age int
)
--创建班级人数表 被修改的列必须不能为null,否则不执行
create table student_num(
stu_num int default 0
)
创建insert触发器
create trigger tri_insert -- 创建触发器
on student -- 在student表上
after insert -- 在insert之后
as
begin
declare @num int --声明变量
set @num = (select count(*) from student) -- 获得学生表总条数
update student_num set stu_num=@num -- 给student_num赋值
end
insert into student values('张三','男',18)
select * from student_num
创建delete触发器
用户执行delete操作,就会激活delete触发器,从而控制用户能够从数据库中删除数据记录,触发delete触发器后,用户删除的记录会被添加到deleted表中,原来表的相应记录被删除,所以在deleted表中查看删除的记录。
create trigger tri_delete -- 创建触发器
on student --在student表上
after delete -- 在删除之后
as
begin
--从系统生成的delete表中获取已删除的信息
select stu_id 已删除的学生编号, stu_name, stu_gender, stu_age from deleted
end
delete from student where stu_id=2
创建update触发器
update触发器是当用户在指定表上执行update语句时被调用被调用,这种类型的触发器用来约束用户对数据的修改。update触发器可以执行两种操作:更新前的记录存储在deleted表中,更新后的记录存储在inserted表中。
create trigger tri_update
on student
after update -- 修改之后
as
begin
declare @num int -- 声明变量
set @num=(select count(*) from student) -- 获得学生表总人数
update student_num set stu_num=@num
select stu_id,stu_name 更新前的名字 ,stu_gender,stu_age from deleted -- 获得更新前的数据
select stu_id,stu_name 更新后的名字 ,stu_gender,stu_age from inserted -- 获得更新后的数据
end
update student set stu_name='王五' where stu_id=3
与前面介绍的三种after触发器不同,SqlServer服务器在执行after触发器的sql代码后,先建立临时的inserted表和deleted表,然后执行代码中对数据库操作,最后才激活触发器中的代码。而对于替代(instead of)触发器,SqlServer服务器在执行触发instead of 触发器的代码时,先建立临时的inserted表和deleted表,然后直接触发instead of触发器,而拒绝执行用户输入的DML操作语句。instead of触发器一般用来验证数据完整性,拥有比check约束更强大的功能。
create trigger tri_insteadof
on student
instead of insert -- 创建添加的代替触发器
as
begin
declare @age int -- 声明变量
set @age=(select stu_age from inserted) -- 从inserted表中查出刚添加的年龄,赋值给age
if(@age<0 or @age>120) --如果刚添加的年龄<0或>120
select '年龄有误'
end
insert into student values('李四','男',150) -- 年龄有误,添加失败
任务:将所有商品价格+1000
update product set unitPrice=unitPrice+1000
< = >= <=
任务:查询价格大于5000的商品信息
select * from product where unitPirce>5000
<>,!= 不等于
任务:查询价格不等于5000的商品信息
select * from product where unitPrice != 5000
! 非
and or not
任务:查询价格大于3000并且小于5000的商品信息
select * from product where unitPrice>3000 and unitPrice<5000
‘_’表示一个字符
‘%’表示0-n个字符
[] 表示括号中所指定范围内的一个字符
[^] 表示不在括号中所指定范围内的任意一个字符
训练
--查询一下名字当中含有王字的学生信息
select * from student where name like '%王%'
--查询一下名字姓王的学生信息
select * from student where name like '王%'
--查询一下名字姓王的学生信息并且名字是两个字
select * from student where name like '王_'
--查询一下名字姓王并且名字是三个字,并且第三个字是"三"或者"四"或者"五"或者"六"或者"七"
select * from student where name like '王_三'
or name like '王_四'
or name like '王_五'
or name like '王_六'
or name like '王_七'
select * from student where name like '王_[三四五六七]'
--查询一下名字姓王并且名字是三个字,并且第三个字不是三或者四或者五或者六或者七
select * from student where name like '王_[^三四五六七]'
NULL值要用特殊的关键字IS 或IS NOT,而不能用!=或<>之类的符号
任务:查询谁的姓名为null
select * from student where stu_name is null
任务:查询一下年龄是19,22,24的学生信息
select * from student where stu_age in (19,22,24)
可以用 ALL 或 ANY 关键字修改引入子查询的比较运算符。SOME 是与 ANY 等效的
创建两张表
create table a(
num int
)
create table b(
num int
)
-----随意添加几条测试数据
any/some
例如:num>=(1,2,3) 只要年龄大于其中的某一个值就成立
任务:查询a表中的哪些数据比b表中某些数据值要大
select * from a where num > any(select * from b)
=ANY 运算符与 IN 等效
all
任务:查询a表中的哪些数据比b表中的数据值都要大
select * from a where num > all(select * from b)
< >ANY 运算符则不同于 NOT IN
总结
< >ANY(a,b,c) 表示不等于 a,或者不等于 b,或者不等于 c
NOT IN 表示不等于 a、不等于 b 并且不等于 c
<>ALL 与 NOT IN 表示的意思相同
大白话
--a表中的哪些数据可以把b当中的其中一个数据干掉那你就赢了
select num from a where num > any(select num from b)
--a表中的哪些数据可以把b当中的每一个数据都干掉那你就赢了
select num from a where num > all(select num from b)
任务:查询出年龄介于20到22之间
select * from student where stu_age>=20 and stu_age<=22
-- 或
select * from student where stu_age between 20 and 22
任务:查询出年龄不介于20到22之间的学生数据
select * from student where stu_age<20 or stu_age>22
-- 或
select * from student where stu_age not between 20 and 22
任务:查询年龄最大的三个人
select top(3) * from student order by stu_age desc
任务:查询年龄排名是4,5,6人的
--任务:查询年龄排名是4,5,6人的
--1.我们可以先查询出年龄最大的三个人
--2.然后排除这三个人进行查询
--最牛逼的三个人都走了,剩下的4,5,6是不是就是123名了
select top(3) * from student order by age desc
select top(3) * from student where id
not in (select top(3) id from student order by age desc )
order by age desc
一般是与ORDER BY连用,而且TOP 必须放在*型号或者列名的前面。不和ORDER BY也可以使用,但是查询结果不确定
任务:查询学生性别有几种
select distinct stu_sex from student
备注:只能查询列的结果集。DISTINCT要放在SELECT之后,和所有列名的前面
CREATE TABLE OrderDetails
(
ListPrice money NOT NULL,
Quantty int NOT NULL,
LineItemTotal AS (ListPrice * Quantty) PERSISTED
)
GETDATE()
select getdate()
DATEADD(datepart, number, date)
datepart:计量单位(year、quarter季度、
month、day、week、hour、minute、second等)
number:增量(正数为:加时间 负数为:减时间)
-- 当前时间增加两季度
select DATEADD(quarter,2, GETDATE())
-- 指定时间增加两季度
select DATEADD(quarter, 2, '2020-01-01')
FORMAT(value,format)
yyyy-MM-dd hh:mm:ss【格式写法和java一样】
select FORMAT(GETDATE(),'yyyy年MM月dd日 HH时mm分ss秒')
DATEDIFF(datepart, startdate, enddate)
select DATEDIFF(day,'2002-02-13',GETDATE())
DATEPART(datepart,date)
-- 获取当前星期几
select DATEPART(WEEKDAY,GETDATE()-1)
-- 因为美国人1代表星期日,我们可以上面减一。
-- 也可以修改@@datefirst的值,例如:set datefirst 1
set datefrist 1
拓展
DATENAME(datepart,date)
-- 获取当前星期几 不用-1 结果是:星期日
select DATENAME(WEEKDAY,GETDATE())
获取哪个国家的语音
select alias, * from master..syslanguages
数据显示用什么语言
set language N'Simplified Chinese'
EOMONTH(GETDATE(),-2)
select EOMONTH(GETDATE(),-2)
获取08年二月【必须也得写个日,不然报错】
select EOMONTH('2008/2/4')
获取当前时间-3个月
select EOMONTH(GETDATE(),-3)
style 规定日期/时间的输出格式(可选)
例如:
-- 将后面的类型转换成前面
select CONVERT(datetime,'2012-12-12')
-- 将后面的类型转换成前面的然后以特定格式显示
select CONVERT(nvarchar(100),GETDATE(),21)
-- 将当前日期转换为date类型然后加1天
select DATEADD(day,1,CONVERT(DATE,GETDATE()))
select CONVERT(varchar, GETDATE(), 112)
parse(string_value AS data_type)
-- 执行成功 值为123
select parse('123' AS int)
-- 执行成功 值为null 如果失败则为null
select TRY_PARSE('123a' AS int)
例如:
select iif(1>2,'张三','李四')
例如:
create table stus(
id int identity(1,1) primary key,
name varchar(50)
)
insert into stus(name) values('张三'),(null)
-- 查询所有名字,将null值替换为未知
select isnull(name,'未知') from stus
-- 查询距现在日期,两年前的日期是多少
select DATEADD(year,-2,GETDATE())
-- 查询2000年的2月最后一天的日期
select EOMONTH('2000/2/1')
-- 查询现在为周几
select DATEPART(WEEKDAY,GETDATE()-1)
select DATENAME(WEEKDAY,GETDATE())
-- 查询将字符串202000202转换成日期格式'2020/02/02'
select FORMAT(CONVERT(date,'20200202',111),'yyyy/MM/dd')
-- 查询20080808日距离现在有多少天
select DATEDIFF(day,'2008-08-08',GETDATE())
-- 查询一年后的今天日期为多少
select DATEADD(year,1,GETDATE())
完成任务
根据脚本创建表添加测试数据
create table students(
id int identity primary key,
name varchar(50),
age int
)
insert into students(name,age) values('张三',11),('李四',22),('22',33),(null,77)
查询学生姓名、年龄;把名字当中含有张字的年龄+1,否则年龄-1
-- 查询学生姓名、年龄;把名字当中含有张字的年龄+1,否则年龄-1
select name,IIF(name like '%张%',age+1,age-1) from students
查询学生姓名、年龄;将名字转换为int类型,转换错误时候以null显示
-- 查询学生姓名、年龄;将名字转换为int类型,转换错误时候以null显示
select try_parse(name AS int),age from students
查询学生姓名、年龄;姓名如果为null则显示未知
-- 查询学生姓名、年龄;姓名如果为null则显示未知
select ISNULL(name,'未知'),age from students
千万注意: 在标准SQL中, 查询的列名只能是出现在group by之后的列以及聚合函数
由于MYSQL不是标准SQL, 导致MYSQL中也可以使用未出现在group by后的列, 但是结果不一定正确。
使用Group By进行分组查询(一般出现"每个",“各个”,"分别"等词时都会使用group by)。
任务:查询一下每个班级有多少人
select class,count(*) from students group by class
查询一下男女生学生分别有多少人
select sex,count(*) from students group by sex
多列分组查询
– 任务:按照班级、性别进行分组查询【每个班级的男生、女生各有多少人】
select class,sex,count(*) from students group by class,sex
Having 子句- - 分组查询的条件筛选
任务:查询一下班级人数大于等于2人的班级
select class,count(*) from students group by class having count(*)>=2
任务:在上一个任务基础上,按照人数降序排序
select class,count(*) from students group by class having count(*)>=2 order by count(*) desc
任务:在上一个任务基础上,不用统计103班级
select class,count(*) from students where class != 103 group by class having count(*)>=2 order by count(*) desc
小结
with rollup对分组统计的结果再次统计
select count(*),sex from students group by sex with rollup
测试脚本
--分组查询一下每个班级里面有多少学生
--【分组查询的select 后面显示的列只能是聚合函数或者是被分组的列】
select class,count(*) from students group by class
--我想通过上面分组查询的结果来看到学生的总人数是多少
select class,count(*) from students group by class with rollup
--分组查询一下每个班级里面有多少男生和多少女生
select class,sex,count(*) from students group by class,sex
--我想通过上面分组查询的结果来看到每个班级的总人数、以及学生的总人数
select class,sex,count(*) from students group by class,sex with rollup
select sex,class,count(*) from students group by grouping sets(sex,class)
select clazzId,sex,city,count(*) from student group by grouping sets((clazzId,sex),city)
select row_number() over(order by age desc),* from student
注意over里面的order by和真正sql查询后面的order by排序可以完全不一样
select row_number() over(order by age desc),* from student order by id
rank over子句中排序字段值相同的序号是一样的,后面字段值不相同的序号将跳过相同的排名号排下一个,也就是相关行之前的排名数加一,可以理解为根据当前的记录数生成序号,后面的记录依此类推。
select rank() over(order by age desc),* from student
用法和rank一样,只不过rank如果值相等序号是跳跃式的(1,1,3,4),但是dense_rank会是一直连续的(1,1,2,3)
select dense_rank() over(order by age desc),* from student
优点
缺点
大多数情况下,聚集索引的速度比非聚集索引要略快一些.因为聚集索引的B树叶子节点直接存储数据,而非聚集索引还需要额外通过叶子节点的指针找到数据.
还有,对于大量连续数据查找,非聚集索引十分乏力,因为非聚集索引需要在非聚集索引的B树中找到每一行的指针,再去其所在表上找数据,性能因此会大打折扣.有时甚至不如不加非聚集索引.
因此,大多数情况下聚集索引都要快于非聚集索引。但聚集索引只能有一个,因此选对聚集索引所施加的列对于查询性能提升至关紧要.
create [unique][clustered][nonclustered] index name on table(column)
create nonclustered index name on students(name)