id name sex grade_math grade_english
1 Curry male 100 90
2 Nash male 90 95
3 James male 10 10
4 Kobe male 80 80
5 Marry female 70 100
6 Sherry female 100 95
SELECT name FROM Student;
SELECT name,sex,grade_math FROM Student;
SELECT * FROM Student;
select distinct name from Student;
select distinct name,sex from Student; --多元素,判断是否相同的依据是多个属性均相同
select name from Student limit 5; --前5个元素
select name from Student limit 5 offset 3; --从索引为3的位置(第4行)开始5个元素
select name from Student limit5,3; --同上,简写
# SQL Server/Access - TOP
# ORACLE - ROWNUM
select name,sex,grade_math from student order by grade_math; --由小到大
select name,sex,grade_math from student
order by sex,grade_math; --先按照第一个数据排序,然后按照第二个排序,从小到大
Marry female 70
Sherry female 100
James male 10
Kobe male 80
Nash male 90
Curry male 100
select name,sex,grade_math from student
order by 2,3; --检索的第2、3列,结果同上
select name,sex,grade_math from student
order by grade_math desc --按照grade_math大-小的数序排列
select name,sex,grade_math,grade_englist from student
order by grade_math desc, grade_english --desc只应用到位于其前面的列名
Curry male 100 90
Sherry female 100 95
Nash male 90 95
Kobe male 80 80
Marry female 70 100
James male 10 10
select name, sex, grade_math
from student
where grade_math = 100;
ORDER BY
和WHERE
子句时,应当让ORDER BY
位于WHERE
之后,否则会报错select name, sex, grade_math
from student
where sex = 'male'
order by grade_math;
Marry female 70
Sherry female 100
操作符 | 说明 | 操作符 | 说明 |
---|---|---|---|
= | 等于 | > | 大于 |
<> | 不等于 | >= | 大于等于 |
!= | 不等于 | !> | 不大于 |
< | 小于 | BETWEEN | 在指定的两个值之间 |
<= | 小于等于 | IS NULL | 为NULL的值 |
! | 不小于 |
- ! 和 !> MySQL不支持
- BETWEEN用法是: BETWEEN XXX AND XXX
数学成绩大于80、英语成绩大于80的男生
# 错误写法
select name, sex, grade_math, grade_english
from student
where sex = 'male' and grade_math > 80 or grade_english > 80;
Curry male 100 90
Nash male 90 95
Marry female 70 100
Sherry female 100 95
# 正确写法
select name, sex, grade_math, grade_english
from student
where sex = 'male' and (grade_math > 80 or grade_english > 80);
Curry male 100 90
Nash male 90 95
select name, sex, grade_math
from student
where grade_math in ('80','100')
Curry male 100
Kobe male 80
Sherry female 100
NOT WHERE …
意思是否定其后条件的关键字
select name, sex, grade_math
from student
where not grade_math < 80
Curry male 100
Nash male 90
Kobe male 80
Sherry female 100
为什么使用NOT?
在简单的where子句中,使用not确实没什么优势。但是在更复杂的子句中,not是非常有用的,比如在与in操作符联合使用时,可以非常简单的找出与条件列表不匹配的行
通配符 wildcard
用来匹配值的一部分的特殊字符
搜索模式 search pattern
由字面值、通配符或者两者组合构成的搜索条件
%
相当于任意个字符
select name, sex, grade_math
from student
where name like 'cu%'; --表示以cu开头的name
Curry male 100
select name, sex, grade_math
from student
where name like '%ry'; --表示以ry结尾的name
Curry male 100
Marry female 70
Sherry female 100
select name, sex, grade_math
from student
where name like '%rr%'; --表示中间有rr的name
Curry male 100
Marry female 70
Sherry female 100
select name, sex, grade_math
from student
where name like '%h%r%y'; --可以灵活使用
Sherry female 100
%
可匹配任意个字符,包括0个字符,但是不匹配null _
相当于一个字符
select name, sex, grade_math
from student
where name like '__rry' --这是两个下划线
Curry male 100
Marry female 70
[]
用来指定一个字符集,它必须匹配指定位置的一个字符
select name, sex, grade_math
from student
where name like '[CN]%' --mysql不支持此语法
SQL的通配符很有用,但是这种功能是有代价的,即通配符搜索一般比前面的其他搜索要耗费更长的处理时间
select concat(name, ' ', sex), grade_math
from student
where grade_math > 70
order by sex
Sherry female 100
Curry male 100
Nash male 90
Kobe male 80
- MySQL: concat(str1, str2)
- ACCESS / SQL Server: str1 + str2
- DB2 / Oracle / SQLite / Open Office Base : ||
LTRIM(str):去除str左侧的空格
RTRIM(str):去除str右侧的空格
select trim(name), sex, grade_math
from student
where grade_math > 90
select name,grade_math+grade_english as grade_sum
from student
where (grade_math+grade_english) > 180 --判断条件只能是原表中的列名,不可以是as后的
order by grade_sum desc --排序顺序为select结果的列名
Sherry 195
Curry 190
Nash 185
不同的数据库使用的函数可能会有差异,此处为MySQL中的函数
函数 | 作用 |
---|---|
ABS(x) | x的绝对值 |
BIN(x) | x的二进制 |
OCT(x) | x的八进制 |
HEX(x) | x的十六进制 |
FLOOR(x) | 小于x的最大整数 |
CEILING(x) | 大于x的最大整数 |
EXP(x) | e的x次方 |
GREATEST(x1, x2, …, xn) | 集合中的最大值 |
LEATEST(x1, x2, …, xn) | 集合中的最小 |
LN(x) | x的自然对数 |
LOG(x, y) | x的以y为底的对数 |
MOD(x, y) | 返回x/y的余数 |
PI() | π的值 |
RAND() | 0到1内的随机值 |
ROUND(x, n) | x的四舍五入、有n位小数的值 |
SIGN(x) | 代表数字x的符号的值 |
SQRT(x) | x的平方根 |
TRUNCATE(x, n) | 将x截断为n为小数(不是) |
常用于GROUP BY
从句的SELECT
查询中
函数 | 作用 |
---|---|
AVG(col) | col列的平均值 |
COUNT(col) | col列中非null值的个数 |
MIN(col) | col列中的最小值 |
MAX(col) | col列中的最大值 |
SUM(col) | col列所有值的和 |
GROUP_CONCAT(col) | 由属于一组的列值连接组合而成的结果 |
函数 | 作用 |
---|---|
ASCII(ch) | 字符ch的ASCII码 |
BIT_LENGTH(str) | 字符串str的比特长度 |
CONCAT(str1, …, strn) | 将字符串str1-strn连接成新的字符串 |
CONCAT_WS(sep, str1, …, strn) | 将字符串str1-strn连接成新的字符串并且用sep字符间隔 |
INSERT(str, x, y, instr) | 将字符串str从第x位置开始,y个字符场的子串替换为字符串instr |
FIND_IN_SET(str, list) | 分析逗号分隔的list列表,如果发现str,返回str在list中的位置 |
LCASE(str) / LOWER(str) | 将字符串str中所有字符改为小写 |
LEFT(str, x) | 字符串str中最左边的x个字符 |
LENGTH(str) | 字符串str的字符数 |
LTRIM(str) | 将字符串str开头的空格删除 |
POSITION(subset, str) | 子串substr在字符串str中第一次出现的位置 |
QUOTE(str) | 用反斜杠转义str中的单引号 |
REPEAT(str, count) | 将字符串str重复count次 |
REVERSE(str) | 将字符串str颠倒 |
RIGHT(str, x) | 字符串str中最右边的x个字符 |
RTRIM(str) | 将字符串str结尾的空格 |
STRCMP(str1, str2) | 比较字符串str1和str2(可以理解为比较ASCII码值) |
TRIM(str) | 去除字符串str首部和尾部的所有空格 |
UCASE(str) / UPPER(str) | 将字符串str中所有字符改为大写 |
函数 | 作用 |
---|---|
CURDATE() / CURRENT_DATE() | 返回当前的日期 |
CURTIME() / CURRENT_TIME() | 返回当前的时间 |
DATE_ADD(date, INTERVAL int keyword) | 返回日期date加上间隔时间int的结果 |
DATE_FORMAT(date, fmt) | 依照指定的fmt格式格式化日期date的值 |
DATE_SUB(date, INTERVAL int keyword) | 返回日期date减去间隔时间int的结果 |
DAYOFWEEK(date) | 返回日期date所代表的一星期中的第几天 |
DAYOFMONTH(date) | 返回date是一个月的第几天(1~31) |
DAYOFYEAR(date) | 返回date是一年的第几天(1~366) |
DAYNAME(date) | 返回date的星期名 |
FROM_UNIXTIME(ts,fmt) | 根据指定的fmt格式,格式化UNIX时间戳ts |
HOUR(time) | 返回time的小时值(0~23) |
MINUTE(time) | 返回time的分钟值(0~59) |
MONTH(date) | 返回date的月份值(1~12) |
MONTHNAME(date) | 返回date的月份名 |
NOW() | 返回当前的日期和时间 |
QUARTER(date) | 返回date在一年中的季度(1~4) |
WEEK(date) | 返回日期date为一年中第几周(0~53) |
YEAR(date) | 返回日期date的年份(1000~9999) |
一些示例:
# 获取当前系统时间:
SELECT FROM_UNIXTIME(UNIX_TIMESTAMP());
SELECT EXTRACT(YEAR_MONTH FROM CURRENT_DATE);
SELECT EXTRACT(DAY_SECOND FROM CURRENT_DATE);
SELECT EXTRACT(HOUR_MINUTE FROM CURRENT_DATE);
# 返回两个日期值之间的差值(月数):
SELECT PERIOD_DIFF(200302,199802);
# 在Mysql中计算年龄:
SELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(NOW())-TO_DAYS(birthday)),'%Y')+0 AS age FROM employee;
# 这样,如果Brithday是未来的年月日的话,计算结果为0。
# 下面的SQL语句计算员工的绝对年龄,即当Birthday是未来的日期时,将得到负值。
SELECT DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(birthday, '%Y') -(DATE_FORMAT(NOW(), '00-%m-%d') '00-%m-%d' )) AS age from employee
函数 | 作用 |
---|---|
AES_ENCRYPT(str,key) | 返回用密钥key对字符串str利用高级加密标准算法加密后的结果,调用AES_ENCRYPT的结果是一个二进制字符串,以BLOB类型存储 |
AES_DECRYPT(str,key) | 返回用密钥key对字符串str利用高级加密标准算法解密后的结果 |
DECODE(str,key) | 使用key作为密钥解密加密字符串str |
ENCRYPT(str,salt) | 使用UNIXcrypt()函数,用关键词salt(一个可以惟一确定口令的字符串,就像钥匙一样)加密字符串str |
ENCODE(str,key) | 使用key作为密钥加密字符串str,调用ENCODE()的结果是一个二进制字符串,它以BLOB类型存储 |
MD5() | 计算字符串str的MD5校验和 |
PASSWORD(str) | 返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法 |
SHA() | 计算字符串str的安全散列算法(SHA)校验和 |
SELECT ENCRYPT('root','salt');
SELECT ENCODE('xufeng','key');
SELECT DECODE(ENCODE('xufeng','key'),'key');#加解密放在一起
SELECT AES_ENCRYPT('root','key');
SELECT AES_DECRYPT(AES_ENCRYPT('root','key'),'key');
SELECT MD5('123456');
SELECT SHA('123456');
MySQL有4个函数是用来进行条件操作的,这些函数可以实现SQL的条件逻辑,允许开发者将一些应用程序业务逻辑转换到数据库后台。
MySQL控制流函数:
CASE WHEN[test1] THEN [result1]…ELSE [default] END如果testN是真,则返回resultN,否则返回default
CASE [test] WHEN[val1] THEN [result]…ELSE [default]END 如果test和valN相等,则返回resultN,否则返回default
IF(test,t,f) 如果test是真,返回t;否则返回f
IFNULL(arg1,arg2) 如果arg1不是空,返回arg1,否则返回arg2
NULLIF(arg1,arg2) 如果arg1=arg2返回NULL;否则返回arg1
函数 | 作用 |
---|---|
DATE_FORMAT(date,fmt) | 依照字符串fmt格式化日期date值 |
FORMAT(x,y) | 把x格式化为以逗号隔开的数字序列,y是结果的小数位数 |
INET_ATON(ip) | 返回IP地址的数字表示 |
INET_NTOA(num) | 返回数字所代表的IP地址 |
TIME_FORMAT(time,fmt) | 依照字符串fmt格式化时间time值 |
其中最简单的是FORMAT()函数,它可以把大的数值格式化为以逗号间隔的易读的序列。
SELECT FORMAT(34234.34323432,3);
SELECT DATE_FORMAT(NOW(),'%W,%D %M %Y %r');
SELECT DATE_FORMAT(NOW(),'%Y-%m-%d');
SELECT DATE_FORMAT(19990330,'%Y-%m-%d');
SELECT DATE_FORMAT(NOW(),'%h:%i %p');
SELECT INET_ATON('10.122.89.47');
SELECT INET_NTOA(175790383);
为了进行数据类型转化,MySQL提供了CAST()函数,它可以把一个值转化为指定的数据类型。类型有:BINARY,CHAR,DATE,TIME,DATETIME,SIGNED,UNSIGNED
SELECT CAST(NOW() AS SIGNED INTEGER),CURDATE()+0;
SELECT 'f'=BINARY 'F','f'=CAST('F' AS BINARY)
函数 | 作用 |
---|---|
DATABASE() | 返回当前数据库名 |
BENCHMARK(count,expr) | 将表达式expr重复运行count次 |
CONNECTION_ID() | 返回当前客户的连接ID |
FOUND_ROWS() | 返回最后一个SELECT查询进行检索的总行数 |
USER()或SYSTEM_USER() | 返回当前登陆用户名 |
VERSION() | 返回MySQL服务器的版本 |
SELECT DATABASE(),VERSION(),USER();
SELECT BENCHMARK(9999999,LOG(RAND()*PI())); --该例中,MySQL计算LOG(RAND()*PI())表达式9999999次。
vend_id vend_name vend_city
BRS01 Bears R US Los Angeles
DLL01 Doll House Inc. Golden States
FNG01 Fun and Games New York
pro_id pro_name pro_price vend_id
1 Fish bean bag toy 3.4900 BRS01
2 Bird bean bag toy 3.4900 BRS01
3 Rabbit bean bag toy 3.4900 BRS01
4 8 inch teddy bear 5.9900 DLL01
5 12 inch teddy bear 8.9900 DLL01
6 18 inch teddy bear 11.9900 DLL01
7 Raggedy Ann 4.9900 BRS01
8 King doll 9.4900 FNG01
9 Queen doll 9.5900 FNG01
select count(*) as num_pro
from products
where vend_id = 'BRS01'
num_pro
4
select vend_id, count(*) as num_pro
from products
group by vend_id
vend_id num_pro
BRS01 4
DLL01 3
FNG01 2
select vend_id, count(*) as num_pro
from products
where pro_price >= 4
group by vend_id
having count(*) >= 2
vend_id num_pro
DLL01 3
FNG01 2
ORDER BY | GROUP BY |
---|---|
对产生的输出排序 | 进行分组,但输出可能不是分组的顺序 |
任意列都可以使用 | 只可能使用选择列或表达式列,而且必须使用每个选择列表达式 |
不一定需要 | 如果与密集函数一起使用列(或表达式),则必须使用 |
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 尽在安组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
select pro_name
from products
where vend_id in (select vend_id
from vendors
where vend_city = 'Golden States')
pro_name
8 inch teddy bear
12 inch teddy bear
18 inch teddy bear
select pro_name,
(select vend_id --可以放在查询字段中
from vendors
where vend_city = 'Golden States')
from products
order by pro_name
select pro_name, vend_name
from products, vendors
where products.vend_id = vendors.vend_id
SELECT cust_name, cust_contact
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.order_num --条件中的列名,应当为结果中的列名
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01'
Oracle中,没有AS关键字,直接写成
Customers C
即可
例子: 假如要给与Jim Jones同一公司的所有顾客发送一封信件
# 使用子查询 #
SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
FROM Customers
WHERE cust_contact = 'Jim Jones')
# 使用自联结 #
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
AND c2.cust_contact = 'Jim Jones'
无论何时对表进行联结,应该至少有一列不止出现在一个表中(被联结的列)。标准的联结(前一课中介绍)
SELECT Customers.cust_id, COUNT(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id
SELECT Customers.cust_id, COUNT(Orders.order_num) AS num_ord
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id
多数SQL查询只包含一个或多个表中返回数据的单条SELECT语句。但是SQL也允许执行多个查询(多条SELECT语句),并将结果作为一个查询结果集返回。这些组合查询通常称为并(union)或符合查询(compound query)。
主要有两种情况需要使用组合查询:
例子:需要Illinois、Indiana和Michigan这三个州所有顾客的保镖,家乡包括不管位于哪个周的所有的Fun4A11
# 使用UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL', 'IN', 'MI')
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4A11'
# 使用多条WHERE子句
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL', 'IN', 'MI')
OR cust_name = 'Fun4A11'
UNION的限制
使用UNION组合SELECT语句的数目,SQL没有标准的限制,但是最好参考一下具体的DBMS文档。
性能问题
多数好的DBMS使用内部查询优化程序,在处理各条SELECT语句前组合它们。从理论上来讲,着意味着从性能上看两种方式应该没有实际的差别
UNION
必须由两条或两条以上的SELECT
语句组成,语句之间用关键字UNION
分隔UNION
中的每个查询必须包含相同的列、表达式或聚集函数(不过,各个列的顺序可以不同)UNION ALL
在使用UNION组合查询时,只能使用一条ORDER BY子句,它必须位于最后一条SELECT语句之后(对于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY语句)
INSERT INTO Customers
VALUES('1000006',
'Toy Land',
'123 Any Street',
'New York',
NULL)
虽然这种语法很简单,但是并不安全,应该尽量避免使用。上面的SQL语句高度依赖于表中列的定义次序,还依赖于其容易获得的次序信息。机试可以得到这种次序信息,也不能保证各列在下一次表结构变动后保持完全相同的次序。因此,编写依赖于特定列次序的SQL语句是很不安全的。
更安全的方法如下:
INSERT INTO Customers(cust_id,
cust_name,
cust_address,
cust_city,
cust_email)
VALUES('1000006',
'Toy Land',
'123 Any Street',
'New York',
NULL)
这种语法指定的列的次序不一定是各列在表中的实际次序
如果表的定义允许,则可以在INSERT操作中省略某些行。省略的列必须满足以下某个条件:
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city)
SELECT cust_id, cust_name, cust_address, cust_city --select前若加上values会报错
FROM CustomersNew
各个DBMS语法稍有不同,下面的是MySQL语法
CREATE TABLE CustCopy AS
SELECT * FROM Customers
在使用SELECT INTO时:
注意不要省略WHERE子句。在使用UPDATE时一定要细心,因为稍不注意就会更新表中的所有行
在客户端/服务器的DBMS中,使用UPDATE语句可能需要特殊的安全权限,在使用UPDATE前,应该保证自己有足够的安全权限
三部分:
UPDATE Customers
SET cust_name = 'pokerface',
cust_email = '[email protected]',
cust_tel = null
WHERE cust_id = '10000006'
小心使用,注意添加WHERE子句
需要特殊的权限
DELETE FROM Customers
WHERE cust_id = '1000006'
TRUNCATE TABLE
create table Products
( --注意是小括号
pro_id char(10) not null,
pro_name char(254) not null,
pro_price decimal(8,2) not null,
vend_id char(10) not null
prod_desc varchar(1000) null
)
在创建新的表时,指定的表名必须不存在,否则会出错。为了防止意外覆盖已有的表,SQL要求首先手工删除该表,然后再重建它,而不是简单的用创建表语句覆盖它。
每个列或者是NOT NULL,或者就是NULL;NULL表示为输入值可以为空
指定null
如果不指定not null,多数dbms认为指定的是null
主键和null值
只有not null的列可以作为主键,null的列不能作为唯以标识
理解null
null值不是没有值,不是空字符串。如果指定”,这在not null列中是允许的。空字符串是一个有效的值,不是无值。null值用关键字null而不是空字符串指定
null在算数表达式中的应用
create table OrderItems
(
order_num integer not null,
pro_id char(10) not null,
quantity integer not null default 1
)
DBMS | 函数 |
---|---|
Access | NOW() |
DB2 | CURRENT_DATE |
MySQL | CURRENT_DATE() |
Oracle | SYSDATE |
PostgraSQL | CURRENT_DATE |
SQL Server | GETDATE() |
SQLite | date(‘now’) |
例:给Vendors表增加一个名为vend_phone的列,其数据类型为char
ALTER TABLE Vendors
ADD vend_phone CHAR(20);
例:删除Vendors表中vend_phone列
ALTER TABLE Vendors
DROP COLUMN vend_phone
DROP TABLE CustCopy
删除表没有确认,也不能撤销,执行这条语句将永久删除该表
RENAME TABLE Customers TO CustomersNew
具体略,自行研究,很简答
简单来说,存储过程就是为以后使用而保存的一条或多条SQL语句。可将其视为批处理文件,虽然它们的作用不仅限于批处理。
Access和SQLite不支持存储过程,MySQL5开始支持存储过程
换句话说,使用存储过程有三个主要的号处,即:简单、安全、高性能
不过使用存储过程也有一些缺陷:
EXECUTE AddNewProduct( 'JTS01',
'Stuffed Eiffed Tower',
6.49,
'Plush stuffed toy with the text La....');
分析:
这里执行一个名为AddNewProduct的存储过程,将一个新产品添加到Products表中。AddNewProductyou有4个参数,分别是:供应商ID(Vendors表的主键)、产品名、价格和描述。着4个参数匹配存储过程中的4个预期变量(定义为存储过程自身的组成部分)。此存储过程将新行添加到Products表,并将传入的属性赋值给相应的列。
以下是存储过程完成的工作:
14.4 创建存储过程
例子:对邮件发送清单中具有邮件地址的顾客进行计数:
# Oracle版本
CREATE PROCEDURE MailingListCount (
ListCount OUT INTEGER
)
IS
v_rows INTEGER;
BEGIN
SELECT COUNT(*) INTO v_rows
FROM Customers
WHERE NOT cust_email IS NULL;
ListCount := v_rows;
END;
分析:
这个存储过程有一个名为ListCount的参数。此参数从存储过程反悔一个值而不是传递一个值给存储过程。关键字OUT用来指示这种行为。Oracle支持IN(传递值给存储过程)、OUT(从存储过程返回值)、INOUT(既传递值给存储过程也从存储过程传回值)类型的参数。存储过程的代码括在BEGIN和END语句中,这里执行一条简单的SELECT语句,它检索具有邮件地址的顾客。然后用检索出的行数设置ListCount(要传递的输出参数)
调用Oracle例子可以像下面这样:
var ReturnValue NUMBER
EXEC MailingListCount(:ReturnValue);
SELECT ReturnValue;
这段代码声明了一个变量来保存存储过程返回的任何值,然后执行存储过程,再使用SELECT语句显示返回的值
使用事务处理(transaction processing),通过确保成批的SQL操作要么完全执行,要么完全不执行,来维护数据库的完整性
事物处理是一种机制,用来管理必须成批执行的SQL操作,保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们要么完全执行,要么完全不执行(除非明确指示)。如果没有错误发生,整租i语句提交给(写到)数据库表;如果发生错误,则进行回退(撤销),将数据库恢复到某个已知且安全的状态
BEGIN TRANSACTION
...
COMMIT TRANSACTION
SET TRANSACTION
...
DELETE * FROM Orders WHERE price = 0;
ROLLBACK;
一般的SQL语句都是针对数据库表直接执行和编写的,这就是所谓的隐式提交(implicit commit),即提交(写或保存)操作是自动进行的
在事务处理块中,提交不会隐式进行
BEGIN TRANSACTION
DELETE OrderItems WHERE order_num = 12345
DELETE Orders WHERE order_num = 12345
COMMIT TRANSACTION
最后的COMMIT语句尽在不出错时写出更改,如果第一条DELETE起作用,但第二条失败,则DELETE不会提交
在MySQL、Oracle中创建保留点,可以使用SAVEPOINT语句
SAVEPOINT delete1
每个保留点都要取能够标识它的唯一名字,以便在回退时,DBMS知道回退到何处。要回退到上述保留点,可如下操作:
ROLLBACK TO delete1
有时,需要在检索出来的刚中前几年或者后退一行或多行,这就是游标的用途所在。游标(cursor)是一个存储在DBMS服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据
不同的DBMS支持不同的游标选项和特性,常见的一些选项和特性如下:
声明游标后,可根据需要频繁的打开和关闭游标。在游标打开时,可根据需要频繁的执行取操作
例子:创建一个游标来检索没有电子邮件地址的所有顾客
# 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
16.2.2 使用游标
使用OPEN CURSOR
语句打开游标,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;
下面这个例子使用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;
CLOSE CustCursor
约束(constraint):管理如何插入或处理数据库数据的规则
虽然可以在插入新行是进行检查(在另一个表上执行SELECT,以保证所有值合法并存在),但是最好不要这样做,原因如下:
执行客户端检查是非常耗时的,而DBMS执行这些检查会相对高效
DBMS通过在数据库表上时间约束来实施引用完整性,大多数约束是在表定义中定义的,比如用CREATE TABLE
/ ALTER TABLE
语句
主键是一种特殊的约束,用来保证一列(或一组列)中的值是唯一的,而且用不改动。换句话说,表中的一列或多个列的值唯一标识表中的每一行。
表中任意列只要满足以下条件,就都可以用作主键:
create table vendors
(
vend_id char(10) not null primary key,
vend_name char(50) not null,
vend_add char(50) not null
);
alter table vendors
add constraint primary key (vend_id); --也可用于CREATE TABLE和ALTER TABLE中
外键是表中的一列,其值必须列在另一表的主键中。外键是保证引用完整性的极其重要的部分。
# 使用navicat自动生成的语法版本
create table orders (
`order_no` integer not null,
`order_date` datetime not null,
`cust_id` char(10) not null,
primary key (`order_no`),
constraint `cust_id` foreign key (`cust_id`) reference `customers`(`cust_id`)
)
唯一约束用来保证一列(或一组列)中的数据是唯一的。他们类似于主键,但是存在以下重要区别:
检查约束用来保证一列(或一组列)只能够的数据满足一组指定的条件。检查约束的常见用途有以下几点:
create table orderitems(
order_num integer not null;
quantity integer not null check (quantitiy > 0),
...
)
add constraint check (gender like '[MF]')
索引用来排序数据以加快搜索和排序操作的速度。
例如,如果想搜索住在某个州的客户,表中数据并未按照州排序,DBMS必须独处表中所有的行,看是否匹配。结果方法是使用索引,可以在一个或多个列上定义索引,是DBMS保存其内容的一个排过序的列表。在定义了索引后,DBMS以使用书的索引类似的方式使用它。DBMS搜索排过序的索引,找出匹配的位置,然后检索这些行(个人觉得可以理解为哈希)
可以在索引中定义多个列(如省加上市)。这样的索引仅在以省加上市的顺序排序时有用。如果想按照城市排序,则这种索引没有用处
索引用CREATE INDEX
语句创建
CREATE INDEX pro_name_ind ON PRODUCTS (prod_name);
索引必须唯一命名,pro_name_ind为索引名,pro_name为列名
索引的效率随表的数据增加或改变而变化,最好定期检查索引,并根据需要对索引进行调整
触发器是特殊从存储过程,它在特定的数据库活动发生时自动执行。触发器可以与特定表上的INSERT、UPDATE和DELETE操作(或组合)相关联
触发器内的代码具有以下数据的访问权:
DELETE操作中删除的数据
下面是触发器一些常见的用途:
保证数据一致。例如在INSERT或UPDATE操作中奖所有州名转换为大写
例子:在所有INSERT和UPDATE操作时,将Customers表中的cust_state列转换为大写
# 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语句
|
符号用来表示几个中选择一个[]
表示其中的内容是可选的用来更新已经存在表的结构
alter table tableName (
add|drop column dataType [null|not null] [constraints],
...
);
用来将事务写入数据库
commit [transaction];
用于在一个或多个列上创建索引
create index indexName on tableName (column, ...);
用于创建存储过程
create procedure procedureName [parameters][options]
as
SQL statement;
用于创建新数据库表
create table tableName (
add|drop column dataType [null|not null] [constraints],
...
);
用来创建一个或多个表上的新视图
create view viewName as
select columnName, ...
from table, ...
[where ...]
[group by ...]
[having ...];
用于从表中删除一行或多行数据
delete from tableName
[where ...];
永久地删除数据库对象(表、视图、索引等)
drop index|procedure|table|view indexName|procedureName|tableName|viewName;
为表添加一行数据
insert into tableName [(column, ...)] values(value, ...);
将select的结果插入到一个表中
insert into tableName [(column, ...)]
select column, ... from tableName, ...
[where ...];
用于撤销一个事务块
rollback [to savePointName];
用于从一个或多个表(视图)中检索数据
select columnName, ...
from tableName, ...
[where ...]
[union ...]
[group by ...]
[having ...]
[order by ...]
用于更新表中的一行或多行
update tableName
set columnName = value, ...
[where ...];
有两种基本的字符串类型,分别为定长字符串和变长字符串
数据类型 | 说明 |
---|---|
char | 1-255个字符的定长字符串,它的长度必须在创建时规定 |
nchar | char的特殊形式,用来支持多字节或unicode字符(此类型的不同实现变化很大) |
nvarchar | text的特殊形式,用来支持多字节或unicode字符(系列性的不同实现变化很大) |
text(也称为long、memo或varchar) | 变长文本 |
- 字符串使用引号
不管使用何种形式的字符串数据类型,字符串值必须都括在单引号内
丢失数字
比如电话号码邮政编码,如果保存为数值字段中,则可能会丢失数字(比如0123会保存为123)。
需要遵守的基本规则是:如果树枝是计算(求和、平均等)中使用的数值,则应该存储在树枝数据类型列中;如果作为字符串(可能只包含数字)使用,则应该保存在字符串数据类型列中
数据类型 | 说明 |
---|---|
bit | 蛋哥二进制位值,或者为0或者为1,主要用于开/关标志 |
decimal / numeric | 定点或精度可变的浮点值 |
float / number | 浮点值 |
tinyint | 1字节整数值,支持0 ~ 255的数 |
smallint | 2字节整数值,支持-32768 ~ 32767的数 |
real | 4字节浮点值 |
int / integer | 4字节整数值,支持-2147483648 ~ 2147483647的数 |
- 不使用引号
- 货币数据类型
多出DBMS支持,一般记为money / currency,这些数据类型基本上是有特定取值范围的decimal数据类型,更适合存储货币值
不存在所有DBMS都理解的定义日期的标准方法,格式大多不同
数据类型 | 说明 |
---|---|
date | 日期值 |
datetime / timestamp | 日期时间值 |
smalldatetime | 日期时间值,精确到分(无秒或毫秒) |
time | 时间值 |
二进制数据类型是最不具有兼容性的数据类型,也是最少使用的。
二进制数据类型可包含任何数据,甚至可以包含二进制信息,如图像、多媒体、字处理文档等
数据类型 | 说明 |
---|---|
binary | 定长二进制数据(最大长度从255b到8000b,依赖于其具体的是实现) |
long raw | 变长二进制数据,最长2gb |
raw | 定长二进制数据,最长255b |
varbinary | 变长二进制数据(最大长度一般在255b到8000b,依赖于具体的实现) |