Skip to main content

SQL In 10 minutes

v5

1 了解SQL

数据库基础

数据库:数据库是以某种有组织的方式存储的数据集合。 表:一种结构化的文件,可以用来存储某种特定类型的数据。某种特定类型数据的结构化清单。 模式:关于数据库和表的布局及特性的信息。 列:表中的一个字段。所有表都有一个或多个列组成的。 数据类型:允许什么类型的数据。每个表列都有相应的数据类型,它限制该列中存储的数据。 数据类型兼容:数据类型及其名称是SQL不兼容的一个主要原因。虽然大多数基本数据类型得到了一致的支持,但许多高级的数据类型却没有。更糟糕的是,偶尔会有相同的数据类型在不同的DBMS中有不同的名称。 行:表中的一条记录。 主键(primary key):一列或几列,其值能够唯一标识表中每一行。 应该总是定义主键:虽然并不总是需要主键,但多数数据库设计者都会保证每个表中有主键,以便于以后的数据操作和管理。

  • 任意两列不具有相同的主键值
  • 每一行都必须具有一个主键值(主键列不允许空值NULL)
  • 主键列不允许修改或更新
  • 主键不能重用(如果某行从表中删除,他的主键不能赋值给以后的新行)

什么是SQL

Structured Query Language结构化查询语言 设计SQL的目的:提供一种从数据库中读写数据的简单有效的方法。 标准SQL由ANSI标准委员会管理,从而称为ANSI SQL。 推荐DBMS:

  • MySQL(或派生的MariaDB)
  • Microsoft SQL Server Express

2 检索数据

官网附录SQL

SELECT语句

检索单个列

SELECT prod_name 
FROM Products;

多条SQL必须以分号(;)分割 SQL语句不区分大小写,许多开发人员喜欢对SQL关键字使用大写,而对列名和表名使用小写,这样做代码更易于阅读和调试。

SELECT prod_name 
FROM Products;

SELECT prod_name FROM Products;

SELECT
prod_name
FROM
Products;

多数SQL开发人员认为,将SQL语句分成多行更容易阅读和调试。

检索多个列

SELECT prod_id, prod_name, prod_price
FROM Products;

检索所有列

SELECT * 
FROM Products;

一般而言,除非你确实需要表中的每一列,否则最好别使用*通配符。虽然省事,但检索不需要的列通常会降低检索速度和应用程序的性能。 使用通配符最大的优点是可以检索出未知的列。

检索不同的值

SELECT DISTINCT vend_id
FROM Products;

不能部分使用DISTINCT,该关键字作用于所有列。

限制结果

只检索前5行。 SQLServer

SELECT TOP 5 prod_name
FROM Products;

Oracle:

SELECT prod_name
FROM Products
WHERE ROWNUM <=5;

MySQL

SELECT prod_name
FROM Products
LIMIT 5;

-- 6-10行
SELECT prod_name
FROM Products
LIMIT 5 OFFSET 5;
-- OFFSET从0开始

SELECT prod_name
FROM Products
LIMIT 1, 3;1开始3行,与上面同义。

使用注释

-- 推荐两个连字符 单行,或行内
-- --------------------
-- 多行注释
-- --------------------


# 单行
/*
多行,这两种方式有些DBMS不支持
*/

3 排序检索数据

SELECT的ORDER BY子句

排序数据

子句:由一个关键字加上所提供的数据组成。例如SELECT语句的FROM子句。

SELECT prod_name 
FROM Products
ORDER BY prod_name;

ORDER BY子句应该放在SELECT语句的最后,否则会出错。 可以通过非选择列进行排序,是完全合法的。

按多个列排序

-- 先对价格排序再对名称排序
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price, prod_name;

按列位置排序

SELECT prod_id, prod_price, prod_name 
FROM Products
ORDER BY 2, 3; -- 相当于 ORDER BY prod_price, prod_name;

优点是不用重复输入列名,缺点是如果排序的列不在选择列中,则不能使用。

指定排序方向

SELECT prod_id, prod_price, prod_name 
FROM Products
ORDER BY prod_price DESC, prod_name;

DESC是降序,ASC是升序,升序是默认不写也行。

