SQL教程
本文是本人学习数据库和SQL时自己做的笔记,属于基本知识,现整理出来放在这里,供自己以后学习和查看。
一、数据库基础知识
1、基础知识
(1)服务型、文件型数据库
- 基本概念
- 数据库(数据库文件)
- 数据库管理系统(DBMS,Database management system)
- 数据库系统
- 数据库管理员(DBA,database administrator)
- 实例:一个数据库管理系统可以运行多个实例,每个实例相互独立
- 服务型、文件型数据库
- 服务型 MS SQLServer
- 文件型 Access
- 工具
- 配置管理器(ConfigurationManager)
- 数据库操作
- 联机事务处理
- 联机分析处理
- 使用数据库
a、使用SSMS:SQL Sever 客户端(不考虑)
b、使用脚本
2、连接数据库
(1) Windows身份验证(能登录windows就能登录数据库)
- Step1:选择数据库服务(保证服务是开启的)
- Step2:能登录windows就能登录数据库
- Step3:打开数据库管理工具SQL Server Management Studio(SSMS)
1) 安装SQL Express版本默认是没有这个程序的
2) 第一次运行的时候会有一个配置 - Step4:选择数据库引擎、添加实例名(重点)、选择身份验证模式
- Step5:连接
(2) 使用SQL Server身份验证
- Step1:使用Windows身份验证登录
- Step2:找到安全性节点,找到sa用户名,右键属性, 修改密码,启用该用户名
- Step3: 找到数据库对象,右键属性,找到安全性选项卡,选择混合模式
- Step4:重新启动服务
3、 创建数据库(2种方法)
(1) 使用SSMS创建数据库(可视化)
在SQL Server中任何可视化行为都是在后台作为SQL语句执行的
(2) T-SQL
use master; --使用主数据库
if exists(select * from sysdatabases where name = 数据库名)
drop database 数据库名;--创建前先判断是否存在
create database 数据库名
on primary --在主文件组
(
name=N'逻辑名_data',--N表明是使用UniCode编码
filename=N'路径\文件名_data.mdf',--物理文件名
size=10MB,
filegrowth=1MB
)
log on--日志文件
(
name=N'逻辑名_log',
filename=N'路径\文件名_log.ldf',
size=10MB,
filegrowth=10%
);--加分号可以提高SQL语句执行的效率
go -- go不是SQL语句中的命令,他是通知数据库将前面的语句脚本一并执行
use 数据库名;
注意:执行SQL语句
先点击勾勾,检查语法
-> 检查的范围是选中的SQL语句,如果没有选中SQL语句,则检查所有的SQL语句
然后点击感叹号执行SQL语句
4、 问题
(1)注释: 使用–表示行注释
使用/* 表示多行注释,这个注释不允许嵌套 */
(2)分号:习惯上每一句SQL语句结束的时候加上一个分号
(3)如果限制最大大小,使用maxsize=值
(4)数据库名(使用数据库时使用)、逻辑名(操作系统逻辑)、文件名(操作系统文件)
(5)创建数据库文件的时候,不要直接放在根目录下(D:\TestDataBase.mdf)
5、修改和删除数据库
(1) 修改数据库
修改数据库时修改数据文件和日志文件的长度,或者增加或删除操作系统文件
Alter database 数据库名
{
…
}
(2) 删除数据库
drop database 数据库名
6、架构schema(2种方法)
(1) 架构属于谁的?
架构是属于数据库的,相当于命名空间,使用架构可以设置用户的访问权限
(2) T-SQL
use 数据库;
create schema 架构名 authorization dbo;
go
7、 数据库的分离与附加
略
二、数据库中表的基本操作
1、创建表
(1)什么是表
- 1) 设计表
- a. 列的名字
- b. 这一列可以存什么类型的数据
- 2) 表节点 -> 右键新建 -> 填写列名和数据类型 -> 保存即可
(2) 使用T-SQL创建表
use 数据库;
if exists(select * from sysobjects where type ='U' AND name = 架构名.表名)
drop table 架构名.表名;--创建前先判断是否存在
create table 架构名.表名
(
字段1 类型1 not null或null,
字段2 类型2 not null或null,
字段3 类型3 not null或null,
...
);
(3) 表中的数据如何描述
1)常用的数据类型
类别 | SQL | 备注 |
---|---|---|
数字类型 | int bigint money numeric bit | |
时间类型 | datetime(date,time) | |
字节数据 | image | |
字符串类型 | 1)基于ASCII编码:char varchar数字代表能存储多少字节 2)基于Unicode编码:nchar nvarchar数字代表能存储多少个字符 |
注意
char(数字)数字可以取0到8000,nchar(数字)可以取0到4000
如果超出这个范围,可以使用varchar(max)这个类型会在单独的一个地方存储,容量在2G左右,或者用nvarchar(max)
char(数字)ASCCII码字符,固定长度的字符,如果输入数据的长度不够末尾补空格
char(10) 1
char(10) 1234567890
一二三四五
优势,插入数据的时候不需要考虑计算
缺点,占用资源
nchar(数字) unicode字符集,结构与char一致
nchar(10) 占十个unicode字符的位置
1234567890
一二三四五六七八九十
varchar(数字) ASCCII编码,使用可变长度,只要不超过数字即可
varchar(10) 1
nvarchar(数字) unicode字符集,使用可变长度
Text和NText 存储大文本数据(在将来的SQL Server不再支持)
varchar(max)和nvarchar(max)
补充:标准中char与varchar的最大长度为8000字节,标准中nchar与nvarchar的最大长度为4000个字符,
但是varchar(max)与nvarchar(max)不一样
2、 主键
(1)什么是主键
唯一区分
(2)逻辑主键
与描述数据无关的一个字段(如额外的添加的自动增长的ID)
(3) 业务主键
与描述数据相关的一个字段(如描述一个人时用身份证号最为主键)
(4)如何创建主键
- SSMS
在表中设计表的时候,在指定字段上右键设为主键 - 使用T-SQL创建主键
在表创建完毕以后,通过修改表,创建主键
alter table 表名
add
constraint PK_表名_字段名 primary key(字段名);
(5)创建表的时候如何使得字段自动增长
- 能够自动增长的一定是整数
- 语法
testId int identity(1,1) not null
identity(1,1) 表示字段的值从1开始,每次添加数据的时候自动增长1个
- 一旦数据添加了自动增长,那么添加数据的时候就不允许手动添加这个值,执行一个设定
set identity_insert 表名 off/on;
3、添加其他约束(一般先创建表,然后添加其他约束)
- 唯一约束(unique):保证数据唯一
- 与主键的一些区别
- 主键定位了表,一张表中只允许有一个主键,但是可以有多个唯一约束
- 主键不允许为空,但是唯一约束可以为空(空也只能有一个)
- TSQL 语法
alter table 表名 add constraint UQ_表名_字段名 unique(字段);
- 通过SSMS添加唯一约束
- 设计表 -> 右键唯一键 -> 添加 -> 右边更改名字 -> 设定字段
- 与主键的一些区别
- 默认约束(default):有些时候需要有默认值
- 用户插入数据的时间
- 软删除(IsDel表示是否删除了)
- T-SQL语法
alter table 表名 add constraint DF_表名_字段名 default(值) for 字段;
- SSMS操作
- 设计表 -> 选中字段 -> 属性选项卡中找到默认值绑定
- 检查约束(check)
- 用来限定输入的数据,检查是否符合某些规则
- 一个人的性别
- 一个人的年龄
- T-SQL
alter table 表名 add constraint CK_表名_字段名 check(表达式); 表达式是一些判断的规则 stuAge>=0 and stuAge<=150 or stuAge is null 连接多个条件使用and和or
- 用来限定输入的数据,检查是否符合某些规则
- 外键约束
- 连接两张表的关系
- 有外键的就是外键表
- 关系是建立在两张表之间的
- 外键约束有什么用?
- 如何创建
- T-SQL
alter table 外键表名 add constraint FK_外键表_主键表_字段 foreign key(外键表中的字段) references 主键表名(主键表中的字段);
- 连接两张表的关系
4、 删除约束
alter table 表名
drop 约束名;
5、 补充:
alter命令表示修改的意思,凡是要修改表的数据都可以使用这个命令
- 添加约束
alter table 表名 add constraint 约束名...
- 删除约束
alter table 表名 drop 约束名;
- 添加一个字段
alter table 表名 add 字段名 类型名;
- 删除字段
alter table 表名 drop column 字段名;
- 修改字段
alter table 表名 alter column 字段名 类型;
- 如果想去查看数据库中有多少个对象
可以使用系统视图sys.objects来实现select * from sys.objects;
- 视图可以看做是一个虚拟的表
- 重命名、删除表
- 重命名数据库表和其中的列
Use 数据库 Go Sp_rename 表 ,新的名称 –重命名数据库表 Sp_rename 表.列名 ,新的列名 –重命名数据库表中的列
- 删除约束
alter table 表名 drop 约束名;
6、 系统的查询语句
select top 数字 percent distinct
字段1
, 字段2
, ...
, 聚合函数
, 表达式
, 常量
from
数据源
where
条件
group by
字段
having
条件
order by
字段;
->执行顺序:From -> where -> group by -> having -> select ->distinct和Top -> order by
-> from子句
是一个查询中最先开始的地方
表示需要查询的数据源是什么
数据源可以是表、结果集、视图等
-> 注意缩进
-> 书写格式
-> where子句
是对数据源进行直接的筛选
-> 直接拿字段进行运算
-> 判断
不等于 <> != !> !<
判断优先级:在判断的时候有多个条件,多个条件使用and与or连接
-> 匹配使用通配符,命令由=改为like
_ 表示任意的一个字符
% 表示任意多个任意字符
stuName like '牛%'; -- cow
-> 如果需要匹配"_"怎么办?
stuName like '[_]'
[] 还可以表示范围 [a-z] [^a-g]
-> 处理范围
stuAge between 19 and 26; 取19岁到26岁之间的人,注意两边取得到
stuAge in (19, 21, 25); 取19岁或21岁或25岁的人
-> 空值处理
-> 任何包含null的表达式,结果都是不知道(不知道的反面还是不知道)
-> 使用is null和is not null进行判断筛选
-> 有时需要显示数据,但是不希望出现null值可以使用ISNULL()函数
-> group by子句
-> 按照什么分组就只能显示什么数据
select后面只能跟你分组的字段
-> 聚合函数,就是将组的数据进行汇总,求出平均、总和、总数、最大和最小
AVG(字段) 求平均
SUM(字段) 求数据和
COUNT(字段) 求总条数
MAX(字段) 求最大
MIN(字段) 求最小
-> 查询出名字出现3次以上的名字
-> having子句
-> having对数据分组后筛选与where都是筛选但操作不同
-> where 直接对数据源进行筛选
-> 补充一下聚合函数的使用
-> 聚合函数默认忽略NULL值
avg(字段)
count(字段)
count(*)
-> 在SQL语句中不能将聚合函数与其他字段写在一起
使用开窗函数(*难点*)
语法
select ..., avg(字段) over() from...
-> select子句
-> 将数据源的字段显示出来
-> select可以用来显示数据
-> 别名
字段 as 列名
字段 列名
列名=字段
-> 关于字段的执行顺序,同一个子句中是同时进行的
select year(getdate()) - year(stuBirthdate) as age, age+1 from Student--错误的写法
select --正确而写法
year(getdate()) - year(stuBirthdate) as age
, year(getdate()) - year(stuBirthdate)+1
from
Student
-> distinct选项
select distinct 字段 from ...
-> top选择
select top 数字 字段 from ...
显示结果的前几条数据
select top 数字 percent--百分比
字段
from ...
-> order by子句
专门排序的子句,表示使用什么字段进行排序
select ... order by 字段 [desc] --默认升序的,加desc是降序
7、开窗函数over()的常用方法
- 1)、为每条数据显示聚合信息
- 2)、为每条数据提供分组的聚合函数结果
- 3)、与排名函数一起使用
- 准备一些数据
- 该查询表只能用在SQL Server 2008中
- a.例子
select * from ( values (1, '张三', 100), (2, '李四', 87), (3, '赵钱', 95), (4, '孙李', 88) ) as tbl(stuId, stuName, stuScore);
将上数据作为临时数据,求平均值
select avg(stuScore) as avgScore from ( values (1, '张三', 100), (2, '李四', 87), (3, '赵钱', 95), (4, '孙李', 88) ) as tbl(stuId, stuName, stuScore);
得到平均分为92分,如果需要显示出所有学员成绩,并在每个学员成绩后面加上平均分
select *, avg(stuScore) as avgScore from ( values (1, '张三', 100), (2, '李四', 87), (3, '赵钱', 95), (4, '孙李', 88) ) as tbl(stuId, stuName, stuScore);
报错,因为聚合函数只有一行数据,而学生有4条数据,两者不统一
- 使用开窗函数
- 语法
聚合函数 over() as 别名
- 修改SQL语句
select *, avg(stuScore) over() as avgScore from ( values (1, '张三', 100), (2, '李四', 87), (3, '赵钱', 95), (4, '孙李', 88) ) as tbl(stuId, stuName, stuScore);
- b. 为每条数据提供分组的聚合函数结果
依旧是这些数据,但是为每个学员添加一个组别,数据如下select * from ( values (1, '张三', 100, '组长'), (2, '李四', 87, '学干'), (3, '赵钱', 95, '组长'), (4, '孙李', 88, '学干') ) as tbl(stuId, stuName, stuScore, stuOtherName);
-- 希望平均分还要分开求,即总平均分和所有学干的平均分和所有组长的平均分分别显示 -- 可以使用 -- 聚合函数 over(partition by 字段) as 别名 -- 即
select *,
avg(stuScore) over() as 平均分,
avg(stuScore) over(partition by stuOtherName) as 分组平均分
from
(
values
(1, '张三', 100, '组长'),
(2, '李四', 87, '学干'),
(3, '赵钱', 95, '组长'),
(4, '孙李', 88, '学干')
) as tbl(stuId, stuName, stuScore, stuOtherName);
- c.与排名函数一起使用
使用row_number()函数可以为数据生成一个自动增长的数字列
语法
row_number() over(order by 字段) as 别名
表示这个自动增长的列使用”字段”的排序规则添加
- i.例如按照分数添加序号
select
row_number() over(order by stuScore) as 序号,
*,
avg(stuScore) over() as 平均分,
avg(stuScore) over(partition by stuOtherName) as 分组平均分
from
(
values
(1, '张三', 100, '组长'),
(2, '李四', 87, '学干'),
(3, '赵钱', 95, '组长'),
(4, '孙李', 88, '学干')
) as tbl(stuId, stuName, stuScore, stuOtherName);
- ii.按照姓名添加序号
select
row_number() over(order by stuName) as 序号,
*,
avg(stuScore) over() as 平均分,
avg(stuScore) over(partition by stuOtherName) as 分组平均分
from
(
values
(1, '张三', 100, '组长'),
(2, '李四', 87, '学干'),
(3, '赵钱', 95, '组长'),
(4, '孙李', 88, '学干')
) as tbl(stuId, stuName, stuScore, stuOtherName);
8、 在查询的结果中有两个类型
-> 表(结果集):结果集必须有列名(无序)
-> 游标CURSOR:游标是有序的(有顺序的,order by)
-> 两本书:数据库内部底层:
《数据库系统实现》
《数据库系统概念》
9、 case语法相当于C#中的if-else if或switch-case语法
- 相当于if … else if
case
when 包含字段的表达式1 then 值1
when 包含字段的表达式2 then 值2
...
else 值n
end
- switch-case
case 字段
when 值1 then 显示1
when 值2 then 显示2
...
else 显示n
end
10、 联合结果集
- 就是将两个结果集合并
- 语法
查询1
union [all]
查询2
- 说明
- 联合结果集的列名由第一个查询决定
- 联合结果集可以进行多个结果集的连接
- 保留all的时候,结果集就是单纯的合并,如果去除all会将重复数据删除
11、 向表中添加数据–insert语句
原始语法
insert into 表名(列名,列名….) values(值,值….);
补充语法
insert into 表名(列名,列名….) 结果集;
12、 修改和更新表中数据—update语句
Update from 表名
Set 列名=新值
Where 条件
13、 删除表中数据—update语句
delete 表名
Where 条件
14、 表值构造函数(了解)
- 语法
(values (值1, 值2, 值3, ...),
(值1, 值2, 值3, ...),
(值1, 值2, 值3, ...),
...
) as 表名(列1, 列2, 列3, ...)
- 使用1
insert into 表名(列名) values(值),(值),(值),...
- 使用2
使用表值构造函数得到临时结果集
SQL Server 2008+才适用
15、复制表
- 将查询的结果放到一个新表中
select
*
into 新表名
from
表
- 注意
- 这个新表是不允许事先存在的
- 使用这个方法可以复制表结构
select * into 新表 from 表 where 1 > 2;
16、字符串函数
略
17、日期函数
函数名 | 格式 |
---|---|
datetime | yyyy-MM-dd hh:mm:ss.000 |
date | yyyy-MM-dd |
time | hh:mm:ss.000 |
datetime2 | yyyy-MM-dd hh:mm:ss.000 |
datatimeoffset | 使用标准时间+偏移时间 |
- 输入
在SQL Server中没有时间类型的变量值。
如果需要赋值就使用时间格式的字符串。 - year()、day()、month()
- dateadd()
- datediff()
18、 转换
考虑数字运算,一般有字符串型的数字参与时,如果有数字则全部转换为数字计算联合结果集的时候
cast(变量或数据 as 类型);
convert(类型, 变量或数据[, 格式])
19、 独立子查询(将子查询选中可以直接执行)
- 一个查询的结果可以是一个单值、一多个值或一张表
- 将一个查询的结果(单值或多值)作为另一个查询的条件
select -- 外部查询
字段
from
表1
where
字段 =
(
select top 1 字段 from 表2 -- 单值(标量)子查询
)
and
字段 in
(
select 字段 from 表3 -- 多值子查询
)
- 查XXX的考试成绩
- 相关子查询
select
字段
from
表1 as t1
where
字段 =
(
select top 1 字段 from 表2 as t2 where t1.字段=t2.字段
);
20、表连接
内部连接:内部连接使用比较运算符,根据每个表的通用列中的值匹配两个表中的行。
外部链接:在内部连接中,只用两个表中匹配的行才能在结果集中出现,而在外部连接中可以只限制一个表,而对另一个表不加限制
交叉连接:没有where子句的交叉连接,即笛卡尔积,第一个表的行数乘以第二个表的行数等于笛卡尔积得到的结果集大小
例子:
- 问题:将表的各列拆散进行组合变成一个新的表结构
tbl1(id, name, pwd); tbl2(id, note); tbl(id, name, pwd, note);
- 内连接
select * from 表1 as t1 inner join 表2 as t2 on t1.字段=t2.字段
外连接
- 左连接
select * from 表1 as t1 left join --左连接,加入表1的字段 表2 as t2 on t1.字段 = t2.字段;
- 左连接
select * from 表1 as t1 right join --右连接,加入表2的字段 表2 as t2 on t1.字段 = t2.字段;
- 全连接
select * from 表1 as t1 full join --全连接,加入表1和表2的字段 表2 as t2 on t1.字段 = t2.字段;
- 交叉连接(笛卡尔积)
select * from 表1 cross join(join) 表2
21、 表表达式
- 派生表
将一个查询的结果放在另一个查询的from子句中作为数据源- 步骤与注意
- 游标不允许,没有列名不允许
- 将这个查询用括号括起
- 为这个这些取别名
- 将其写在from子句中,外面使用“别名.字段名”查询即可(如果没有字段冲突,别名可省略)
- CTE 公共表
- 作用与派生表一模一样,唯一的不同就是定义在前面,后面可以重复使用,结果集中一定要有列名,不然就在表名后定义列名
- 语法
with 表别名(列别名...可省略)
as
(
结果集
)
select * from 表别名;
注意:公用表表达式是一个定义执行的整体,使用的时候所谓的定义一次使用多次指的是紧跟定义的查询中的自连接,以及进一步定义和递归定义
- CTE可以在后面的查询中多次被引用,构成一些特定结构的查询
- 视图
视图相当于表,如果不严格的考虑就可以将其作为表来看
语法create view vw_名字 as 结果集 go
视图与派生表和CTE的最大区别在与用一个别名存到了数据库中,在使用的时候可以将别名当作表直接使用
重要:视图是虚拟表,不具备存储数据的能力,在执行的时候,SQL Server底层依旧是分别处理物理表结构
22、分页(几种方法实现)
问题:假定有数据60000行,每页显示10行,显示第1页,第2页,以及第n页
- (1)、top方法
select top 10
*
from
Student
where
stuId not in (select top ((n-1)*10) stuId from Student order by stuId)-- 第n页
order by stuId;
性能低
- (2)、SQL Server 2005的时候,row_number(),在结果集上排序
select
row_number() over(order by 字段)--需要over(order by 字段)
,*
from
tbl
select ROW_NUMBER() over(order by stuId),* from Student
- (3)、派生表(将一个有序列的结果集作为一个数据源)
select
*
from
(select ROW_NUMBER() over(order by stuId) as num, * from Student) as tbl
where
tbl.num between (99-1)*10 + 1 and 99*10;--99页
- (4)、分页视图
create view vw_fenye1
as
select ROW_NUMBER() over(order by stuId) as num, * from Student;
go
select * from vw_fenye1 where num between 1 and 10;
23、内联标识函数
- 输入显示第几页,每页显示多少条,使用内联表值函数数(了解)
create function fn_函数名
(参数名 as 类型名, ...)
returns table
as
return 结果集
-----------------------------
create function fn_FenYe2--返回index页,每页pageSize条数据
(@index as int, @pageSize as int) returns table--所有的变量都要@引导
as
return
select * from vw_fenye1
where
num between (@index - 1) * @pageSize + 1 and @index * @pageSize;
go
- 就是带有参数的视图
select * from fn_FenYe2(3,25);
24、变量的使用
- 全局变量(系统变量) @@变量名 –已经定义好
select @@version --查看系统版本
局部变量(自定义变量) @变量名 –自己先定义,再复制,后使用
定义:使用declare引导变量的定义
–declare @num int; – 定义了一个变量num,是int类型(int num;)
赋值:两种方法
– ①使用set语句
– set @变量 = 标量(可以是一个数据、可以是一个子查询、也可以是表达式)
– set @num = (select COUNT() from Student);
– ②使用select语句
– select 字段 from 表 表示一个查询
– select @变量=字段 from 表 表示将查询的结果不已表的形式显示,而是赋值
– select @num=COUNT() from Student;使用:用select -- select @num; --使用num
注意
赋值的方式
- set赋值时,等号右边不允许是多值
--只能赋值一次 单值 set @num = (select top 1 t.ID --必须加top 1 from ( select ID from (values(123),(2),(3),(4)) as tbl(id) ) as t ); select @num; go
- select赋值方式,在查询中可以是多个数据,但是会根据查询的内容赋值多次
--赋值多次 declare @num int; select @num = t.ID from ( select ID from (values(123),(2),(3),(4)) as tbl(id) ) as t select @num; go
- 在SQL Server 2008+提供了新的赋值方式,定义的时候进行赋值
-- 定义的时候直接赋值 declare @num int = 10; select @num;
几种常见的系统变量
select @@ERROR;--系统执行最后一次的错误信息
select @@IDENTITY;--最后一次插入表中的唯一标识符;
...
25、流程控制(循环和判断)
- 循环 :和C#语言相似,只是用begin end代替了“{”和“}”
- 跳出循环:break和continue
while(表达式) begin 代码 end
- 例:求1到100中所有奇数的和
declare @i int=0;--赋值 declare @sum int=0; while(@i <= 100) begin if(@i % 2 <> 0)--<>是不等于的意思 begin set @sum += @i; end set @i += 1; end select @sum; go
- 判断:和C#语言相似,只是用begin end代替了“{”和“}”
if(表达式)
begin
代码
end
else if(表达式)
begin
代码
end
else
begin
代码
end
26、事务
- 例子:bank中有两个账户,001账户有1000元,002账户有20元,规定账户中余额不小于10元,现在001账户给002账户转1000,执行两条SQL语句,由于有约束,第一条语句执行失败,而第二条执行成功,导致002账户有1010元,001账户还是1000元,怎么解决问题?
update bank set balance=balance - 1000 where cid='0001'
update bank set balance=balance + 1000 where cid='0002'
- 在数据库中所有的执行单元都是事务,四个特性:
- 1)、原子性:事务中的所有操作要么全部成功,要么全部失败
- 2)、隔离性:每个事务都是与其他事务的数据隔离,访问同一数据,会锁住数据
- 3)、持久性:事务一直保持在数据库中
- 4)、一致性:保证数据的完整– 数据的完整性是数据满足数据库描述对象的实际意义
- 如何操作事务
- 隐式事务,就是平时写的SQL语句,每条语句是一个事务,以分号结束的,没有分号,系统自动判断
- 显式事务,手动的将多条语句操作打包成一个事物
- 开启事务使用 begin transaction
- 从这条语句开始,其后的所有代码,都是这个事务中的操作,只到出现提交或回滚的操作
- 什么是提交就是告诉数据库刚刚从begin transaction开始执行的代码全部生效
- 使用commit transaction进行提交
- 什么是回滚呢?告诉数据库刚才的代码不生效,操作回到执行之前的状态
- 使用rollback transaction表示回滚
- 如何判断是否正确与错误
- 使用@@ERROR变量
- 处理数据库中的错误,利用try-catch语法块将执行进行处理
begin try 可能出现错误的语句 end try begin catch 异常的处理代码 end catch
27、存储过程:处理一些较为复杂的操作,只需要C#提供参数和存储过程名称
- 视图:只能执行查询操作
- 函数:与存储过程相似,但函数返回的是单值
- 存储过程:能返回数据集,功能比函数强
存储过程就是存储在数据库中的过程 ,分两种:
- 系统自带的存储过程,以sp_或者xp_开头
- 用户自定义的存储过程,以usp_开头
如何定义存储过程?三种
- 不带任何参数与返回值的存储过程–与执行SQL语句一样
create procedure usp_存储过程名 as begin SQL语句 end
有参数的存储过程
1、按照定义的顺序依次用逗号隔开
exec usp_Bank1 ‘0001’, ‘0002’, 10;
2、利用“@变量名=值”的形式提供,可以调整赋值顺序create proc usp_存储过程名 @变量名1 类型名1, @变量名2 类型名2, ... as begin SQL语句 end
- 带有默认参数的存储过程
- 如果提供数据,就用你提供的数据
- 如果没有提供数据,就使用默认值
- 只需要声明变量的时候,加上默认值即可
create proc usp_存储过程名 变量名1 类型名1 = 值, 变量名2 类型名2 = 值, ... as begin SQL语句 end
- 带有默认参数的存储过程
有返回值的存储过程
- C#中函数的out参数
create pro usp_存储过程名 @参数1 类型1 , @参数2 类型2 output, ... as begin SQL语句 end
调用存储过程
declare @res int; exec 存储过程名 值1, 值2, @res output, 值4
- 用C#如何执行存储过程?
- 1)、用存储过程名代替sql语句
- 2)、设定CommandType为存储过程(默认是Text)
- 3)、看存储过程的结果选则方法,参数与之前用法也一样(唯独output参数需要设置方向)
28、触发器
- 用法
create trigger tr_触发器名 on 表名
for|after|instead of update/delete/insert
as
begin
SQL语句
end
- 解释
- 两种触发器
- for(after)触发器:执行某个动作后出发
- instead of触发器:不执行原来的操作,取而代之的执行触犯起的动作
- 两张表,系统有两张临时表,inserted和deleted表
- 三种操作,触发器操作实际是操作这两张临时表
- insert 执行插入和更新操作时将新的数据副本缓存一份在inserted表中
- update 将原来的数据线缓存一份在deleted表中,然后将修改的数据缓存一份在inserted表中
- delete 将删除的的数据缓存一份deleted表中
29、索引 –索引的目的就是为了提高查询的性能
聚集索引–聚集索引决定了数据存储的顺序
非聚集索引–与数据存储顺序无关
注意:
聚集索引与表中数据是一致的,索引一张表只允许有一个聚集索引create nonclustered index 索引名 on student(stuBirthdate);--clustered 聚集索引,nonclustered非聚集索引
新华字典
拼音检索–聚集索引
笔画检索–非聚集索引
三、C#中怎么使用数据库
1、ADO.NET–C#中操作数据库的技术
- 命名空间
system.Data 描述数据的命名空间
system.Data.SqlClient SQLServer的命名空间 - 四大类库(DBConnection是父类)
- Connection:连接数据库
- Command:命令
- ExecuteNonQuery 执行非查询语句,返回受影响的行数,如果执行的是飞增删改的操作返回-1
- ExecuteScalar
- ExecuteReader
- DateReader:循环读取所有行数据
- DataAdapter:用来一次性处理(将前三个对象的封装)
- 补充类库:DataSet 用来装数据的类型
- 四大模型
- ExecuteNonQuery 执行非查询语句,返回受影响的行数,如果执行的是飞增删改的操作返回-1
- 1)、创建连接对象
- 2)、创建执行对象
- 3)、打开连接
- 4)、执行操作
- ExecuteScalar
- ExecuteReader
- SqlDataAdapter
- ExecuteNonQuery 执行非查询语句,返回受影响的行数,如果执行的是飞增删改的操作返回-1
2、如何用C#操作数据库
- 首先要连接数据库
提供连接的服务器名字 server=.\sqlexpress
连接到的数据库 database=TestDB
提供用户名 uid=sa
提供密码 pwd=123
利用字符串将这四个信息连起来
利用SqlConnection连接了 - 数据库的连接是非常消耗资源的!
尽早关闭,最晚打开
sql=“Server=.\sqlexpress;database=TestDB;Uid=sa;Pwd=123”–使用密码验证登陆
sql=“Data Source=.\sqlexpress;Initial Catalog=TestDB;User ID=sa;Password=123”–使用密码验证登陆
sql=“Data Source=.\sqlexpress;Initial Catalog=TestDB;Integrated Security=True”–使用windows验证登录 - 使用数据库
using(SqlConnection conn = new SqlConnection(connStr))–自动销毁
{
} - 连接池Connection pool
将连接通道释放的时候,并没有释放掉,移到连接池中了
连接池是默认开启的,如果要关闭,需要在连接字符串中加上 pooling =true - 使用SqlCommand对象,有哪些方法?
ExecuteNonQuery() 执行非查询语句,增、删、改,返回受影响行数
可以执行任何SQL语句,在执行非增删改SQL语句的时候,返回-1
ExecuteScalar() 得到结果的第一行与第一列的数据,如果没有结果就返回null
由于可以获得任意的数据,因此该方法返回object类型的数据
ExecuteReader()
SqlDataAdapter()- ADO.Net中有一个DBNull类型,用来在C#中描述SQLServer中的NULL
- 读取数据的时候,在可以为空的字段上用reader.IsDbNull(数字)方法判断该字段是否为空
- 如果为空,可以考虑使用null表示
- 如果不为空直接使用这个值
3、四个模型
第一个模型
- ExecuteNonQuery(),执行非查询语句,步骤:
-> 准备连接字符串
-> 准备SQL语句
-> 创建连接对象
-> 创建执行对象
-> 打开连接
-> 执行操作
-> 关闭 - 整合的代码
string connStr = @"server=机器名或IP\实例名;database=数据库名;uid=sa;pwd=123;"; string sql = ""; int count; using(SqlConnection conn = new SqlConnection(connStr)) { using(SqlCommand cmd = new SqlCommand(sql, conn)) { conn.Open(); count = cmd.ExecuteNonQuery(); } } ```
第二个模型
- object ExecuteScalar(); 返回查询的第一行第一列的结果
string connStr = @"server=机器名或IP\实例名;database=数据库名;uid=sa;pwd=123;";
string sql = "";
object count;
using(SqlConnection conn = new SqlConnection(connStr))
{
using(SqlCommand cmd = new SqlCommand(sql, conn))
{
conn.Open();
count = cmd.ExecuteScalar();
}
}
使用ExecuteScalar一般与聚合函数连用
第三个模型 SqlDataReader cmd.ExecuteReader();
- 在读取的对象中reader里面包含了数据的内部结构
- 在reader有一些属性专门用来描述有多少列,每列的名字等信息
- reader.Count或Lenght
- 得到列名reader.GetName(索引);
- SQL 语句中的列名是什么就是什么
- 使用索引[字段的名字]也可以获得数据,但是数据依旧是object类型(不推荐使用)
- 列名一点更改就不能再使用原始的列名了
- 使用reader读取数据不允许从下往上读(顺序不可变)
- reader对象在使用的过程中,Connection不允许关闭
string connStr = @"server=机器名或IP\实例名;database=数据库名;uid=sa;pwd=123;";
string sql = "";
object count;
SqlConnection conn = new SqlConnection(connStr)
using(SqlCommand cmd = new SqlCommand(sql, conn))
{
conn.Open();
count = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
第四个模型
SqlDataAdapter
-> DataSet
数据集
内存中的数据库
-> 如何创建DataSet
-> DataSet的结构
-> 使用SQL Server的类型都在命名空间System.Data.SqlClient中
-> 使用数据处理的类型均在System.Data中
-> SqlDataAdapter
-> 创建DataSet
-> 创建SqlDataAdapter,提供SQL语句和连接字符串
-> 调用一个Fill方法将数据加到DataSet中
-> 关闭释放即可
SqlDataAdapter sda = new SqlDataAdapter(sql语句, 连接字符串);
sda.Fill(DataSet对象);
4、DataGridView控件
-> 专门用来显示数据
-> 填充数据的方法很多,我主要介绍DataSource赋值的方法
-> 步骤
-> 拖控件,dgview
-> 得到DataSet数据,dataset
-> 赋值
dgview.DataSource = dataset.Tables[0];
-> 使用DataGridView的Columns属性可以访问某一列
得到该列以后使用Forzen属性可以冻结该列,冻结该列表示该列与其左侧的列均被冻结
5、更新数据,使用SqlDataAdapter
-> 步骤
-> 获得数据源(即修改后的数据)
-> 得到或创建与读取数据的SqlDataAdapter一样的对象(SQL语句有主键)
-> 创建一个SqlCommandBuilder对象,处理一下sda,自动生成SQL执行对象(迷惑?)
-> 调用sda的Update方法即可
6、 参数化查询
-> 步骤
-> 将原来需要字符串拼接的值,用一个@引导的变量名代替
-> 使用SqlParameter类型将参数变量与值绑定在一起,将参数与值配对
-> 将SqlParameter对象交给cmd对象的Parameters集合,cmd.Parameters.Add*();
7、使用配置文件获得连接字符串
-> 添加App.config
-> 在里面添加键值
-> 添加connectionStrings标签
-> 在里面添加add标签
-> add标签有两个属性:name、connectionString
-> name用于存储名字,也就是键。在C#中使用这个名字获得连接字符串
-> connectionString就是连接字符串
-> 添加引用System.Configuration
-> 使用ConfigurationManager类的Connections属性
-> 利用索引获得连接字符串
8、注入漏洞
-> 原本是一个数据,现在变成一个数据和一个条件
-> 限制只允许是数据
-> 所谓的参数化,就是将需要值的地方,用一个参数变量表示,而操作数据库的时候,给这个参数赋值即可