SQL(Structured Query Language)是一种专门用来与数据库沟通的语言。算法
数据库(database):保存有组织的数据的容器(一般是一个文件或一组文件)。sql
表(table):某种特定类型数据的结构化清单。数据库
模式(schema):关于数据库和表的布局及特性的信息。服务器
列(column):关于数据库和表的布局及特性的信息。网络
行(row):表中的一个记录。数据库设计
主键(primary key):一列(或一组列),其值可以惟一标识表中每一行。函数
为了使用 SELECT 检索表数据,必须至少给出两条信息——想选择什么,以及从什么地方选择。布局
结束 SQL 语句:多条 SQL 语句必须以分号(;)分隔。多数 DBMS 不须要在单条 SQL 语句后加分号,但也有 DBMS 可能必须在单条 SQL 语句后加上分号。固然,若是愿意能够老是加上分号。事实上,即便不必定须要,加上分号也确定没有坏处。性能
SQL 语句和大小写:请注意,SQL 语句不区分大小写,所以 SELECT 与 select 是相同的。一样,写成 Select 也没有关系。许多 SQL 开发人员喜欢对 SQL 关键字使用大写,而对列名和表名使用小写,这样作使代码更易于阅读和调试。不过,必定要认识到虽然 SQL 是不区分大小写的,可是表名、列名和值可能有所不一样(这有赖于具体的 DBMS 及其如何配置)。测试
使用空格:在处理 SQL 语句时,其中全部空格都被忽略。
SELECT prod_name
FROM Products;
复制代码
SELECT prod_id, prode_name, prod_price
FROM Products;
复制代码
小心逗号:在选择多个列时,必定要在列名之间加上逗号,但最后一个列名后不加。若是在最后一个列名后加了逗号,将出现错误。
SELECT *
FROM Products;
复制代码
使用通配符:通常而言,除非你确实须要表中的每一列,不然最好别使用*通配符。虽然使用通配符能让你本身省事,不用明确列出所需列,但检索不须要的列一般会下降检索和应用程序的性能。
SELECT DISTINCT vend_id
FROM Products;
复制代码
# 检索前 5 行
SELECT prod_name
FROM Products
LIMIT 5;
# 从第 3 行开始检索 5 行
SELECT prod_name
FROM Products
LIMIT 5 OFFSET 3;
# or
SELECT prod_name
FROM Products
LIMIT 3, 5;
复制代码
第 0 行:第一个被检索的行是第 0 行,而不是第 1 行。所以,LIMIT 1 OFFSET 1 会检索第 2 行,而不是第 1 行。
# 单行注释
SELECT prod_name FROM Products; -- 行内注释
/* 块级注释 */
SELECT prod_name
FROM Products
ORDER BY prode_name;
复制代码
ORDER BY 子句的位置:在指定一条 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;
复制代码
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC;
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC, prod_name;
复制代码
请注意,DESC 是 DESCENDING 的缩写,这两个关键字均可以使用。与 DESC 相对的是 ASC(或ASCENDING),在升序排序时能够指定它。但实际上,ASC 没有多大用处,由于升序是默认的(若是既不指定 ASC 也不指定 DESC,则假定为 ASC)。
在 SELECT 语句中,数据根据 WHERE 子句中指定的搜索条件进行过滤。WHERE 子句在表名(FROM 子句)以后给出。
SELECT prod_name, prod_price
FROM Products
WHERE prod_price = 3.49;
SELECT prod_name, prod_price
FROM Products
WHERE prod_price < 10;
复制代码
SQL 过滤与应用过滤:数据也能够在应用层过滤。为此,SQL 的 SELECT 语句为客户端应用检索出超过实际所需的数据,而后客户端代码对返回数据进行循环,提取出须要的行。 一般,这种作法极其不妥。优化数据库后能够更快速有效地对数据进行过滤。而让客户端应用(或开发语言)处理数据库的工做将会极大地影响应用的性能,而且使所建立的应用彻底不具有可伸缩性。此外,若是在客户端过滤数据,服务器不得不经过网络发送多余的数据,这将致使网络带宽的浪费。
WHERE 子句的位置:在同时使用 ORDER BY 和 WHERE 子句时,应该让 ORDER BY 位于 WHERE 以后,不然将会产生错误。
=
:等于>
:大于>=
:大于等于<>
:不等于!=
:不等于!>
:不大于!<
:不小于<
:小于<=
:小于等于BETWEEN
:在指定的两个值之间IS NULL
:为 NULL 值操做符兼容:某些操做符是冗余的(如 <> 与 != 相同,!< 至关于 >=)。并不是全部 DBMS 都支持这些操做符。
SELECT prod_name, prod_price
FROM Products
WHERE vend_id != 'DLL01’; 复制代码
什么时候使用引号:若是仔细观察上述 WHERE 子句中的条件,会看到有的值括在单引号内,而有的值未括起来。单引号用来限定字符串。若是将值与字符串类型的列进行比较,就须要限定引号。用来与数值列进行比较的值不用引号。
SELECT prod_name, prod_price
FROM Products
WHERE prod_price BETWEEN 5 AND 10;
复制代码
SELECT prod_name, prod_price
FROM Products
WHERE prod_price IS NULL;
复制代码
为了进行更强的过滤控制,SQL 容许给出多个 WHERE 子句。这些子句有两种使用方式,即以 AND 子句或 OR 子句的方式使用。
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'D 复制代码
AND: 用在 WHERE 子句中的关键字,用来指示检索知足全部给定条件的行。
OR:WHERE 子句中使用的关键字,用来表示检索匹配任一给定条件的行。
# 优先匹配 AND 左右两侧的条件
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01' AND prod_price >= 10;
# 优先匹配括号内的条件
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01') AND prod_price >= 10;
复制代码
SQL 在处理 OR 操做符以前,优先处理 AND 操做符。圆括号具备更高的优先级。
IN 操做符用来指定条件范围,范围中的每一个条件均可以进行匹配。
SELECT prod_name, prod_price
FROM Products
WHERE vend_id IN ( 'DLL01', 'BRS01' )
ORDER BY prod_name;
复制代码
IN:WHERE 子句中用来指定要匹配值的清单的关键字,功能与 OR 至关。
WHERE 子句中 NOT 操做符有且只有一个功能,那就是否认其后所跟的任何条件。
SELECT prod_name
FROM Products
WHERE NOT vend_id = 'DLL01'
ORDER BY prod_name;
复制代码
NOT:WHERE 子句中用来否认其后条件的关键字。
为在搜索子句中使用通配符,必须使用 LIKE 操做符。LIKE 指示 DBMS,后跟的搜索模式利用通配符匹配而不是简单的相等匹配进行比较。
通配符(wildcard):用来匹配值的一部分的特殊字符。
搜索模式(search pattern):由字面值、通配符或二者组合构成的搜索条件。
在搜索串中,% 表示任何字符出现任意次数(包括 0 个字符)。
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '%bean bag%';
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'F%y';
复制代码
下划线的用途与 % 同样,但它只匹配单个字符,而不是多个字符。
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear';
复制代码
方括号([])通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符。
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%' ORDER BY cust_contact;
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[^JM]%' ORDER BY cust_contact;
SELECT cust_contact
FROM Customers
WHERE NOT cust_contact LIKE '[JM]%' ORDER BY cust_contact;
复制代码
SQL 的通配符颇有用。但这种功能是有代价的,即通配符搜索通常比前面讨论的其余搜索要耗费更长的处理时间。
存储在数据库表中的数据通常不是应用程序所须要的格式,下面举几个例子。
咱们须要直接从数据库中检索出转换、计算或格式化过的数据,而不是检索出数据,而后再在客户端应用程序中从新格式化。
这就是计算字段能够派上用场的地方了。与前几课介绍的列不一样,计算字段并不实际存在于数据库表中。计算字段是运行时在 SELECT 语句内建立的。
SELECT vend_name + ' (' + vend_country + ')'
FROM Vendors
ORDER BY vend_name;
# or
SELECT vend_name || ' (' || vend_country || ')'
FROM Vendors
ORDER BY vend_name;
复制代码
许多数据库(不是全部)保存填充为列宽的文本值,而实际上你要的结果不须要这些空格。为正确返回格式化的数据,必须去掉这些空格。这可使用 SQL 的 RTRIM() 函数来完成。
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
FROM Vendors
ORDER BY vend_name;
复制代码
RTRIM()
:去掉字符串右边的空格。LTRIM()
:去掉字符串左边的空格。TRIM()
:去掉字符串两边的空格。SELECT vend_name + ' (' + vend_country + ')' AS vend_title
FROM Vendors
ORDER BY vend_name;
复制代码
SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;
复制代码
SQL 支持的算术操做符:
+
:加-
:减*
:乘/
:除事实上,只有少数几个函数被全部主要的 DBMS 等同地支持。虽然全部类型的函数通常均可以在每一个 DBMS 中使用,但各个函数的名称和语法可能极其不一样。
大多数 SQL 实现支持如下类型的函数:
SELECT vend_name, UPPER(vend_name) AS upper_vend_name
FROM Vendors
ORDER BY vend_name;
SELECT vend_name LENGTH(vend_name) AS vend_name_length
FROM Vendors
ORDER BY vend_name;
复制代码
经常使用的文本处理函数:
LEFT()
:返回字符串左边的字符LENGTH()
:返回字符串的长度LOWER()
:将字符串转换为小写LTRIM()
:去掉字符串左边的空格RIGHT()
:返回字符串右边的字符RTRIM()
:去掉字符串右边的空格SOUNDEX()
:返回字符串的 SOUNDEX 值UPPER
:将字符串转换为大写SOUNDEX 是一个将任何文 本串转换为描述其语音表示的字母数字模式的算法。SOUNDEX 考虑了相似的发音字符和音节,使得能对字符串进行发音比较而不是字母比较。
日期和时间采用相应的数据类型存储在表中,每种 DBMS 都有本身的特殊形式。日期和时间值以特殊的格式存储,以便能快速和有效地排序或过滤,而且节省物理存储空间。
应用程序通常不使用日期和时间的存储格式,所以日期和时间函数老是用来读取、统计和处理这些值。因为这个缘由,日期和时间函数在 SQL 中具备重要的做用。遗憾的是,它们很不一致,可移植性最差。
SELECT order_num
FROM Orders
WHERE strftime('%Y', order_date) = '2012';
复制代码
经常使用数值处理函数:
ABS()
:返回一个数的绝对值COS()
:返回一个角度的余弦EXP()
:返回一个数的指数值PI()
:返回圆周率SIN()
:返回一个角度的正弦SQRT()
:返回一个数的平方根TAN()
:返回一个角度的正切汇集函数(aggregate function):对某些行运行的函数,计算并返回一个值。
SQL 汇集函数:
AVG()
:返回某列的平均值COUNT()
:返回某列的行数MAX()
:返回某列的最大值MIN()
:返回某列的最小值SUM()
:返回某列值之和AVG() 经过对表中行数计数并计算其列值之和,求得该列的平均值。AVG() 可用来返回全部列的平均值,也能够用来返回特定列或行的平均值。
SELECT AVG(prod_price) AS avg_price
FROM Products;
SELECT AVG(prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';
复制代码
COUNT() 函数进行计数。可利用 COUNT() 肯定表中行的数目或符合特定条件的行的数目。
COUNT() 函数有两种使用方式:
SELECT COUNT(*) AS num_cust
FROM Products;
SELECT COUNT(cust_email) AS num_cust
FROM Customers;
复制代码
MAX() 返回指定列中的最大值。
SELECT MAX(prod_price) AS max_price
FROM Products;
复制代码
对非数值数据使用 MAX():虽然 MAX() 通常用来找出最大的数值或日期值,但许多(并不是全部)DBMS 容许将它用来返回任意列中的最大值,包括返回文本列中的最大值。在用于文本数据时,MAX() 返回按该列排序后的最后一行。
MIN() 的功能正好与 MAX() 功能相反,它返回指定列的最小值。
SELECT MIN(prod_price) AS min_price
FROM Products;
复制代码
对非数值数据使用 MIN():虽然 MIN() 通常用来找出最小的数值或日期值,但许多(并不是全部)DBMS 容许将它用来返回任意列中的最小值,包括返回文本列中的最小值。在用于文本数据时,MIN() 返回该列排序后最前面的行。
SUM() 用来返回指定列值的和(总计)。
SELECT SUM(quantity) AS items_ordered
FROM OrderItems
WHERE order_num = 20005;
SELECT SUM(item_price * quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;
复制代码
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01; 复制代码
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;
复制代码
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
复制代码
在使用 GROUP BY 子句前,须要知道一些重要的规定。
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id HAVING COUNT(*) >= 2;
SELECT vend_Id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id HAVING COUNT(*) >= 2;
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num HAVING COUNT(*) >= 3
ORDER BY items, order_num;
复制代码
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算汇集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
SELECT cust_id
FROM Orders
WHERE order_num IN (
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');
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 = 'RGAN01'));
复制代码
只能是单列:做为子查询的 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;
复制代码
相同的数据出现屡次决不是一件好事,这是关系数据库设计的基础。关系表的设计就是要把信息分解成多个表,一类数据一个表。各表经过某些共同的值互相关联(因此才叫关系数据库)。
关系数据能够有效地存储,方便地处理。所以,关系数据库的可伸缩性远比非关系数据库要好。
将数据分解为多个表能更有效地存储,更方便地处理,而且可伸缩性更好。
简单说,联结是一种机制,用来在一条 SELECT 语句中关联表,所以称为联结。使用特殊的语法,能够联结多个表返回一组输出,联结在运行时关联表中正确的行。
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
复制代码
彻底限定列名:就像前一课提到的,在引用的列可能出现歧义时,必须使用彻底限定列名(用一个句点分隔表名和列名)。若是引用一个没有用表名限制的具备歧义的列名,大多数 DBMS 会返回错误。
WHERE 子句做为过滤条件,只包含那些匹配给定条件(这里是联结条件)的行。没有 WHERE 子句,第一个表中的每一行将与第二个表中的每一行配对,而无论它们逻辑上是否能配在一块儿。
笛卡尔积:由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
# 错误示范
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products;
复制代码
目前为止使用的联结称为等值联结(equijoin),它基于两个表之间的相等测试。这种联结也称为内联结(inner join)。
SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
复制代码
在使用这种语法时,联结条件用特定的 ON 子句而不是 WHERE 子句给出。传递给 ON 的实际条件与传递给 WHERE 的相同。
SQL 不限制一条 SELECT 语句中能够联结的表的数目。建立联结的基本规则也相同。首先列出全部表,而后定义表之间的关系。
SELECT prod_name, vend_name, prod_price, quantity
FROM OrderItems, Products, Vendors
WHERE Products.vend_id = Vendors.vend_id
AND OrderItems.prod_id = Products.prod_id
AND order_num = 20007;
复制代码
性能考虑:DBMS 在运行时关联指定的每一个表,以处理联结。这种处理可能很是耗费资源,所以应该注意,不要联结没必要要的表。联结的表越多,性能降低越厉害。
多作实验:执行任一给定的 SQL 操做通常不止一种方法。不多有绝对正确或绝对错误的方法。性能可能会受操做类型、所使用的 DBMS、表中数据量、是否存在索引或键等条件的影响。所以,有必要试验不一样的选择机制,找出最适合具体状况的方法。
给列起别名的语法以下:
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')' AS vend_title
FROM Vendors
ORDER BY vend_name;
复制代码
给表起别名的语法:
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';
复制代码
须要注意,表别名只在查询执行中使用。与列别名不同,表别名不返回到客户端。
迄今为止,咱们使用的只是内联结或等值联结的简单联结。如今来看三种其余联结:自联结(self-join)、天然联结(natural join)和外联结(outer join)。
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';
复制代码
此查询中须要的两个表其实是相同的表,所以 Customers 表在 FROM 子句中出现了两次。虽然这是彻底合法的,但对 Customers 的引用具备歧义性,由于 DBMS 不知道你引用的是哪一个 Customers 表。解决此问题,须要使用表别名。
用自联结而不用子查询:自联结一般做为外部语句,用来替代从相同表中检索数据的使用子查询语句。虽然最终的结果是相同的,但许多 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;
复制代码
与内联结关联两个表中的行不一样的是,外联结还包括没有关联行的行。在使用 OUTER JOIN 语法时,必须使用 RIGHT 或 LEFT 关键字指定包括其全部行的表(RIGHT指出的是 OUTER JOIN 右边的表,而LEFT指出的是 OUTER JOIN左边的表)。上面的例子使用 LEFT OUTER JOIN 从 FROM 子句左边的表(Customers 表)中选择全部行。为了从右边的表中选择全部行,须要使用 RIGHT OUTER JOIN。
SELECT Customers.cust_id, Orders.order_num
FROM Customers RIGHT OUTER JOIN Orders
ON Orders.cust_id = Customers.cust_Id;
复制代码
外联结的类型:有两种基本的外联结形式:左外联结和右外联结。它们之间的惟一差异是所关联的表的顺序。换句话说,调整 FROM 或 WHERE 子句中表的顺序,左外联结能够转换为右外联结。所以,这两种外联结能够互换使用,哪一个方便就用哪一个。
还存在另外一种外联结,就是全外联结(full outer join),它检索两个表中的全部行并关联那些能够关联的行。与左外联结或右外联结包含一个表的不关联的行不一样,全外联结包含两个表的不关联的行。
SELECT Customers.cust_id, Orders.order_num
FROM Orders FULL OUTER JOIN Customers
ON Orders.cust_id = Customers.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;
复制代码
汇集函数也能够方便地与其余联结一块儿使用。
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;
复制代码