区分大小写和排序顺序 A与a相同吗?a位于B之前还是Z之后,其答案取决于数据库的设置方式。ORDER BY做不到,需要DBA的帮助。

4 过滤数据

SELECT语句的WHERE子句指定搜索条件。

使用WHERE子句

数据库表一般包含大量的数据,很少检索表中所有行。只检索所需数据需要指定搜索条件(search condition),搜索条件也成为过滤条件(filter condition)。 WHERE子句在表名之后给出。

SELECT prod_name, prod_price 
FROM Products
WHERE prod_price = 3.49;

SQL过滤和应用过滤 客户端应用过滤将会极大的影响应用的性能,此外服务器不得不通过网络发送多余的数据,导致网络带宽浪费。

WHERE子句操作符

  • 大于 小于 等于 不等于<> 大于等于 小于等于
  • BETWEEN AND 范围检查
  • IS NULL 空值检查

字符串需要使用单引号例如 vend_id != 'DLL01';

5 高级数据过滤

如何组合WHERE子句以建立功能更强、更高级的搜索条件。还将学习NOT和IN操作符。

组合WHERE子句

AND操作符

SELECT prod_id, prod_price, prod_name 
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;

OR操作符

SELECT prod_id, prod_price, prod_name 
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';

求值顺序

优先处理AND,其次是OR操作符。有括号优先运算括号中的式子。

IN操作符

SELECT prod_price, prod_name 
FROM Products
WHERE vend_id IN ('DLL01', 'BRS01');

IN操作符优点:

  • 语法直观
  • 与AND和OR组合使用时,求值顺序更容易管理
  • IN比一组OR操作符执行更快(数量少的时候)
  • IN的最大优点是可以包含其他SELECT语句,能够动态的建立WHERE子句。

阿里开发手册:IN操作能避免就避免,若避免不了,评估后使元素数量保持在1000以内。

in的效率是比较低的,但不大会和LEFT JOIN做比较,一般是和exists做比较的。 in的实质就是无数个=,中间用OR连接起来 比如IN (1,2,3)实质上就是=1 or =2 or =3,效率自然不高 为什么说SQL语句中使用IN性能不高?_sql in 效率-CSDN博客

NOT操作符

有且仅有一个功能,就是否定其后所跟的任何条件。

SELECT prod_price, prod_name 
FROM Products
WHERE NOT vend_id IN ('DLL01', 'BRS01');

6 用通配符进行过滤

LIKE操作符

前面都是基于已知值进行过滤,而针对未知值,例如怎样搜索产品中包含文本bean bag的所有产品?就得使用通配符。 通配符(wildcard):用来匹配值的一部分的特殊字符。 搜索模式:由字面值、通配符或两者组合构成的搜索条件。 使用通配符必须使用LIKE操作符。 通配符搜索只能用于文本字段,非文本数据类型不能使用。

百分号通配符

%表示任何字符出现任意次数。

SELECT prod_id, prod_name 
FROM Products
WHERE prod_name LIKE 'Fish%';

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '%Fish%';

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'F%y';

后面所跟的空格,如果某列有50个字符,单钱存储了17个字符后面补充33个空格,检索是引用'F%y%',在MySQL中,后面不用再加%。

请注意NULL %不会匹配产品名称为NULL的行。

下划线_通配符

匹配单个字符。 DB2不支持_。

SELECT prod_id, prod_name 
FROM Products
WHERE prod_name LIKE '__ inch teddy bear'; -- 匹配两个字符

方括号通配符

用来指定一个字符集,必须匹配指定位置中的一个字符。

LIKE '[JM]%'

SQL Server支持。MySQL不支持。

通配符的技巧

通配符很有用,但是这种功能是有代价的,即比前面讨论的普通搜索要耗费更长的处理时间。

  • 不要过度使用通配符。如果有其他方案,应采用其他方案。
  • 在确实需要使用通配符时,尽量不要放到搜索模式的开始处。

7 创建计算字段

计算字段

计算字段是运行时在SELECT语句内创建的。

字段用在计算字段场合下,列是用在数据库表中

客户端与服务器的格式:

在数据服务器上完成这些操作比在客户端中完成要快得多。

