目录
《Sams Teach Yourself SQL in 10 Minutes (4th Edition)》 —— SQL 入门书
闲来无事,翻一翻,确实作为入门书籍来说还是不错的。
SQL 是使用最广泛的数据库语言。
数据库设计、规范化到关系数据库理论以及管理问题等,一大堆,甚是繁琐,但这些往往不是我们需要的。
从最简单的数据检索(select)入手,逐步过渡一些较为复杂的内容,例如:联结、子查询、存储过程、游标、触发器以及表约束等,循序渐进。
数据库基础、数据库设计、规范化、关系数据库理论
数据库:一个以某种有组织的方式存储的数据集合(文件柜、database)。
数据库软件:数据库管理系统(DBMS),数据库是通过 DBMS 创建和操纵的容器。
表:某种特定类型数据的结构化清单(table)。
模式:关于数据库和表的布局及特性的信息(schema)。
列:(column)字段
行:(row) 也称为数据库记录 (record)。
主键:唯一标识 (primary key),主键不允许为空值 NULL
应该总是定义主键
复合主键
外键
SQL:Structured Query Language 结构化查询语言
标准SQL:ANSI SQL
动手实践 —— The Most Important!!!
使用 select 语句从表中检索一个或多个数据列。
关键字(keyword):作为 SQL 组成部分的保留字,英语单词。
select prod_name from Products;
select * from table_name;
未排序数据
过滤 → 子集
多条 SQL 语句必须以分号(;)分隔
SQL 语句不区分大小写
忽略空格
多个列,用逗号(;)分隔
select prod_id,
prod_name,
prod_price
from Products;
数据格式化
使用星号(*)通配符可以检索所有列
select * from Products;
通配符 *
检索不需要的列通常会降低检索和应用程序的性能,因此需要确保真的需要所有的列,才使用通配符进行检索。
检索未知列
distinctdistinct 去重 具有唯一性
select distinct vend_id from Products;
作用于所有的列 只返回不同的值
select distinct a,b from table_name;
不能部分使用 distinct
limit只返回一定数量的行
1.SQL Server & Access -- TOP
select top 5 prod_name from table_name;
2.DB2 -- FETCH FIRST 5 ROWS ONLY
select prod_name from table_name fetch first 5 rows only;
3.Oracle -- 基于rownum(行计数器)计算行
select prod_name from table_name where rownum <= 5;
4.Mysql & MaraiDB & PostgreSQL & SQLite -- limit
select * from table_name limit 5;
或者
select * from table_name limit 5 offset 5; -- MySQL 等 DBMS 返回从第5行开始的5行数据
从第5行开始后面的3行数据
select * from table_name limit 3 offset 4; -- MySQL返回从第5行起的3行数据
select * from table_name limit 3,4; -- MySQL返回从第4行开始的4行数据
limit m,n
? 其中,m是指记录开始的 index,从0开始,表示第一条记录
? n是指从第m+1条开始,取n条数据
? 初始记录行的偏移量是0,不是1
? limit 95,-1 96-last所有 —— 测试过,不行!
? limit 5 等同于 limit 0,5
希望包括不进行处理和执行的文本,例如描述性的注释。
井号#、--、/* */ 等3种方式。
使用 select 语句的 order by 子句,根据需要排序检索出的数据。
如果不排序,数据一般将以它在底层表中出现的顺序显示。
子句(clause):一个子句通常由一个关键字加上所提供的数据组成。
select *
from table_name
order by prod_name;
order by 子句应该放在最后面。
使用非检索列进行排序数据也是完全合法的。
按多个列进行排序时,用逗号隔开即可。
select *
from table_name
order by prod_name, prod_id;
按列位置排序
select prod_id, prod_name, prod_price
from Products
order by 2,3; -- 先按第2列进行排序,后按第3列进行排序
不用重新输入列名,却可能导致不明确。
降序:desc
-- desc descending 缩写
-- asc ascending 缩写
select *
from table_name
order by prod_name desc; -- asc 升序
多个列
select *
from table_name
order by prod_name desc, prod_id;
desc 关键字只应用到直接位于其前面的列名。
因此如果想对多个列都进行降序,必须针对每一列加上 desc 关键字。
a 和 A 的排序方式则取决于数据库的设置方式。
使用 select 语句的 where 子句指定搜索条件。
一般只会根据特定操作或者特定需求提取表数据的子集。
需要指定搜索条件(search criteria)、过滤条件(filter condition)。
select prod_name, prod_price
from Products
where prod_price = 3.49;
数据类型
同时使用 order by 和 where 子句时,应该让 order by 位于 where 之后。
| 操作符 | 说明 |
|---|---|
| = | 等于 |
| <> | 不等于 |
| != | 不等于 |
| < | 小于 |
| <= | 小于等于 |
| !< | 不小于 |
| > | 大于 |
| >= | 大于等于 |
| !> | 不大于 |
| between | 在指定的2个值之间 |
| is null | 为 NULL 值 |
部分操作符冗余,可相互替换。
select vend_id, prod_name
from Products
where vend_id <> 'DDDDD';
单引号用来限定字符串,字符类型需要使用单引号。
数值无需单引号。
between范围的开始值和结束值
select * from Products
where prod_price between 5 and 10; -- 包括指定的开始值和结束值
列可否为空值,在创建表初期可指定。
NULL -- 空值(no value),与0、空字符串、空格等不同。
select * from Products
where prod_price is null;
过滤数据时,一定要验证被过滤列中含 NULL 的行是否出现在返回的数据中。
组合 where 子句建立高级搜索条件,使用 not 和 in 操作符。
通过 and 子句或者 or 子句进行组合使用。
操作符(operator):用来联结或改变 where 子句中的关键字,也称为逻辑操作符(logical operator)。
select * from Products
where vend_id = 'DDDD' and prod_price <= 4; -- and 同时满足
多个条件之间都要使用 and 关键字。
order by 子句放在 where 子句之后。
select * from Products
where vend_id = 'DDDD' or prod_price <= 4; -- or 任一满足
DBMS 检索匹配任一条件的行,许多 DBMS 在 or where 子句的第一个条件满足情况下,就不再计算第二个条件。
and 和 or 两者可以结合以进行复杂、高级的过滤。
注意优先级。
select * from Products
where (vend_id = 'DDDD' or vend_id = 'FFFF')
and prod_price = 10;
不要过分依赖系统默认的计算顺序,多使用圆括号。
in 操作符in 操作符用来指定条件范围,范围内的每个条件都可以匹配。
select * from Products
where vend_id in ('DDD', 'FFFF')
order by prod_name;
类似于 or 的功能。
in 操作符更加清晰、直观。
in 性能高于 or 操作符。
in 可以包含其他 select 语句,能够更加动态地建立 where 子句。
not 操作符否定其后所跟的任何条件。
select * from Products
where not vend_id = 'DDDD'; -- 也可以使用 <> 操作符完成
在复杂的语句中,使用 not 才有优势,并且与 in 操作符联合使用的时候,更显优势。
MariaDB 支持使用 not 否定 in、between 和 exists 子句。
总结:and、or、not、in、求值顺序。
使用通配符 * 、like 操作符进行通配搜索,以便对数据进行复杂过滤。
like 操作符利用通配符,可以创建模糊搜索模式。
通配符(wildcard):用来匹配值的一部分特殊字符。
搜索模式(search pattern):由字面值 + 通配符或两者组合构成的搜索条件。
通配符搜索只能用于字符串,非文本数据类型字段无法使用通配符搜索。
表示任何字符出现任意次数。
select prod_id, prod_name
from Products
where prod_name like 'Fish%'; -- 以 Fish 开头的产品
Access 通配符:使用 * 而不是 %
不同的 DBMS 区分大小写,具体看配置。
select prod_id, prod_name
from Products
where prod_name like '%bean%'; -- 字符两端
字符中间也可以。
在匹配邮件地址的时候,很有用。例如:where email like ‘b%@qq.com‘
% 还能匹配 0 个字符
% 代表搜索模式中给定位置的 0 个、1 个或多个字符。
注意:空格的影响,是否匹配不到数据?
注意:NULL 无法匹配
只匹配单个字符,而不是多个字符。
DB2 不支持通配符 _
Access 需要使用?而不是 _
select prod_id, prod_name
from Products
where prod_name like '_inch'; -- 单个字符 注意空格
指定字符集,必须匹配指定位置(通配符的位置)的一个字符。
Access 和 SQL Server 支持集合
select cust_contact
from Customers
where cust_contact like '[JM]%'
order by cust_contact; -- 以J或M开头的名字
select cust_contact
from Customers
where cust_contact like '[^JM]%'
order by cust_contact; -- 不以J或M开头的名字
Access 中使用 ‘[!JM]‘ 进行检索
使用 not 操作符同样可以得到结果。not like
通配符的搜索将耗费更多的时间
注意:
关联、and、upper、sum、avg 等计算字段。
从数据库中检索出转换、计算或格式化过的数据。
字段(field):意思同列(column)
在数据库服务器上完成转换和格式化比在客户端中完成快得多。
拼接(concatenate):将值联结在一起。
加号(+)或者 两个竖杆(||)表示。
Mysql 和 MariaDB 使用特殊的函数。
select vend_name + '(' + vend_country + ')'
from Vendors
order by vend_name; -- 字符拼接 使用||同理
-- MySQL --
select concat(vend_name, '(', vend_country, ')')
from Vendors
order by vend_name;
rtrim 函数select rtrim(vend_name) + '(' + rtrim(vend_country) + ')'
from Vendors
order by vend_name;
rtrim 函数 -- 去掉值右边的所有空格
ltrim 函数 -- 去掉值左边的所有空格
trim 函数 -- 去掉值左右两边的所有空格
别名(alias)是一个字段或值的替换名。
别名使用 as 关键字赋予。
select rtrim(vend_name) + '(' + rtrim(vend_country) + ')' as vend_title
from Vendors
order by vend_name;
as 关键字是可选的,不过最好使用它。
导出列(derived column)
select prod_id,
quantity,
item_price,
quantity * item_price as expanded_price
from OrderItems
where order_num = 20008;
基本算术操作符:
+-*/ 加减乘除、圆括号区分优先级
select 3 * 2; -- 测试数据
省略 from 子句之后就是简单的访问和处理表达式。
select now(); -- 返回当前时间
不同的 DBMS 支持不同的函数,如何使用函数。
利用函数处理数据,例如:rtrim() 函数可以去掉字符串空格。
mid() substring() sbustr()convert() cast()curdate() sysydate() now() current_date() getdate() date()SQL 函数不是可移植的。
可移植(portable):所编写的代码可以在多个系统上运行。
使用函数,应该做好代码注释。
rtrim() 函数:去除多余空格
upper() 函数:转换为大写
select vend_name,
upper(vend_name) as vend_name_upcase
from Vendors
order by vend_name; -- 将文本转换为大写
常用文本处理函数:
left() 函数:返回字符串左边字符 -- Oracle 不支持
length() 函数:返回字符串长度 -- 同 len() 、datalength() 函数
upper() 函数:返回字符串大写 -- Access 使用 ucase() 函数
lower() 函数:返回字符串小写 -- Access 使用 lcase() 函数
ltrim()函数:去掉字符串左边空格
right()函数:返回字符串右边字符
rtrim() 函数:去掉字符串右边空格
soundex() 函数:返回字符串的 soundex 值
soundex 是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。考虑其类似发音字符、音节,使其能对字符串进行发音比较而不是字母比较。
Microsoft Access 和 PostgreSQL 不支持 soundex( ) 函数。
SQLite 必须指定 SQLite_soundex 编译选项才支持。
select soundex('Michael Green'),
soundex('Michelle Green')
from dual; -- 发音模糊过滤
-- 1 M242 M242
Oracle 中没有 left 和 right 的写法,但可以使用 substr 函数进行字符截取处理。例如:
select substr('abcdefg', 1,5) from dual;
-- abcde
日期和时间采用相应的数据类型存储在表中。
不同的 DBMS 采用不同的时间日期处理函数,可移植性最差。
-- SQL Server
select order_num from Orders
where datepart(yy, order_date) = 2012;
-- Access
select order_num from Orders
where datepart('yyyy', order_date) = 2012;
-- PostgreSQL
select order_num from Orders
where date_part('year', order_date) = 2012;
-- Oracle
select order_num from Orders
where to_number(to_char(order_date, 'yyyy')) = 2012; -- 提取日期 转换为数值 进行对比
-- Oracle between
select order_num from Orders
where order_date between to_date('01-01-2012')
and to_date('12-31-2012');
-- MySQL MariaDB
select order_num from Orders
where year(order_date) = 2012;
-- SQLite
select order_num from Orders
where strftime('%Y', order_date) = '2012';
datepart() 函数:返回日期的某一部分
大多数 DBMS 具有比较日期、执行基于日期的运算、选择日期格式等函数。
数值处理函数仅处理数值数据。
代数、三角或几何运算。
在各 DBMS 中,数值函数反而是最一致、最统一的函数。
常用数值处理函数:
abs() 返回绝对值
cos() 返回余弦值
sin() 返回正弦值
tan() 返回正切值
exp() 返回指数值
pi() 返回圆周率
sqrt() 返回平方根
利用 SQL 聚集函数汇总表的数据。
汇总表中的数据,而不需要实际数据本身。
聚集函数(aggregate function):对某些行运行的函数,计算并返回一个值。
avg() 平均值
count() 计数
max() 最大值
min() 最小值
sum() 求和
avg() 函数只能用于单个列,确定特定数值列的平均值。多个列必须使用多个 avg() 函数。
avg() 函数忽略列值为 NULL 的行。
使用 count(*) 对表中行进行计数,不管列中是否为空值(NULL)还是非空值。
使用 count(column) 对特定列进行计数,忽略 NULL 值。
对非数值数据使用 max() 函数,返回排序后最后一行。忽略 NULL 行。
对非数值数据使用 min() 函数,返回排序后最前面一行。忽略 NULL 行。
select count(*) as num_items,
min(prod_price) as price_min,
max(prod_price) as price_max,
avg(prod_price) as price_avg
from Products; -- 组合聚集函数
对所有行执行计算,指定 ALL 参数或不指定参数(因为 ALL 是默认行为)。
只包含不同的值,指定 distinct 参数。
Access 中不支持 distinct,必须使用子查询把 distinct 结果返回到外部查询。
select avg(distinct prod_price) as avg_price
from Products
where vend_id = 'AAAA';
distinct() 必须指定列名
有的 DBMS 支持其他参数,例如:top()、top percent() 等。
别名应该采用新的名称,而不该使用已有的列名。
聚集函数很高效,比客户端计算快得多。
分组数据,汇总表内容的子集。
select 语句子句:group by 子句和 having 子句。
使用 SQL 聚集函数可以汇总数据。
使用分组可以将数据分为多个逻辑组,对每个组进行聚集计算。
select vend_id,
count(*) as num_prods
from Products
group by vend_id;
group by 子句指示 DBMS 分组数据,然后对每个组而不是整个结果集进行聚集。
group by 子句可以包含任意数目的列,可以对分组进行嵌套,更细致的进行数据分组。group by 子句必须出现在 where 子句之后,order by 子句之前。SQL Server 支持可选的 ALL 子句,返回所有的分组。
也可以通过相应位置指定列,例如: group by 2,1
除了能用 group by 分组数据外,SQL 还允许过滤分组。
where 没有分组的概念。
必须使用 having 子句,类似于 where ,唯一的区别是 where 过滤行,而 having 过滤分组。
select cust_id,
count(*) as orders
from Orders
group by cust_id
having count(*) >= 2; -- 过滤
where 子句在这里不起作用,因为过滤是基于分组聚集值,而不是特定行的值。
where 在数据分组前进行过滤,而 having 在数据分组后进行过滤。
select vend_id,
count(*) as num_prods
from Products
where prdo_price >= 4
group by vend_id
having count(*) >= 2;
使用 having 时应该结合 group by 子句。而 where 子句用于标准的行级过滤。
order by 对产生的输出排序,任意列都可以使用。
group by 对行分组,但输出可能不是分组的顺序。
使用 order by 排序保证数据正确排序的唯一方法。
select order_num,
count(*) as items
from OrderItems
group by order_num
having count(*) >= 3
order by items, order_num; -- 先分组 后过滤 再排序
Access 不允许按别名排序,需要用实际的字段替代,例如:order by count(*), order_num。
在使用 select 子句时必须遵循的次序。
| 子句 | 说明 | 是否必须使用 |
|---|---|---|
| select | 要返回的列或表达式 | 是 |
| from | 从中检索数据的表 | 仅在从表选择数据时使用 |
| where | 行级过滤 | 否 |
| group by | 分组说明 | 仅在按组计算聚集时使用 |
| having | 组级过滤 | 否 |
| order by | 输出排序顺序 | 否 |
子查询,如何使用?
查询(query):一般指 select 语句。
SQL 子查询(subquery),即嵌套在其他查询中的查询。
MySQL 早期版本不支持子查询,从4.1版本开始引入。
将一条 select 语句返回的结果用于另一条 select 语句的 where 子句。
select cust_id
from Orders
where order_num in (select order_num
from OrderItems
where prod_id = 'AAAA'); -- 子查询
在 select 语句中,子查询总是从内向外处理。
手动操作,写 SQL 称之为:硬编码。
格式化 SQL:把子查询分解为多行并进行适当的缩进,能极大地简化子查询的使用。
颜色编码:代码高亮。
多层嵌套查询:
select cust_name,
cust_contact
from Customers
where cust_id in (select cust_id
from Orders
where order_num in (select order_num
from OrderItems
where prod_id = 'AAAA')); -- 子查询
可以无限嵌套查询。
作为子查询的 select 语句只能查询单个列。
效率很低。
创建计算字段,作为子查询。
select cust_name,
cust_state,
(select count(*)
from Orders
where Orders.cust_id = Customers.cust_id) as orders
from Customers
order by cust_name;
where 子句完全限定列名,它指定表名和列名。
用1个句点分隔表名和列名。
必须限制有歧义的列。
出现冲突列名而引起的歧义性,会引起 DBMS 抛出错误信息。
完全限定列名
这种写法并非最有效的,后续的 join 语法效率更高!
联结的 select 语句。
SQL 能够在数据查询的执行中联结(join)表。
关系表、关系数据库设计基础知识。
供应商表、产品信息表,分开存放。
数据一致性
数据库范式
关系数据库设计的基础、分解信息
唯一标识,主键(primary key),任何唯一值都可以。
不重复,不浪费存储空间。
如果需要变动,只需要更改一次。
可伸缩(scale)性强。
使用联结可以关联不同的表,返回一组输出。
联结并非物理实体,在实际数据库中并不存在。
DBMS 根据需要建立联结,在查询执行期间一直存在。
引用完整性,强制实施数据完整性规则。例如:外键。
select vend_name,
prod_name,
prod_price
from Vendors a,
Products b
where a.vend_id = b.vend_id; -- 联结
使用 where 子句进行正确的联结。
完全限定列名。
联结的实际操作是将2个表的每一行都进行匹配,而 where 子句则是过滤满足条件的行。
笛卡尔积:(cartesian product)检索的数目为2个表行数相乘。
select vend_name,
prod_name,
prod_price
from Vendors a,
Products b; -- 产生 N 行
因此,不要忘了 where 子句。
返回笛卡尔积的联结,也称为叉联结(cross join)。
等值联结(equijoin),基于两个表之间的相等条件。
也称为内联结(inner join)。
select vend_name,
prod_name,
prod_price
from Vendors a
inner join Products b
on a.vend_id = b.vend_id;
传递给 on 的实际条件与传递给 where 相同。
ANSI SQL 规范首选 inner join 语法。
SQL 不限制可以联结表的数目,在实际生产中,PostgreSQL 能关联10个表以上。
select prod_name,
vend_name,
prod_price,
quantity
from OrderItems a,
Products b,
Vendors c
where b.vend_id = c.vend_id
and a.prod_id = b.prod_id
and order_num = 20007; -- 多表关联
where 子句定义联结条件,过滤条件。
性能问题:联结很消耗资源,联结越多,性能越低。
第11课中的子查询写法可以使用联结写法替代。
性能可能会受操作类型、所使用的的 DBMS、表中数据量、是否存在索引或键等条件的影响。
其他联结,表别名,对联结的表使用聚集函数等。
缩短 SQL 语句
允许在一条 select 语句中多次使用相同的表
使用 as 作为别名
表别名不返回至客户端,不像列别名
self-join 自联结
natural join 自然联结
outer join 外联结
使用表别名可以在一条 select 中多次使用同一个表。
select cust_id,
cust_name,
cust_contact
from Customers
where cust_name = (select cust_name
from Customers
where cust_contact = 'Jim Jones');
通过子查询查找同公司下的用户。也可以使用自联结:
select a.cust_id,
a.cust_name,
a.cust_contact
from Customer a,
Customer b
where a.cust_name = b.cust_name
and a.cust_contact = 'Jim Jones'; -- 简洁
使用表别名。联结后过滤。
标准的联结返回所有数据,相同的列甚至多次出现。自然联结排除多次出现,去重。
select a.*
b.order_num,
b.order_date,
c.prod_id,
c.quantity,
c.item_price
from Customers as a,
Orders as b,
OrderItems as c
where a.cust_id = b.cust_id
and c.order_num = b.order_num
and prod_id = 'RGAN01';
第一个表使用通配符,其他表数据指定具体列。
包含那些在相关表中没有关联行的行,这种联结成为外联结。
select a.cust_id,
b.order_num
from Customers a
left outer join Orders b -- 左连接
on a.cust_id = b.cust_id;
在使用 outer join 语法时,必须使用 right 或者 left 关键字指定包括其他行的表。
分别表示包含左、右数据表的所有数据行。
SQLite 支持 left outer join,但不支持 right outer join。
检索两个表中的所有行并关联那些可以关联的行。
select a.cust_id,
b.order_num
from Customers a
full outer join Orders b
on a.cust_id = b.cust_id;
另外一种 cross jon 连接。
聚集函数用来汇总数据。
select a.cust_id,
count(b.order_num) as num_ord
from Customers a
inner join Orders b
on a.cust_id = b.cust_id
group by a.cust_id;
也可以与其他联结一起使用。
注意:
利用 union 操作符将多条 select 语句组合成一个结果集。
组合查询通常被称为并(union)或者复合查询(compound query)。
select cust_name,
cust_contact,
cust_email
from Customers
where cust_state in ('A', 'B', 'C')
union
select cust_name,
cust_contact,
cust_email
from Customers
where cust_name = 'xxxx';
union 指示 DBMS 执行这两条 select 语句,并把输出组合成一个查询结果集。
使用 union 组合 select 语句的数目具体没有限制。
注意考虑一下性能问题。
union 从查询结果集中自动去除了重复的行。
返回所有匹配行,不去重。
select cust_name,
cust_contact,
cust_email
from Customers
where cust_state in ('A', 'B', 'C')
union all
select cust_name,
cust_contact,
cust_email
from Customers
where cust_name = 'xxxx'; -- 不去重
使用 union 组合查询时,只能使用一条 order by 子句,且必须位于最后一条 select 语句之后。
select xxx from xxx
union all
select xxx from xxx
order by xxx; -- 排序所有结果
某些 DBMS 还支持另外两种 union:
使用 union 还可以组合不同表查询,结合别名使用。
使用 insert 语句将数据插入表中。
使用 insert 语句进行插入需要特定的操作权限。
语法:
insert into Customers
values('100','Toy','123',NULl,100);
必须给每一列提供一个值。如果某列没有值,必须使用 NULL 值填充,而且顺序应该依次出现。
into 关键字可选?某些 DBMS 支持。
这种语法适用于测试,单独插入某条数据时使用。不适用于大批量更新。
insert into Customers (cust_id, cust_name, cust_address, cust_zip, cust_email)
values('100','Toy','123',NULl,100);
明确指出列名,因此无须按顺序出现。
即使表结构发生变化,也不影响。
values 数目必须正确,才能成功插入。
只给某些列提供值,给其他列不提供值。
insert into Customers(cust_id, cust_name)
values('100', 'Toy'); -- 插入部分行
省略的列将自动插入空值 NULL。
列必须允许为空。
insert select 语法也是合法的。
insert into Customers(cust_id, cust_name,xxxx,xxx,xxx)
select cust_id,
cust_name,
xxxx,
xxx,
xxx
from CustNew;
亲测:Oracle可使用!
insert select 中可以包含 where 子句,以过滤插入的数据。
可以插入多行。
创建新的表,可以使用 select into 语句。
表不存在,新建。
insert select -- 导出数据
select into -- 导入数据
select * into CustCopy
from Customers;
MariaDB、MySQL、Oracle、PostgreSQL 和 SQLite 使用以下语法:
create table temp_xxx as
select * from xxx;
可以结合 where 和 group by 进行使用,可以联结多个表插入数据,但只能插入一个表。
使用 update 语句更新数据。
不要省略 where 子句。
更新需要特定的安全权限。
update Customers
set cust_email = 'xxxxxx'
where cust_id = '100';
set 关键字用于将新值赋给被更新的列。
没有 where 将更新所有的行。
update Customers
set cust_email = 'xxxxxx',
cust_contact = 'Sam'
where cust_id = '100';
利用键-值对更新多个值。
在 update 语句中还可以使用子查询。
使用 from 子句,用一个表的数据更新另一个表的行。
要删除某个列的值,可设置为 NULL。
update Customers
set cust_email = NULL
where cust_id = '200';
NULL 与空字符串不同。
使用 delete 语句删除数据。
不要省略 where 语句。
先通过 select 测试子集之后再修改为 delete 语句进行数据行的删除。
delete from Customers
where cust_id = '100';
where 进行数据过滤。
友好的外键:可以使用外键来严格实施关系,实施引用完整性。防止 delete 语句删除必要的数据。
delete 是删除整行数据,因此不需要列名或者通配符。
删除指定列可以通过 update 进行。
delete 不删除表本身。
delete 删除部分行
更快的清空所有行可以使用 truncate table 语句,速度更快,因为不记录数据的变动。
创建、更改和删除表。
SQL 还可以用来执行数据库和表的所有操作。
通过 create table 语句。
通过交互式界面工具创建表会自动生成并执行相应的 SQL 语句。
create table Products
(
prod_id char(10) not null,
vend_id char(10) not null,
prod_name char(254) not null,
prod_price decimal(8,2) not null,
prod_desc varchar(1000) null
); -- 数据类型及兼容性
不同 DBMS ,必须书写不同的语句。
语句格式化:多行、缩进。
每个表列要么是 NULL 列,要么是 not NULL 列。NULL 为默认设置。
只有不允许 NULL 值的列可作为主键、唯一标识。
注意NULL 值 && 空字符串‘‘的区别。
指定默认值:使用关键字 default 指定。
create table temp_cccc
(
order_num integer not null,
quantity integer not null default 1
);
默认值经常用于日期或时间戳列。引用系统日期的函数或变量,将系统日期用作默认日期。
-- 建表
create table temp_cccccc
(
id integer not null,
money varchar(10),
date2 date default sysdate -- 系统时间 默认值
);
-- 插入数据
insert into temp_cccccc(id) values(2)
-- 查询
select * from temp_cccccc
-- 1 2 2020/1/17 16:56:53
-- 默认获取系统日期
default current_date() -- MySQL
default sysdate -- Oracle
default getdate() -- SQL Server
default now() -- Access
使用 default 而不是 NULL 值,有利于计算和数据分组。
更新表定义,可以使用 alter table 语句。
不要在表中包含数据时对其进行更新。
对表增加列的数据类型(以及 NULL 和 default 的使用)有所限制。
重命名表中的列。
修改表结构:表名,列。
-- 新增一个列
alter table temp_cccccc
add data_desc varchar2(10);
更改或删除列、增加约束或增减键,使用类似语法。
-- 删除一个列
alter table temp_cccccc
drop column money;
以下操作为 Oracle 中常用的修改表结构用法,注意部分操作需要字段内容为空时方可操作。
------------ Oracle 更新表结构 ------------
-- 1.添加字段
alter table temp_xxx add col_name varchar2(10);
-- 2.删除字段
alter table temp_xxx drop column col_name;
-- 3.修改字段类型
alter table temp_xxx modify col_name integer;
-- 4.添加备注
comment on column 库名.表名.字段名 is '输入的备注';
复杂的表结构修改一般需要手动删除,重新创建表。
再根据需要,重新创建触发器、存储过程、索引和外键。
alter table 进行前备份表,防止意外。
使用 drop table 语句删除表。
drop table temp_cccc;
永久删除改表。
drop table temp_ccccc purge; -- 删除并清空回收站
启用关系规则防止意外删除有用的表。
使用 rename 语句进行重命名表名。
rename temp_cccccc to temp_xxx; -- 重命名
alter table temp_xxx rename to temp_yyy; -- 也可以
create table temp_zzz as
select * from temp_yyy;
drop table temp_yyy; -- 备份再删除
切记:备份!!备份!!备份!!
视图是虚拟的表,与包含数据的表不同,视图只包含使用时动态检索数据的查询。
利用视图可以简化某些 SQL 操作。
Microsoft Access 不支持视图。
任何需要明细数据的人不可能都要详细的去了解表结构,如何关联和对表进行联结。
作为视图,包含的只是查询。
可以对视图执行 select 操作,过滤和排序数据,将视图联结其他视图或表,甚至添加和更新数据。
性能问题:每次使用视图都必须处理查询执行时需要的所有检索。
视图必须唯一命名。
创建视图,必须具有足够的访问权限。
视图可以嵌套。
许多 DBMS 禁止在视图查询中使用 order by 子句。 -- 测试一番? Oracle 可以排序。
如果列是计算字段,需要使用别名,例如:count(*) as cnt。
视图不能索引、也不能有关联的触发器或默认值。
使用 create view 语句创建视图。
使用 drop view view_name 删除视图。
利用视图简化复杂的联结。
create view ProductCustomers as
select cust_name,
cust_contact,
prod_id
from Customers a,
Orders b,
OrderItems c
where a.cust_id = b.cust_id
and b.order_num = c.order_num; -- 创建视图
使用查询。
select * from ProductCustomers
where prod_id = 'xxx';
但 DBMS 处理查询时,将指定的 where 子句添加到视图查询中已有的 where 子句中,正确的过滤数据。
利用视图,可以一次性编写基础的 SQL,然后根据需要多次使用。
视图需要进行维护。
另一用途是重新格式化检索的数据。
create view VendorLocations as
select rtrim(vend_name) || '(' || rtrim(vend_country) || ')' as vend_title
from Vendors;
创建视图之后,通过 where 过滤数据。
视图内与传递给视图的 where 子句将自动组合。
使用视图与计算字段。
create view OrderItemsExpanded as
select order_num,
prod_id,
quantity,
item_price,
quantity * item_price as expanded_price
from OrderItems;
正确使用视图,可以极大地简化复杂数据的处理。
视图为虚拟的表。
重新格式化或保护基础数据。
存储过程是什么?为甚么要使用存储过程?如何使用?创建和使用存储过程的基本语法。
存储过程(Stored Procedure) 是在大型数据库系统中,一组为了完成特定功能的 SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来调用存储过程。
针对许多表的多条 SQL 语句,多种判断,有条件的执行其他语句。
可以视为批文件。
Microsoft Access 和 SQLite 不支持存储过程。
简单、安全、高性能!!!
缺陷:不同 DBMS 语法不同,可移植性差!
通过 execute 执行存储过程,execute 接受存储过程名和需要传递的任何参数。
execute procedure_name('a','b','c','d');
执行存储过程,插入数据。ID 自动生成。
-- Oracle -- 执行不通过 待修改
create procedure MailListCount (
ListCount out integer -- 返回一个值
)
is
v_rows integer;
begin
select count(*) into v_rows
from Customers
where cust_email is not null;
ListCount := v_rows;
end;
in -- 传递值给存储过程
out -- 从存储过程返回值
inout -- 既输入又输出
begin、end -- 存储过程代码
执行存储过程。
-- Oracle --
call MailListCount();
-- 或者 --
var ReturnValue number
exec MailListCount(:ReturnValue);
select ReturnValue;
declare 语句声明变量
@开头 为局部变量
Return @cnt 返回调用程序
-- SQL Server --
create procedure MailingListCount
as
declare @cnt integer
select @cnt = count(*)
from Customers
where cust_email is not null;
return @cnt;
-- 调用 --
declare @ReturnValue int
execute @ReturnValue = MailingListCount;
select @ReturnValue;
另外的例子,在数据库表中插入数据。
-- SQL Server --
create procedure NewOrder @cust_id char(10)
as
declare @order_num integer
select @order_max = max(order_num)
from Orders
select @order_num = @order_num + 1 -- 自动生成
insert into Orders(order_num,
order_date,
cust_id)
values(@order_num,
getdate(),
@cust_id)
return @order_num;
局部变量保存最大订单号,和系统日期。
多加注释。
-- SQL Server --
create procedure NewOrder @cust_id char(10)
as
insert into Orders(cust_id)
values(@cust_id)
select order_num = @@identity;
自动增量的列为标识字段(identity field),或者自动编号(auto number)或序列(sequence)。
结合 DBMS 特性进行编写存储过程。
利用 commit 和 rollback 语句管理事务处理。
使用事务处理(transaction processing),通过确保成批的 SQL 操作要么完全执行,要么完全不执行,来维护数据库的完整性。
设计良好的数据库模式,通过主键的唯一ID互相关联。
假设由于某种数据库故障(超出磁盘空间、安全限制、表锁等),插入数据过程无法完成,将该如何?
—— 事务处理,是一种机制。
如果没有错误发生,整组语句提交给数据库表;
如果发生错误,则进行回退(撤销),将数据库恢复到某个已知且安全的状态;
事务处理用来管理 insert、update 和 delete 语句。
不能回退 select 语句,也没必要。
不能回退 create 和 drop 操作。
管理事务的关键在于将 SQL 语分解为逻辑块,并明确规定数据何时该回退?何时不应该回退?
-- SQL Server --
begin transaction
...
commit transaction
中间的 SQL 必须完全执行或者完全不执行。
-- MariaDB or MySQL --
start transaction
...
-- Oracle --
set transaction
...
-- PostgreSQL --
begin
...
事务一直存在,没有明确标识事务结束,直到被中断。
通常,commit 用于保存更改,rollback 用于撤销。
select * from temp_cwh_test;
delete from temp_cwh_test;
rollback; -- 回滚
一般的 SQL 语句都是针对数据库表直接执行和编写的,这就是所谓的隐式提交(implicit commit),即提交操作是自动进行的。
-- SQL Server --
begin transaction
delete from OrderItems where order_num = 12345
delete from Orders where order_num = 12345
commit transaction -- 保证2条语句都执行成功时才提交
-- Oracle --
set transaction
...
...
commit;
复杂的事务可能需要部分提交或回退。
要支持回退部分事务,必须在事务处理块中的合适位置放置占位符。
在 SQL 中,这些占位符称为保留点。
创建占位符,可以使用 savepoint 语句。
-- MariaDB/MySQL/Oracle --
savepoint deletel;
-- SQL Server --
save transaction deletel;
每个保留点都要有能够标识的唯一名字。
-- SQL Server --
rollback transaction deletel;
-- Oracel --
rollback to deletel;
完整的例子:
begin transaction
insert into Customers(cust_id, cust_name)
values('100','Toy');
save transaction StartOrder;
insert into Orders(order_num, order_date, cust_id)
values(200,'2020/12/1','100');
if @@error <> 0 rollback transaction StartOrder;
insert into OrderItems(order_num, order_item, prod_id, quantity, item_price)
values(100,1,'ds',100,5,49);
if @@error <> 0 rollbak transaction StartOrder;
insert into OrderItems(order_num, order_item, prod_id, quantity, item_price)
values(100,2,'xx',100,10.99);
if @@error <> 0 rollback transaction StartOrder;
commit transaction
定义保留点,如果操作失败则事务处理能够回滚。
保留点越多越能灵活处理。
事务是必须完整执行的 SQL 语句块。利用 commit 和 rollback 语句进行管理。保留点。
如何使用游标?
结果集(result set):SQL 查询所检索出的结果。
游标(cursor)是一个存储在 DBMS 服务器上的数据库查询,不是一条 select 语句,而是被该语句检索出来的结果集。
有了游标之后,可以根据需要滚动或浏览其中的数据。
能够标记游标只读,使数据能读取,不能更新和删除。
能够控制定向操作(向前、向后、第一、最后、绝对位置、相对位置等)。
Microsoft Access 不支持游标。
SQLite 支持的游标称为步骤(step)。
声明游标,定义 select 语句和游标选项。
打开游标以供使用。
根据需要检索各行。
结束游标使用后,关闭游标,释放游标。
使用 declare 语句创建游标。
-- MySQL --
declare CustCursor cursor
for
select * from Customers
where cust_email is null;
-- Oracle --
declare cursor CustCursor
is
select * from Customers
where cust_email is null;
使用 open cursor 语句打开游标。
open cursor CustCursor;
执行查询,存储检索出的数据以供浏览和滚动。
利用 fetch 语句访问游标数据。
-- Oracle --
declare type CustCursor is ref cursor
return Customers%ROWTYPE;
declare CustRecord Customers%ROWTYPE
begin
open CustCursor;
fetch CustCursor into CustRecord;
close CustCursor;
end; -- fetch 检索当前行,放到声明的变量中,不做任何处理
-- Oracle --
declare type CustCursor is ref cursor
return Customers%ROWTYPE;
declare CustRecord Customers%ROWTYPE
begin
open CustCursor;
loop
fetch CustCursor into CustRecord;
exit when CustCursor%NOTFOUND;
...
end loop;
close CustCursor;
end; -- 循环所有行
-- SQL Server --
declare @cust_id char(10),
@cust_name char(50),
@cust_address char(50),
@cust_city char(50),
@cust_state char(50),
@cust_zip char(10),
@cust_email char(255)
open CustCursor
fetch next from CustCursor
into @cust_id,@cust_name,@cust_address,@cust_city,@cust_state,@cust_zip,@cust_email
while @@fetch_status = 0
begin
fetch next from CustCursor
into @cust_id,@cust_name,@cust_address,@cust_city,@cust_state,@cust_zip,@cust_email
end
close CustCursor
声明变量,fetch 语句检索一行并保存。
while 循环处理下一行。
释放游标所占用的资源。
-- Oracle --
close CustCursor;
-- SQL Server --
close CustCursor
deallocate cursor CustCursor;
先声明,后打开,再使用,记得关闭!
高级数据处理特性:约束、索引和触发器。
关联表和引用完整性(referential integrity)。
关系数据库设计
数据库完整性规则
约束(constraint):管理如何插入或处理数据库数据的规则。
DBMS 通过在数据库表上施加约束来实施引用完整性。
通过使用 create table 或者 alter table 语句。
特殊的约束,保证一列中的值是唯一的,而且永不改动。
唯一标识每一行。
任意两行的主键值都不相同。
主键值唯一,且不为 NULL 值。
主键值从不修改或更新。
主键值不能重用。
create table Vendors
(
vend_id char(10) not null primary key,
vend_name char(50) not null,
vend_state char(50) null,
vend_country char(50) null
);
添加关键字 primary key ,使其成为主键。
alter table Vendors
add constraint primary key (vend_id); -- 添加约束
SQL Lite 不允许使用 alter table 定义键,要求初始的 create table 语句中定义。
外键是表中的一列,其值必须在另一表的主键中。
外键是保证引用完整性的重要部分。
create table Orders
(
order_num integer not null primary key,
order_date datetime not null,
cust_id char(10) not null references Customers(cust_id)
)
使用 references 关键字,建立依赖。或者:
alter table Orders
add constraint
foreign key (cust_id) references Customers (cust_id);
外键有助于防止意外删除。
有的 DBMS 支持级联删除(cascading delete)的特性。
用来保证一列中的数据是唯一的,类似于主键。但存在区别:
表可以多个唯一约束,但只允许一个主键。
可包含 NULL 值。
可修改或更新。
可重复使用。
不能用来定义外键。
可以通过在列上定义 unique 约束做到,也可以单独使用 constraint 定义。
检查约束保证一列中的数据满足一组指定的条件。
检查最小、最大值。
指定范围。
只允许特定的值。
DBMS 会拒绝任何无效的数据。
create table OrderItems
(
order_num integer not null,
order_item integer not null,
quantity integer not null check (quantity >0)
);
或者
add constraint check(gender like '[MF]')
用户定义数据类型。
索引用来排序数据以加快搜索和排序操作的速度。
数据库的索引。类似于书籍的目录。
主键数据总是排序的,因此按主键检索特定行总是一种快速有效的操作。
可以在一个或多个列上定义索引,使 DBMS 保存内容为一个排序的列表。
注意:
索引使用 create index 语句创建。
create index prod_name_ind
on Products(prod_name);
索引必须唯一命名。
定期检查索引,优化调整索引。
触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行。
触发器可以与特定表上的 insert、update 和 delete 操作(或组合)相关联。
触发器与单个表相关联。
触发器可在特定操作执行之前或之后执行。
保证数据一致。
基于某个表的变动在其他表上执行活动。
进行额外的验证并根据需要回退数据。
-- SQL Server --
create trigger customer_state
on Customers
for insert, update
as
update Customers
set cust_state = upper(cust_state)
where Customers.cust_id = inserted.cust_id;
-- Oracle --
create trigger customer_state
after insert or update
for each row
begin
update Customers
set cust_state = upper(cust_state)
where Customers.cust_id = :OLD.cust_id
end;
约束比触发器更快。
数据安全
用户访问权限
用户授权和身份确认
grant -- 授权
revoke -- 回收权限
总结:约束、索引、触发器、授权。
基本数据库设计
表中存放的信息
表与表的关联
行中数据分解
玩具经销商使用的订单录入系统的组成部分:
5个表
Vendors表——销售产品的供应商
所有的表都应该有主键
Products表——产品目录
为实施引用完整性,应该定义外键,关联到Vendors表
Customers表——顾客信息
Orders表——顾客订单
OrderItems表——订单的实际物品
使用关系图 E-R图说明数据库表的关联方式。
使用 ODBC 进行数据库集成。
ODBC 是一个标准,能使客户端应用与不同的后端数据库或基础数据库引擎交互。
数据:
原文:https://www.cnblogs.com/hider/p/12221195.html