一、什么是 SQL ?

SQL 是 Structured Query Language(结构化查询语言)的缩写,是一种专门用来与数据库沟通的语言。与其他语言(如英语或 C、C++、Java 这样的编程语言)不一样,SQL 中只有很少的词,这是有意而为的。设计 SQL 的目的是很好地完成一项任务 一 提供一种从数据库中读写数据的简单有效的方法。

SQL 有如下的优点:

  • SQL 不是某个特定数据库供应商专有的语言。几乎所有重要的 DBMS 都支持 SQL ,所以学习此语言使你几乎能与所有数据库打交道;
  • SQL 简单易学。它的语句全都是由有很强描述性的英语单词组成,而且这些单词的数目不多;
  • SQL 虽然看上去很简单,但实际上是一种强有力的语言,灵活使用其语言元素,可以进行非常复杂和高级的数据库操作。


二、创建、更新和删除表

下面先讲讲授创建、更改和删除表的 SQL 语法。


2.1 创建表

用程序创建表,可以使用 SQL 的 CREATE TABLE 语句。下面的 SQL 语句创建本文中所用的 Products 表:

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  DEFAULT 3,         
proddesc    VARCHAR(1000)   NULL
);

从上面的例子可以看到,表名紧跟 CREATE TABLE 关键字。实际的表定义(所有列)括在圆括号之中,各列之间用逗号分隔。有一列的描述增加了 DEFAULT 3,指示 DBMS,如果不给出价格则使用价格 3。


2.2 更新表

更新表定义,可以使用 ALTER TABLE 语句。因为给已有表增加列可能是所有 DBMS 都支持的唯一操作,所以我们举个这样的例子:

ALTER TABLE Vendors ADD vend_phone CHAR(20);

这条语句给 Vendors 表增加一个名为 vend_phone 的列,其数据类型为 CHAR。删除列,就使用DROP COLUMN col_name即可。


2.3 删除表

删除表(删除整个表而不是其内容)非常简单,如下所示:

DROP TABLE CustCopy;

这条语句删除 CustCopy 表。删除表没有确认,也不能撤销,执行这条语句将永久删除该表。

使用关系规则防止意外删除


三、插入数据

下面介绍如何利用 SQL 的 INSERT 语句将数据插入表中。


3.1 数据插入

把数据插入表中的最简单方法是使用基本的 INSERT 语法,它要求指定表名和插入到新行中的值。下面举一个例子:

INSERT INTO Customers VALUES('1000000006', 'Toy Land', '123 Any Street', 'New York', 'NY', '11111', 'USA', NULL, NULL);

这个例子将一个新顾客插入到 Customers 表中。存储到表中每一列的数据在 VALUES 子句中给出,必须给每一列提供一个值。如果某列没有值,如上面的 cust_contact 和 cust_email 列,则应该使用 NULL 值(假定表允许对该列指定空值)。各列必须以它们在表定义中出现的次序填充。

虽然这种语法很简单,但并不安全,应该尽量避免使用。上面的 SQL 语句高度依赖于表中列的定义次序,还依赖于其容易获得的次序信息。即使可以得到这种次序信息,也不能保证各列在下一次表结构变动后保持完全相同的次序。因此,编写依赖于特定列次序的 SQL 语句是很不安全的,这样做迟早会出问题。


编写 INSERT 语句的更安全(不过更烦琐)的方法如下:

INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) VALUES('1000000006', 'Toy Land', '123 Any Street', 'New York', 'NY', '11111', 'USA', NULL, NULL);

这个例子与前一个 INSERT 语句的工作完全相同,但在表名后的括号里明确给出了列名。在插入行时,DBMS 将用 VALUES 列表中的相应值填入列表中的对应项。因为提供了列名,VALUES 必须以其指定的次序匹配指定的列名,不一定按各列出现在表中的实际次序。其优点是,即使表的结构改变,这条 INSERT 语句仍然能正确工作。


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

要将一个表的内容复制到一个全新的表(运行中创建的表),可以使用 SELECT INTO 语句。MariaDB、MySQL、Oracle、PostgreSQL 和 SQLite 使用的语法如下:

CREATE TABLE CustCopy AS SELECT * FROM Customers;

