本节书摘来自异步社区出版社《SQL学习指南(第2版)(修订版)》一书中的第1章,第1.2节,作者: 【美】Alan Beaulieu,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.2 什么是SQL
SQL学习指南(第2版)(修订版)
根据Codd对关系模型的定义,他提出一种名为DSL/Alpha的语言,用于操控关系表的数据。在Codd的论文发表后不久,IBM建立了一个研究小组来根据他的想法构建原型。该小组创建了一个DSL/Alpha的简化版本,即SQUARE,然后通过对SQUARE的改进,将之发展为SEQUEL语言,并最终命名为SQL。
今天SQL已经发展到了中年期(唉,就像作者一样),在这期间它经历了大量修改。在20世纪80年代中期,美国国家标准组织(ANSI)开始制定SQL语言的第一个标准,并于1986年发布。其后不断对其改进,并在1989年、1992年、1999年、2003年和2006年发布了一系列SQL标准的新版本。通过对语言核心的改良,新的特性被陆续加入到SQL语言中,以吸收面向对象等其他功能。最后一个标准版本,SQL 2006则聚焦于SQL和XML的集成,并定义了XQuery语言以用于在XML文档中查询数据。
SQL与关系模型的关系密切,因为SQL查询的结果也可以视为一张表(在程序上下文中称之为结果集)。因此,可以在关系数据库中简单地创建一个固定表,用于存放查询的结果集。同样地,SQL查询也可以使用固定表或其他查询的结果集作为其输入(在第9章中将会讲述其细节)。
最后需要注意的一点是:SQL并不是任何短语的缩写(尽管许多人坚持认为它代表结构化查询语言(Structured Query Language))。当提到此语言时,可以使用独立的字母(S.Q.L)或使用单词sequel。
1.2.1 SQL语句的分类
在本书中,将分别讨论SQL语言的几个独立模块,即SQL 方案(schema)语句,用于定义存储于数据库中的数据结构;SQL数据语句,用于操作SQL方案语句所定义的数据结构;以及SQL事务语句,用于开始、结束或回滚事务(将在第12章中介绍)。例如,在数据库中创建新表时,需要使用SQL方案语句create table,而在新表中产生数据则需要SQL数据语句insert。
下面给出这些语句的具体例子,用于创建corporation表的SQL方案语句如下:
CREATE TABLE corporation
(corp_id SMALLINT,
name VARCHAR(30),
CONSTRAINT pk_corporation PRIMARY KEY (corp_id)
);
该语句创建的表包括两列:corp_id和name。其中,corp_id列被设置为表的主键。在第2章中,将会介绍该语句的细节,比如MySQL中所提供的各种数据类型。下面给出的SQL数据语句将向corporation表中插入一行关于Acme Paper Corporation的数据:
INSERT INTO corporation (corp_id, name)
VALUES (27,’Acme Paper Corporation’);
该语句向corporation表中添加了一行数据,其中corp_id列的值为27,而name列的值是Acme Paper Corporation。
最后,给出一条简单的select语句,以获取刚才创建的数据:
mysql> SELECT name
-> FROM corporation
-> WHERE corp_id = 27;
+------------------------+
| name |
+------------------------+
| Acme Paper Corporation |
+------------------------+
通过SQL方案语句所创建的所有数据库元素都被存储在一个特殊的表集合,即数据字典中。这些“关于数据库的数据”一般被称为“元数据”,本书第15章将对此进行详细介绍。与用户所创建的表一样,数据字典表也可以通过select语句查询,从而允许在运行时刻查看数据库中的当前数据结构。例如,用户需要编写显示上月新增账户的报表,那么既可以在报表中对account表的各个列名进行硬编码,也可以通过查询数据字典以获取当前的列集合并在每次运行时动态地创建报表。
本书中的大部分篇幅将聚焦于SQL语言中的数据相关部分,包括select、update、insert和delete命令。SQL方案语句将在第2章中说明,并且该章所创建的示例数据库将在全书中使用。一般来说,不需要对SQL方案语句的语法进行太多论述,而对于SQL数据语句,尽管只有寥寥几条,但其中包含了大量值得仔细研究的内容。因此,尽管我尽量介绍更多的SQL方案语句,但本书的大多数章节还是把重点放在SQL数据语句上。
1.2.2 SQL:非过程化语句
如果读者有过编程语言的使用经验,可能习惯于定义变量或数据结构、使用条件逻辑(即if-then-else)和循环结构(即do-while-end),并将程序代码分成可复用的小片段(如对象、函数、过程等)。这些代码经过编译后执行,其执行结果精确地(也并不是总是精确)符合编程的预期。无论是使用Java、C#、C、Visual Basic还是其他过程化语言,都可以完全控制程序的行为。
提示
过程化语言对所期望的结果和产生这些结果的执行机制或过程都进行了定义。非过程化语言同样定义了期望结果,但将产生结果的过程留给外部代理来定义。
使用SQL意味着必须放弃对过程的控制,因为SQL语句只定义必要的输入和输出,而执行语句的方式则交由数据库引擎的一个组件,即优化器(optimizer)处理。优化器的工作包括查看SQL语句并考虑该表的配置信息以及有无索引等,以确定最具效率的执行路径(当然,并不总是最有效率)。大多数数据库引擎允许通过指定优化器选项来影响优化器的决策,比如建议使用特定的索引等。而大多数SQL的用户并不需要考虑这个复杂的层面,而是将之交给数据库管理员或性能调优专家来处理。
因此单独使用SQL并不能开发完整的应用,除了编写简单的脚本来处理某些数据,一般需要将SQL与编程语言相集成。一些数据库厂商已经为用户考虑了这些,如Oracle的PL/SQL语言,MySQL的存储过程语言,以及Microsoft的Transact-SQL语言。在这些语言中,SQL数据语言是其语法的一部分,以准确无误地将数据库查询与过程化命令集成到一起。如果使用非数据库指定的语言,如Java等,则需要使用工具集/API以在代码中执行SQL语句。有些工具集由数据库厂商提供,其他的则由第三方厂商或开源代码提供者所创建。表1-2显示了将SQL集成到特定语言的可用选项。
如果用户仅仅需要执行交互式的命令,那么每种数据库开发商都提供了至少一个简单的命令行工具,用于向数据库引擎提交SQL命令。大多数开发商都提供了图形化的工具,其中包含显示SQL命令的窗口以及另一个显示SQL命令执行结果的窗口。因为本书中的例子都将在MySQL数据库中运行,所以本书使用mysql命令行工具。该工具属于MySQL安装文件的一部分,并用于运行示例和格式化的结果。
1.2.3 SQL示例
在本章前面,我说过要演示返回George Blake的checking账户上所有交易的SQL语句,下面就兑现这个承诺,语句和查询结果如下:
SELECT t.txn_id, t.txn_type_cd, t.txn_date, t.amount
FROM individual i
INNER JOIN account a ON i.cust_id = a.cust_id
INNER JOIN product p ON p.product_cd = a.product_cd
INNER JOIN transaction t ON t.account_id = a.account_id
WHERE i.fname = 'George' AND i.lname = 'Blake'
AND p.name = 'checking account';
+--------+-------------+---------------------+--------+
| txn_id | txn_type_cd | txn_date | amount |
+--------+-------------+---------------------+--------+
| 11 | DBT | 2008-01-05 00:00:00 | 100.00 |
+--------+-------------+---------------------+--------+
1 row in set (0.00 sec)
此处仅对此语句进行简单的分析:该查询查找满足下面两个条件的行,即在individual表中姓名为George Blake的行,以及在product表中的账户名为checking account的行,并通过account表将它们关联起来,然后返回transaction表中所有提交到该账户上的交易信息内容,并分4列显示。如果刚好知道George Blake的客户ID是8并且checking账户的指定代码为“CHK”,就可以简单地根据客户ID找到George Blake在account表中的checking账户,并使用账户ID来查找相关的交易:
SELECT t.txn_id, t.txn_type_cd, t.txn_date, t.amount
FROM account a
INNER JOIN transaction t ON t.account_id = a.account_id
WHERE a.cust_id = 8 AND a.product_cd = 'CHK';
在下面的各章节里,将会覆盖到这些查询的所有概念(并且会涉及得很多),但在这里至少需要展示一下它们的大致结构。
前面的查询包含了3个不同的字句,包括select、from和where。几乎所有的查询都至少会包含这3个子句,当然还有其他几个子句可用于更特定的查询目标。下面展示了这3个子句所起的角色:
SELECT /* 1个或多个事物*/ ...
FROM /* 1个或多个地点*/ ...
WHERE /* 1个或多个条件*/ ...
提示
大多数的SQL实现都将/和/标记之间的文本视为注释。
当用户构造查询时,首先需要确定查找的是哪一个或哪些表,并将它们加入from子句中,然后在where子句中增加查询条件以过滤掉并不感兴趣的数据。最后,需要确定从各个表中所应提取的列,并将之增加到select子句中。下面给出一个简单的例子,以展示如何找到所有姓为“Smith”的客户:
SELECT cust_id, fname
FROM individual
WHERE lname = 'Smith';
该查询搜索individual表,以找到所有lname列匹配字符串'Smith'的行,并返回这些行中的cust_id和fname列。
除了查询数据库,还需要在数据库中建立和修改数据,下面举出一个简单的例子,以说明如何在product表中插入新行:
INSERT INTO product (product_cd, name)
VALUES ('CD', 'Certificate of Depysit')
糟糕,这里将“Deposit”拼错了,不过没有关系,可以使用update语句来修复这个错误:
UPDATE product
SET name = 'Certificate of Deposit'
WHERE product_cd = 'CD';
注意,与select语句一样,update语句也包含了where子句,这是因为update语句也要识别所需修改的行。在本例中,只需要将要修改的行指定为product_cd列与字符串'CD'相匹配的那些行即可。由于product_cd列是product表的主键,因此可以预计update语句会精确地修改某一行(或零行,如果表中该值不存在)。在任何时刻执行SQL数据语句,都会收到一个来自数据库引擎的反馈,以显示该语句所影响的行数。如果使用交互式工具,比如上文提到的mysql命令行工具,那么可以接收到下面几种操作所影响行数的反馈:
select语句的返回行数;
insert 语句创建的行数;
update语句修改的行数;
delete语句所删除的行数。
可以使用过程化语言,并结合上文提到的工具集来调用SQL语句。工具集通常包含了能够获取SQL数据语句执行信息的调用。一般来说,好的做法应当是检查这个信息以确信语句执行并没有超出预料(比如忘记为delete语句增加where子句,从而删除了表中的所有行)。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。