拼接字符串

可用+或两个竖杠(||)标识,在MySQL和MariaDB中,必须使用特殊的函数

SELECT CONCAT(vend_name, '(', vend_country, ')')  
FROM Vendors;

Tip: 在图形化工具中,建议先写表名,在写字段名,工具会给予字段名称提示。

去空格

RTRIM去掉右边的空格,LTRIM去掉左边的空格

SELECT RTRIM(vend_name),LTRIM(vend_name)  
FROM Vendors;

别名

SELECT CONCAT(vend_name, '(', vend_country, ')')  
AS vend_title
FROM Vendors;

AS 通常可选,最好使用它

执行算数计算

SELECT prod_price * 1 + 2 - 1 / 3 % 2 
FROM Products;

8 使用函数处理数据

函数

上面的RTRIM就是一个函数。

与SQL语句不一样,SQL函数不是可移植的。意味着为特定SQL实现编写的代码在其他实现中可能不能用。

许多SQL程序员不在城使用特定于实现的功能。虽然这样做有好处,但有时候并不利于应用程序的性能。

使用函数

  • 文本字符串
  • 数值
  • 日期时间
  • 内容格式化
  • 返回DBMS正使用的特殊信息

文本处理函数

SELECT prod_name , UPPER(prod_name)  
FROM Products;

img

MySQL不支持LEFT方法。

SELECT cust_name , cust_contact 
FROM Customers
WHERE SOUNDEX(cust_contact) = SOUNDEX('Michael Green') ;
-- Kids Place | Michelle Green

SOUNDEX()函数匹配所有发音类似的值。

日期和时间处理函数

日期和时间采用相应的数据类型存储在表中,每种DBMS都有自己的特殊形式。可移植性最差。

在MySQL中

SELECT order_num, order_date, cust_id 
FROM Orders
WHERE YEAR(order_date) = 2020 ;
SELECT order_num, order_date, cust_id
FROM Orders
WHERE DATE(order_date) BETWEEN '2021-01-01' AND '2021-12-31';
SELECT order_num, order_date, cust_id
FROM Orders
WHERE order_date >= '2021-01-01' AND order_date <= '2021-12-31';
SELECT order_num, order_date, cust_id
FROM Orders
WHERE order_date BETWEEN '2021-01-01' AND '2021-12-31';

数值处理函数

一般主要用于代数、三角或几何运算,不如字符串和日期-时间处理函数使用那么频繁。

img

9 汇总数据

聚集函数

汇总数据而不需要把他们检索出来

例如:

  • 确定行数;
  • 获取表中某些行的和;
  • 找出表列的最大值、最小值、平均值

AVG函数只用于单列,并且忽略null值。

COUNT(*)和COUNT(column)区别是列会忽略null值。

MAX既适用于数值,也适用于日期和字符串。忽略null值。

SUM也可适用于多个列上进行计算

SELECT AVG(prod_price), COUNT(*), MAX(prod_price), MIN(prod_price), SUM(prod_price) 
FROM Products;

SELECT SUM(quantity*item_price)
FROM OrderItems ;

聚集不同的值

  • 对所有行执行计算,缺省ALL参数。
  • 只包含不同的值,指定DISTINCT参数。(上面5个只有用在AVG和SUM上有意义)

DISTINCT不能用于COUNT(*)

组合聚合函数

上述SQL

10 分组数据

数据分组

分组后汇总

SELECT vend_id, COUNT(*) AS num_prods 
FROM Products
GROUP BY vend_id;
  • GROUP BY可以嵌套
  • 除聚集计算外,SELECT中的每一列都必须在GROUP BY子句中给出。
  • NULL值会分成一个分组
  • GROUP BY子句必须出现在WHERE子句后面,ORDER BY子句之前。

过滤分组

WHERE过滤的是行,而不是分组

HAVING支持所有WHERE操作符。他们语法相通,只是关键词有差别。

SELECT vend_id, COUNT(*) AS num_prods 
FROM Products
GROUP BY vend_id
HAVING COUNT(*) >= 3;

WHERE 在数据分组前进行过滤,HAVING在数据分组后进行过滤。