这条 SELECT 语句创建一个名为 CustCopy 的新表,并把 Customers 表的整个内容复制到新表中。因为这里使用的是 SELECT *,所以将在 CustCopy 表中创建(并填充)与 Customers 表的每一列相同的列。要想只复制部分的列,可以明确给出列名,而不是使用 * 通配符。


四、更新和删除数据

下面介绍如何利用 UPDATE 和 DELETE 语句进一步操作表数据。


4.1 更新数据

更新(修改)表中的数据,可以使用 UPDATE 语句。例子如下:

UPDATE Customers SET cust_contact = 'Sam Roberts', cust_email = 'sam@toyland.cxx' WHERE cust_id = '1000000006';

UPDATE 语句以 WHERE 子句结束,它告诉 DBMS 更新哪一行。没有 WHERE 子句,DBMS 将会用这个电子邮件地址更新 Customers 表中的所有行,这不是我们希望的。


4.2 删除数据

从一个表中删除数据,使用 DELETE 语句。下面的语句从 Customers 表中删除一行:

DELETE FROM Customers WHERE cust_id = '1000000006';

这条语句很容易理解。DELETE FROM 要求指定从中删除数据的表名,WHERE 子句过滤要删除的行。在这个例子中,只删除顾客 1000000006。如果省略 WHERE 子句,它将删除表中每个顾客。


五、检索数据

下面介绍如何使用 SELECT 语句从表中检索一个或多个数据列。


5.1 SELECT 语句

正如上面所述,SQL语句是由简单的英语单词构成的。这些单词称为关键字,每个 SQL 语句都是由一个或多个关键字构成的。最经常使用的 SQL 语句大概就是 SELECT 语句了。它的用途是从一个或多个表中检索信息。

关键字(keyword)

为了使用 SELECT 检索表数据,必须至少给出两条信息一想选择什么,以及从什么地方选择。


5.2 检索单个列

我们将从简单的 SELECT 语句讲起,语句如下所示:

SELECT prod_name FROM Products;

上述语句利用 SELECT 语句从 Products 表中检索一个名为 prod_name 的列。所需的列名写在 SELECT 关键字之后,FROM 关键字指出从哪个表中检索数据。


提示1: SQL 语句和大小写

请注意, SQL 语句不区分大小写,因此 SELECT 与 SELECT 是相同的。同样,写成 SELECT 也没有关系。许多 SQL 开发人员喜欢对 SQL 关键字使用大写,而对列名和表名使用小写,这样做使代码更易于阅读和调试。不过,一定要认识到虽然 SQL 是不区分大小写的,但是表名、列名和值可能有所不同(这有赖于具体的DBMS及其如何配置)。


提示2:使用空格


5.3 检索多个行

要想从一个表中检索多个列,仍然使用相同的 SELECT 语句。唯一的不同是必须在 SELECT 关键字后给出多个列名,列名之间必须以逗号分隔。

SELECT  prod_id, prod_name, prod_price FROM Products;

这条语句使用 SELECT 语句从表 Products 中选择多个数据。在这个例子中,指定了 3 个列名,列名之间用逗号分隔。


5.4 检索所有列

除了指定所需的列外(如上所述,一个或多个列),SELECT 语句还可以检索所有的列而不必逐个列出它们。在实际列名的位置使用星号(*)通配符可以做到这点,如下所示:

SELECT  * FROM Products;

如果给定一个通配符(*),则返回表中所有列。列的顺序一般是列在表定义中出现的物理顺序,但并不总是如此。


5.5 检索不同的值

如前所述,SELECT 语句返回所有匹配的行。但是,如果你不希望每个值每次都出现,该怎么办呢?办法就是使用 DISTINCT 关键字,顾名思义,它指示数据库只返回不同的值。

SELECT  DISTINCT vend_id FROM Products;

SELECT DISTINCT vend_id 告诉 DBMS 只返回不同(具有唯一性)的 vend_id 行。如果使用 DISTINCT 关键字,它必须直接放在列名的前面。

警告:不能部分使用 DISTINCT


5.6 限制结果

SELECT 语句返回指定表中所有匹配的行,很可能是每一行。如果你只想返回第一行或者一定数量的行,该怎么办呢?这是可行的,然而遗憾的是,各种数据库中的 SQL 实现并不相同。