在MySQL中,如果不指定GROUP BY,会等同对待HAVING 与 WHERE,不过不建议这么用。

SELECT *
FROM Products
HAVING vend_id = 'DLL01';

分组和排序

分组后默认会有个顺序,并不可靠,排序还是使用ORDER BY

SELECT子句顺序

SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY

11 使用子查询

子查询

SELECT cust_id 
FROM Orders
WHERE order_num IN (20007, 20008);

SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';
-- 合并后
-- 这里注意对齐来提高可读性
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');

子查询从内向外处理。

作为子查询只能查询单个列。多个列将会返回错误。

对于嵌套子查询的数目没有限制,不过由于性能限制,不能嵌套太多。

作为计算字段使用子查询

SELECT cust_name, 
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;

该子查询对外层每一条数据进行检索。

上面子查询条件 Orders.cust_id = Customers.cust_id 中的表名是必须的,否则含义将改变,若去掉两个表名,则结果永远为true。

即需要完全限定名(表名.列名)限制有歧义的列。

12 联结表

联结(join)表:用一条SELECT从多个表中查询数据。(俗称:链表查询)

可伸缩(scale):能够适应不断增加的工作量而不失败。

将数据分解为多个表能更有效地存储,更方便地处理,并且可伸缩性更好。但这些好处是有代价的。

-- 一种写法
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;

笛卡尔积:联结时如果没有连接条件则返回第一个表中的行数乘以第二个表中的行数条数据。

SELECT vend_name, prod_name, prod_price 
FROM Vendors, Products;

where子句作为联结条件称为等值联结,这种联结也称为内联结(inner join)。

-- 另一种写法
SELECT vend_name, prod_name, prod_price
FROM Vendors
INNER JOIN Products ON Vendors.vend_id = Products.vend_id;

ANSI SQL规范首选INNER JOIN语法,其实两个都可以,SQL语言纯正论者是用鄙视的眼光看待简单语法的。

SQL中不限制联结表的数目。

性能考虑:DBMS在运行时关联指定的每个表,以处理联结。这种处理可能非常耗费资源,因此应该注意不要联结不必要的表。

联结表的最大数目:虽然SQL没有限制数目。但实际上DBMS都有限制。

-- 子查询不是唯一的办法,也可采用联结查询
SELECT cust_name, cust_contact
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num
AND prod_id = 'RGAN01';

同一种查询结果的SQL可能不止一种,多做实验。

13 创建高级联结

使用表别名理由:

  • 缩短SQL语句
  • 允许在一条SELECT语句中多次是引用相同的表
SELECT cust_name, cust_contact 
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';

AS可以省略

Oracle中没有AS关键词

表别名只在查询中使用,列别名会返回客户端。

上面介绍的是内联结或等值联结的简单联结,还有其他三种联结:自联结、自然联结和外联结。

自联结

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';
-- WHERE子句首先联结两个表,然后按第二个表中的cust_contact过滤数据

用自联结而不用子查询

自联结通常作为外部语句,用来代替相同表中检索数据的使用子查询语句。虽然结果相同,但许多DBMS处理联结远比处理子查询快得多

自然联结

内联结返回所有数据,相同的列甚至出现多次。自然联结排除多次出现,使每一列只返回一次。一般通过对一个表使用通配符(SELECT *),而对其他表中的列使用明确的子集来完成。

SELECT C.*, O.order_num, O.order_date,
OI.prod_id, OI.quantity, OI.item_price
FROM Customers AS C, Orders AS O,
OrderItems AS OI
WHERE C.cust_id = O.cust_id
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';

这个例子中,通配符只对第一个表使用。所有其他列明确列出,所以没有重复的列被检索出来。事实上我们迄今为止建立的每个内联结都是自然联结,很可能永远都不会用到不是自然联结的内联结。

外联结

SELECT Customers.cust_id, Orders.order_num 
FROM Customers
LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;

与内联结关联的两个表中的行不同的是,外联结还包括没有关联的行(为NULL的那些行)。

外联结一定要区分LEFT和RIGHT。

对于SQLite这样只支持LEFT不支持RIGHT的,只需要调整FROM或WHERE子句中表的顺序,即可实现左右互换。

注意:MySQL、MariaDB和SQLite不支持FULL OUT JOIN语法

SELECT Customers.cust_id, Orders.order_num 
FROM Customers
FULL OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;

使用带聚集函数的联结

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 ;

INNER JOIN将Customer和Orders表互相关联。

GROUP BY子句按顾客分组数据。

  • 一般我们使用内联结,但使用外联结也有效。
  • 没有联结条件(不管哪种联结语法)都会得出笛卡尔积。
  • 一个联结中可以包含多个表,甚至可以对每个联结使用不同的联结类型。

14 组合查询

如何使用UNION操作符将多条SELECT语句组合成一个结果集。称为并或复合查询。

  • 在一个查询中从不同的表返回结构数据
  • 对一个表执行多个查询,按一个查询返回数据。

组合查询和多个WHERE条件

多数情况下,组合相同表的两个查询所完成的工作与具有多个WHERE子句条件的一个查询所完成的工作相同。换句话说,任何具有多个WHERE子句的SELECT语句都可以作为一个组合查询。

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 = 'Fun4All';

UNION组合SELECT语句的数目,SQL没有标准限制。最好参考DBMS文档。

WHERE和UNION性能问题理论上没有差别,实际上要视情况而定。

  • UNION中的每个查询必须包含相同的列、表达式或聚集函数(出现次序可以不一样)
  • 列类型不必完全相同,但要兼容。

UNION别名,如果第一个SELECT语句和第二个语句的字段的别名不一样,则采取第一个语句的名字。因此想要排序的话要用第一个语句的字段名

UNION从查询结果中自动去除了重复的行。这是默认选项,如果要展示所有可以使用UNION ALL。UNION ALL的结果通过WHERE语句并不能实现。