如果你使用 My SQL 、MariaDB、Postgre SQL 或者 SQL ite,需要使用 LIMIT子句,像这样:

SELECT  prod_name FROM Products LIMIT 5;

上述代码使用 SELECT 语句来检索单独的一列数据。LIMIT 5 指示 My SQL 等 DBMS 返回不超过 5 行的数据。


为了得到后面的 5 行数据,需要指定从哪儿开始以及检索的行数,像这样:

SELECT  prod_name FROM Products LIMIT 5 OFFSET 5;

LIMIT 5 OFFSET 5 指示 My SQL 等 DBMS 返回从第 5 行起的 5 行数据。第一个数字是指从哪儿开始,第二个数字是检索的行数。

警告:第 0 行

第一个被检索的行是第 0 行,而不是第 1 行。因此,LIMIT 1 OFFSET 1 会检索第 2 行,而不是第 1 行。


提示:My SQL 和MariaDB快捷键


5.7 使用注释

我们先来看行内注释:

SELECT  prod_name FROM Products;    --这是一条注释

注释使用--(两个连字符)嵌在行内,之后的文本就是注释。


你也可以进行多行注释,注释可以在脚本的任何位置停止和开始。

/*  SELECT  prod_name, vend_id 
FROM Products; */

SELECT  prod_name FROM Products;

注释从/*开始,到*/结束,之间的任何内容都是注释。


六、排序检索数据

下面讲授如何使用 SELECT 语句的 ORDER BY 子句,根据需要排序检索出的数据。


6.1 排序数据

为了明确地排序用 SELECT 语句检索出的数据,可使用 ORDER BY 子句。ORDER BY 子句取一个或多个列的名字,据此对输出进行排序。请看下面的例子:

SELECT prod_name FROM Products ORDER BY prod_name;

ORDER BY 子句,指示 DBMS 软件对 prod_name 列以字母顺序排序数据。

注意:ORDER BY 子句的位置


6.2 按多个列排序

下面的代码检索 3 个列,并按其中两个列对结果进行排序 一 首先按价格,然后按名称排序。

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

对于上述例子中的输出,仅在多个行具有相同的 prod_price 值时才对产品按 prod_name 进行排序。如果 prod_price 列中所有的值都是唯一的,则不会按 prod_name 排序。(先对 prod_price 排序,再对 prod_name 排序)


6.3 按列位置排序

除了能用列名指出排序顺序外,ORDER BY 还支持按相对列位置进行排序。为理解这一内容,我们来看个例子:

SELECT prod_id, prod_price, prod_name FROM Products ORDER BY 2, 3;

SELECT 清单中指定的是选择列的相对位置而不是列名。ORDER BY 2 表示按 SELECT 清单中的第二个列 prod_name 进行排序。ORDER BY 2, 3 表示先按 prod_price,再按 prod_name 进行排序。


6.4 指定排序方向

数据排序不限于升序排序(从 A 到 Z),这只是默认的排序顺序。还可以使用 ORDER BY 子句进行降序(从 Z 到 A )排序。为了进行降序排序,必须 指定 DESC 关键字。例子如下:

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

DESC 关键字只应用到直接位于其前面的列名。在上例中,只对 prod_price 列指定 DESC,对 prod_name 列不指定。因此,prod_price 列以降序排序,而 prod_name 列(在每个价格内)仍然按标准的升序排序。

警告:在多个列上降序排序


七、过滤数据

下面将讲授如何使用 SELECT 语句的 WHERE 子句指定搜索条件。


7.1 使用 WHERE 子句

在 SELECT 语句中,数据根据 WHERE 子句中指定的搜索条件进行过滤。WHERE 子句在表名(FROM 子句)之后给出,如下所示:

SELECT prod_name, prod_price FROM Products WHERE prod_price = 3.49;

这条语句从 products 表中检索两个列,但不返回所有行,只返回 prod_price 值为 3.49 的行。


7.2 WHERE 子句操作符

SQL 支持下表列出的所有条件操作符。

操作符

说明

=

等于

!=

不等于

<

小于

<=

小于等于

>

大于

>=

大于等于

BETWEEN

在指定的两个值之间

IS NULL

为NULL值


1. 范围内检查