SELECT cust_name, cust_contact, cust_email 
FROM Customers
WHERE cust_state IN ('IL', 'IN', 'MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';
ORDER BY cust_name, cust_contact;

对组合查询结果排序,使用ORDER BY,放在最后一个SELECT语句后面,对所欲结果进行排序,不存在部分语句排序方式不同的情况。

15 插入数据

  • 插入完整的行
  • 插入行的一部分
  • 插入某些查询的结果
-- 写法简单,容易出问题
INSERT INTO Customers
VALUES('1000000005', 'The Toy Store', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'Kim Howard', NULL);
-- 即使表结构改变,也能正常运行
INSERT INTO Customers
(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES('1000000005', 'The Toy Store', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'Kim Howard', NULL);
-- 部分列(省略列-该列允许NULL值或该列有默认值)
INSERT INTO Customers
(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip)
VALUES('1000000005', 'The Toy Store', '4545 53rd Street', 'Chicago', 'IL', '54545');

INTO关键字,在某些SQL中INSERT后面的INTO关键字是可选的。但是即使不需要最好也带着,提高可移植性。

总是使用列的列表。

插入检索出的数据

INSERT INTO Customers(cust_id, 
cust_name,
cust_address,
cust_city)
SELECT cust_id,
cust_name,
cust_address,
cust_city
FROM CustNew;

INSERT SELECT中的别名:事实上,DBMS一点儿也不关心SELECT返回的列名,只是将第一列填充第一列,第二列填充第二列,如此等等。

INSERT SELECT中的SELECT语句可以包含WHERE子句。

从一个表复制到另一个新表

有一种数据插入不使用INSERT语句,将一个表的内容复制到新表使用 CREATE SELECT 语句。

CREATE TABLE CustCopy AS SELECT * FROM Customers;
  • 任何SELECT选项和子句都可以使用,包括WHERE和GROUP BY;
  • 可利用联结从多个表插入数据;
  • 不管从多少个表里查询,都只能插入一张表中。

进行表复制可用于试验SQL,而不会影响实际的数据。

16 更新和删除数据

更新数据

  • 更新特定行
  • 更新所有行(不写WHERE条件)
UPDATE Customers 
SET cust_email = 'kim@thetoystore.com',
cust_contact = NULL
WHERE cust_id = 1000000005;

删除数据

  • 删除指定行
  • 删除所有行(不写WHERE条件)
DELETE FROM Customers 
WHERE cust_id = 1000000005;

友好的外键:使用外键确保引用完整性的一个好处是,DBMS通常可以防止删除某个关系需要用到的行。例如要删除产品表里的产品,如果该产品已经在订单表中,那么将DELETE语句将抛出错误并中止。

在某些SQL实现中FROM是可选的。即使不需要,最好也提供这个关键字以保证可移植性。

更快的删除:如果想从表中删除所有行,不使用DELETE。可使用TRUNCATE TABLE语句例如:TRUNCATE CustCopy

指导原则

删除和更新之前最好先查询看一下。

使用强制实施引用完整性的数据库,这样DBMS将不允许删除其数据与其他表相关联的行。

有的DBMS允许添加约束,防止不带WHERE子句的UPDATE或DELETE语句。

17 创建和操纵表

创建表

CREATE TABLE Products 
(
prod_id CHAR(10) NOT NULL,
vend_id CHAR(10) NOT NULL,
vend_zip CHAR(10) ,
prod_price DECIMAL(8, 2) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
prod_desc VARCHAR(1000) NULL
)

语句格式化:虽然没有规定,但强烈推荐上面的缩进格式。

使用NULL值:NULL值就是没有值或缺值。不允许NULL值的列不接受没有列值的行。NULL为默认值。

指定NULL:为了可移植性,推荐指定NULL。主键不能为NULL

指定默认值:DEFAULT 默认值,通常用于日期或时间戳列。

  • MySQL:DEFAULT CURRENT_DATE()
  • Oracle:DEFAULT SYSDATE

许多数据库开发人员喜欢用默认值文不是NULL列,对于用于计算或数据分组的列更是如此。

更新表

ALTER TABLE Vendors 
ADD vend_phone CHAR(20);

ALTER TABLE Vendors
DROP COLUMN vend_phone;

删除表

DROP TABLE CustCopy;

删除没有确认步骤,直接永久性删除。

许多DBMS允许强制实施有关规则,防止删除与其他表相关联的表。

重命名表

这个没有严格的标准。

MySQL和Oracle都是使用RENAME语句。

RENAME TABLE CustCopy TO CustCopy2;

18 使用视图

为什么使用视图?

  • 重用SQL语句。
  • 简化复杂的SQL操作。
  • 使用表的一部分而不是整个表。
  • 保护数据。
  • 更改数据格式和表示。与底层表的表示和格式不同的数据

规则和限制:

  • 视图可以嵌套。
  • 视图不能索引,也不能有关联的触发器或默认值。
-- 简化复杂的联结
CREATE VIEW ProductCustomers AS
SELECT cust_name, cust_contact, prod_id
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num;

SELECT cust_name, cust_contact, prod_id
FROM ProductCustomers
WHERE prod_id = 'RGAN01';

-- 格式化检索出的数据
CREATE VIEW VendorLocations AS
SELECT CONCAT(vend_name, '(', vend_country, ')') AS vend_title
FROM Vendors;

-- 过滤不想要的数据 WHERE
-- 使用视图与计算字段

DROP VIEW ProductCustomers;

19 使用存储过程

执行多条SQL语句。

-- 执行
EXECUTE AddNewProduct('JTS01', 'Stuffed Eiffel Tower', 6.49, 'blue');
-- 创建
-- ...

20 管理事务处理

COMMIT和ROLLBACK。

21 使用游标

DECLARE

22 高级SQL特性

约束

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)
);
ALTER TABLE Orders
ADD CONSTRAINT
FOREIGN KEY (cust_id) REFERENCES Customers(cust_id);

CREATE TABLE OrderItems
(
order_num INTEGER NOT NULL UNIQUE, -- 唯一约束
quantity INTEGER NOT NULL CHECK (quantity > 0) -- 检查约束
)

索引

CREATE INDEX prod_name_ind 
ON Products(prod_name);

触发器

触发器是特殊的存储过程,在特定的数据库活动发生时自动执行。可以与特定表上的INSERT、UPDATE和DELETE操作相关联。

触发器与单个表相关联。

-- SQLServer
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;

数据库安全

GRANT和REVOKE。