要检查某个范围的值,可以使用 BETWEEN 操作符。其语法与其他 WHERE 子句的操作符稍有不同,因为它需要两个值,即范围的开始值和结束值。下面的例子说明如何使用 BETWEEN 操作符,它检索价格在 5 美元和 10 美元之间的所有产品:

SELECT prod_name, prod_price FROM Products WHERE prod_price BETWEEN 5 AND 10;

从这个例子可以看到,在使用 BETWEEN 时,必须指定两个值——所需范围的低端值和高端值。这两个值必须用 AND 关键字分隔。


2. 空值检查

确定值是否为 NULL,不能简单地检查是否 =NULL。SELECT 语句有一个特殊的 WHERE 子句,可用来检查具有 NULL 值的列。这个 WHERE 子句就是 IS NULL 子句。其语法如下:

SELECT prod_name FROM Products WHERE prod_price IS NULL;

这条语句返回所有没有价格(空 prod_price 字段,不是价格为 0)的产品。


八、高级数据过滤

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


8.1 组合 WHERE 子句

第 4 课介绍的所有 WHERE 子句在过滤数据时使用的都是单一的条件。为了进行更强的过滤控制,SQL 允许给出多个 WHERE 子句。这些子句有两种使用方式,即以 AND 子句或 OR 子句的方式使用。


1. AND 操作符

要通过不止一个列进行过滤,可以使用 AND 操作符给 WHERE 子句附加条件。下面的代码给出了一个例子:

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

此 SQL 语句检索由供应商 DLL01 制造且价格小于等于 4 美元的所有产品的名称和价格。


2. OR 操作符

OR 操作符与 AND 操作符正好相反,它指示 DBMS 检索匹配任一条件的行。事实上,许多 DBMS 在 OR WHERE 子句的第一个条件得到满足的情况下, 就不再计算第二个条件了。例子如下:

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

此 SQL 语句检索由任一个指定供应商制造的所有产品的产品名和价格。


8.2 IN操作符

IN 操作符用来指定条件范围,范围中的每个条件都可以进行匹配。IN 取一组由逗号分隔、括在圆括号中的合法值。下面的例子说明了这个操作符:

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

此 SELECT 语句检索由供应商 DLL01 和 BRS01 制造的所有产品。IN 操作符后跟由逗号分隔的合法值,这些值必须括在圆括号中。 你可能会猜测 IN 操作符完成了与 OR 相同的功能,恭喜你猜对了!


8.3 NOT操作符

WHERE 子句中的 NOT 操作符有且只有一个功能,那就是否定其后所跟的任何条件。NOT 关键字可以用在要过滤的列前,而不仅是在其后。下面的例子说明了这个操作符:

SELECT prod_name FROM Products WHERE NOT vend_id = 'DLL01';

这里的 NOT 否定跟在其后的条件,因此,DBMS 不是匹配 vend_id 为 DLL01,而是匹配非 DLL01 之外的所有东西。


九、用通配符进行过滤

下面介绍什么是通配符、如何使用通配符以及怎样使用 LIKE 操作符进行通配搜索,以便对数据进行复杂过滤。


1. 百分号(%)通配符

最常使用的通配符是百分号(%)。在搜索串中,% 表示任何字符出现任意次数。例如,为了找出所有以词 Fish 起头的产品,可发布以下 SELECT 语句:

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

此例子使用了搜索模式 'Fish%’。在执行这条子句时,将检索任意以 Fish 起头的词。% 告诉 DBMS 接受 Fish 之后的任意字符,不管它有多少字符。


2. 下划线(_)通配符

另一个有用的通配符是下划线(_)。下划线的用途与 % —样,但它只匹配单个字符,而不是多个字符。例子如下:

SELECT prod_id,prod_name FROM Products WHERE prod_name LIKE '__ inch teddy bear';

这个 WHERE 子句中的搜索模式给出了后面跟有文本的两个通配符。


3. 方括号([])通配符

方括号([])通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符。例如,找出所有名字以 J 或 M 起头的联系人,可进行如下查询:

SELECT cust_contact FROM Customers WHERE cust_contact LIKE '[JM]%';

这一搜索模式使用了两个不同的通配符。[JM] 匹配任何以方括号中字母开头的联系人名,它也只能匹配单个字符。[JM] 之后的 % 通配符匹配第一个字符之后的任意数目的字符,返回所需结果。