一般来说SQL语句是不区分大小写的。虽然SQL是不区分大小写的,但有些标识符(如数据库名、表名、列名)可能不同:在MySQL 4.1及之前的版本中,这些标识符默认是区分大小写的;在MySQL 4.1.1版本中,这些标识符默认是不区分大小写的。这样设置的原因可能是因为不想让人把表间可以以大小写区分,让人们取更具有辨识度的名字。但是为了方便辨认,一般都把SQL关键字写成大写,对表名和列名写成小写。另外,多条SQL语句必须以分号分隔。MySQL如同多数DBMS一样,不需要在单条SQL语句后加分号。但特定的DBMS可能必须在单条SQL语句后加上分号。当然,如果愿意可以总是加上分号。事实上,即使不一定需要,但加上分号肯定没有坏处。如果你使用的是mysql命令行,必须加上分号来结束SQL语句。下面为了辨认清楚,把SQL的语句弄成加粗。
在进入具体的SQL语句学习之前,我们先大致了解一下SQL的语法。
我们可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL)。
SQL (结构化查询语言)是用于执行查询的语法。但是 SQL 语言也包含用于更新、插入和删除记录的语法。
查询和更新指令构成了 SQL 的 DML 部分,它们都是对记录进行的操作和更新。这四个语句,是最常用的SQL语句了:
- SELECT - 从数据库表中获取数据
- UPDATE - 更新数据库表中的数据
- DELETE - 从数据库表中删除数据
- INSERT - 向数据库表中插入数据
SQL 的数据定义语言 (DDL) 部分使我们有能力创建或删除表格。我们也可以定义索引(键),规定表之间的链接,以及施加表间的约束。
SQL 中最重要的 DDL 语句:
- CREATE DATABASE - 创建新数据库
- ALTER DATABASE - 修改数据库
- CREATE TABLE - 创建新表
- ALTER TABLE - 变更(改变)数据库表
- DROP TABLE - 删除表
- CREATE INDEX - 创建索引(搜索键)
- DROP INDEX - 删除索引
对于一个SQL语句来说,我们可以认为它分为几个部分:主句子、子句、某些起辅助作用的关键字。主句就是上面所说的这些关键字。子句是跟在主句后面的,不能单独存在。某些起辅助作用的关键字有不同的作用方向(例如是作用于关键字还是作用于数据,作用于左边还是右边)。而子句又需要保证有特定的顺序(例如:在给出ORDER BY子句时,应该保证它位于FROM子句之后。如果使用LIMIT,它必须位于ORDER BY之后。使用子句的次序不对将产生错误消息)。
一、SELECT
数据库中使用得最多的操作就是查询了。SQL语句一般返回原始的、无格式的数据。数据的格式化是一个表示问题,而不是一个检索问题。因此,表示(对齐和显示上面的价格值,用货币符号和逗号表示其金额)一般在显示该数据的应用程序中规定。一般很少使用实际检索出的原始数据,而是使用应用程序处理过的数据,这样方便人们使用和阅读。SELECT语句返回的事实上是很多行,它符合某个条件(如某些列)。在后面会学到不同的SELECT子句。而这些SELECT子句如果出现在SELECT句子中,是需要遵循一定的出现顺序的。这里先总结如下:
1、检索一个列
SELECT pro_name FROM products;
上述语句利用 SELECT语句从 products表中检索一个名为prod_name的列。所需的列名在SELECT关键字之后给出, FROM关键字指出从其中检索数据的表名。但是注意,如果没有明确排序查询结果(后面介绍),则返回的数据的顺序没有特殊意义。返回的列的顺序只是在数据库中物理层面上存放的数据的顺序。但是如果对数据库有过更改,导致内部物理存储的数据结构并不是连续的,那这样顺序就会跟原来表显示的不一样。返回数据的顺序可能是数据被添加到表中的顺序,也可能不是。但是只要返回相同数目的行,就是正常的。
2、检索多个列
SELECT prod_id, prod_name, prod_price FROM products;
使用SELECT语句从表products中选择了三个列的数据。三个列顺序无意义,但是一行肯定是同属于原来表的同一行的。
3、检索所有列
SELECT * from products;
如果给定一个通配符(*),则返回表中所有列。列的顺序一般是列在表定义中出现的顺序。但有时候并不是这样的,表的模式的变化(如添加或删除列)可能会导致顺序的变化。行的顺序跟上面一样,也不确定。
4、只返回不重复的列
SELECT DISTINCT vend_id, prod_price FROM products;
DISTINCT关键字是只返回不同的值,不返回相同的值。它应用于所有列而不仅是前置它的列。在这里,vend_id和prod_price这两列要两者的组合都不同,才会认为是不同。注意,这个DISTINCT只是一个起返回唯一值作用的关键字,并不是SELECT的子句。后面会讲子句的内容。严格来说,DISTINCT是跟在SELECT后面的,作用于SELECT,它们一起组成一个SELECT DISTINCT语句。
二、限制结果
限制结果,也就是对SELECT出的结果,进行一些条件的补充。
1、只返回自己想要的行数
SELECT prod_name FROM products LIMIT 5;
LIMIT 5意思是返回的结果不多于5行。这里FROM事实上是跟在SELECT后的子句。子句的内容稍后会讲。
SELECT prod_name FROM products LIMIT 5, 5;
它意思是返回从行5开始的5行。注意,检索出来的行数是从下标0开始的。例如, LIMIT 1, 1将检索出第二行而不是第一行。
2、使用完全限定名
事实上,前面所使用的各种名字,如products,prod_id等,都不是完全限定名,因为对于表来说,肯定是有一个完整路径的。完全限定名用.来划分,这跟绝大多数程序设计语言的习惯是一样的。如:
SELECT products.prod_name FROM crashcourse.products;
三、进行排序
先明确什么是子句。一个子句通常由一个关键字和所提供的数据组成。子句的例子有SELECT语句的FROM子句。子句不能单独存在,而是跟在语句后面。为了明确地排序用SELECT语句检索出的数据,可使用ORDER BY子句。ORDER BY子句取一个或多个列的名字,据此对输出进行排序。
对于排序来说,又有一个问题。在字典( dictionary)排序顺序中,A被视为与a相同,这是MySQL(和大多数数据库管理系统)的默认行为。但是,许多数据库管理员能够在需要时改变这种行为(如果你的数据库包含大量外语字符,可能必须这样做)。这里,关键的问题是,如果确实需要改变这种排序顺序,用简单的ORDER BY子句做不到。你必须请求数据库管理员的帮助。
1、按一个列排序
SELECT prod_name FROM products ORDER BY prod_name;
把结果按照prod_name字母序排序。注意,通常, ORDER BY子句中使用的列将是为显示所选择的列。但是,实际上并不一定要这样,用非检索的列排序数据是完全合法的。
2、按多个列排序
为了按多个列排序,只要指定列名,列名之间用逗号分开即可。
SELECT prod_name FROM products ORDER BY prod_price, prod_name;
在这里,会先按照prod_price排序,在prod_price相同的情况下,才再按照prod_name来排序。
3、指明排序方向
可以使用DESC(降序)和ASC(升序,事实上没什么用,因为本来默认的排序就是升序的)关键字。它们只能作用于直接位于它们前面的列名,而不是所有列。如果想在多个列上进行降序排序, 必须对每个列指定关键字。
SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC, prod_name;
只对prod_price列指定 DESC,对 prod_name列不指定。因此,prod_price列以降序排序,而prod_name列(在每个价格内)仍然按标准的升序排序。
三、WHERE子句
数据库表一般包含大量的数据,很少需要检索表中所有行。通常只会根据特定操作或报告的需要提取表数据的子集。只检索所需数据需要指定搜索条件( search criteria) ,搜索条件也称为过滤条件( filter condition) 。
1、检查范围
在SELECT语句中,数据根据WHERE子句中指定的搜索条件进行过滤。WHERE子句在表名(FROM子句)之后给出。WHERE子句后面跟着过滤条件。它支持的操作符有:
注意,如果将值与串类型的列进行比较,则需要限定单引号。而且是不区分大小写。如,检查WHERE prod_name=‘fuses’语句,它返回prod_name的值为Fuses的一行。 MySQL在执行匹配时默认不区分大小写,所以fuses与Fuses匹配用来与数值列进行比较的值不用引号。
SELECT prod_name, prod_price FROM products WHERE prod_price < 10;
SELECT prod_name, prod_price FROM products WHERE vend_id <> 1003;
SELECT prod_name, prod_price FROM products WHERE prod_price BETWEEN 5 AND 10;
注意,使用BETWEEN时候,是闭区间。BETWEEN是一个操作符。
2、检查空值
NULL 无值(no value),它与字段包含0、空字符串或仅仅包含空格不同。在创建表时,表设计人员可以指定其中的列是否可以不包含值。
SELECT prod_name FROM products WHERE prod_price IS NULL;
IS NULL语句是WHERE的子句,返回没有价格的所有产品名字。
3、逻辑操作符
AND和OR操作符,用来连接WHERE子句的关键字,形成一些条件。注意,AND的优先级更高, SQL(像多数语言一样)在处理OR操作符前,优先处理AND操作符。如果要人为改变次序,那就要使用圆括号。
SELECT prod_id, prod_price, prod_name FROM products WHERE vend_id = 1003 AND prod_price <=10;
SELECT prod_name, prod_price FROM products WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >=10;
IN操作符。IN操作符用来指定条件范围,范围中的每个条件都可以进行匹配。 IN取合法值的由逗号分隔的清单,全都括在圆括号中。它其实完成的功能跟OR是一样的,只不过写得更加简单。
SELECT prod_name, prod_price FROM products WHERE vend_id IN (1002, 1003, 1004) ORDER BY prod_name;
NOT操作符。WHERE子句中的NOT操作符有且只有一个功能,那就是否定它之后所跟的条件。
SELECT prod_name, prod_price FROM products WHERE vend_id NOT IN (1002, 1003, 1004) ORDER BY prod_name;
4、使用通配符
怎样搜索产品名中包含文本anvil的所有产品?用简单的比较操作符肯定不行,必须使用通配符。通配符就是用来比较值的一部分的特殊字符。这个有点像正则表达式。为在搜索子句中使用通配符,必须使用LIKE操作符。 LIKE指示MySQL,后跟的搜索模式利用通配符匹配而不是直接相等匹配进行比较。一般使用方法是LIKE的后面加一个搜索条件。事实上,LIKE后面加上的一个搜索条件,就是由字面值、通配符或两者组合构成的一个串。要记住,默认都是不区分大小写的,除非人为设置过数据库。
(1)%
最常使用的通配符是百分号(%)。在搜索串中, %表示任何字符出现任意次数,也就是0次或多次。注意,空格也算字符。如果想去掉首尾空格,最好使用一些函数,后面会讲。%无法匹配NULL。
SELECT prod_id, prod_name FROM products WHERE prod_name LIKE '%jet%';
在执行这条子句时,将检索任意包含jet的词。
(2)_
匹配单个字符。
5、使用正则表达式
为了在搜索子句中使用正则表达式,要使用REGEXP关键字。这个时候有个问题了,正则表达式是区分大小写的,但是SQL不区分大小写,这个时候如何处理?可以在REGEXP后面跟一个BINARY关键字,这样这个正则表达式就是区分大小写的了。
SELECT prod_id, prod_name FROM products WHERE prod_name REGEXP BINARY 'JetPack .000';
四、创建计算字段
字段(field) 基本上与列( column) 的意思相同,经常互换使用,不过数据库列一般称为列,而术语字段通常用在计算字段的连接上。计算字段其实就是指通过一些操作把检索出来的字段变成新的字段。计算字段并不实际存在于数据库表中。计算字段是运行时在SELECT语句内创建的。
1、拼接字段
注意,这里的拼接和后面要讲的连接并不一样。这里只是把检索出来的字段拼接在一起,本质上还是在一个表中的操作。而连接是涉及多个表的。使用Concat()函数来拼接两个列。Concat()拼接串,即把多个串连接起来形成一个较长的串。Concat()需要一个或多个指定的串,各个串之间用逗号分隔。对于函数这里做一个简单介绍。与其他大多数计算机语言一样, SQL支持利用函数来处理数据。函数一般是在数据上执行的,它给数据的转换和处理提供了方便。然而,多数SQL语句是可移植的,在SQL实现之间有差异时,这些差异通常不那么难处理。而函数的可移植性却不强。几乎每种主要的DBMS的实现都支持其他实现不支持的函数,而且有时差异还很大。为了代码的可移植,许多SQL程序员不赞成使用特殊实现的功能。虽然这样做很有好处,但不总是利于应用程序的性能。如果不使用这些函数,编写某些应用程序代码会很艰难。必须利用其他方法来实现DBMS非常有效地完成的工作。如果你决定使用函数,应该保证做好代码注释,以便以后你(或其他人)能确切地知道所编写SQL代码的含义。
SELECT Concat(vend_name, '(', vend_country, ')') FROM vendors ORDER BY vend_name;
上面提到过去掉空格的问题。去掉空格使用函数RTrim(), LTrim()和Trim(),分别是去掉值右边、左边、中间的所有空格。
SELECT Concat(Trim(vend_name), '(', vend_country, ')') FROM vendors ORDER BY vend_name;
上面的例子,把一个字段拼接起来了,但是它却没有名字,这样很不方便使用。因此,我们需要使用AS关键字来赋予别名。
SELECT Concat(Trim(vend_name), '(', vend_country, ')') AS vend_title FROM vendors ORDER BY vend_name;
2、执行算术运算
有时候,需要对某些字段执行算术运算,把结果放到一个我们需要的新的列。这个时候,要使用算数操作符,其实也就是+=*/。
SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price FROM orderitems WHERE order_num=20005;
五、数据处理函数
上面所说的RTrim()就是一种函数。这里要注意函数的可移植性问题。相对来说,多数SQL语句是可移植的,在SQL实现之间有差异时,这些差异通常不那么难处理。而函数的可移植性却不强。几乎每种主要的DBMS的实现都支持其他实现不支持的函数,而且有时差异还很大。对于SQL来说,一般支持的函数都是以下类型的。他们一般为了方便辨认,写法都是大小写混用。
1、处理文本串函数;2、数值算术操作函数;3、处理日期和时间函数;4、返回DBMS信息(如用户登录信息、版本细节)函数。
以下用一些简单例子来说明一些函数的使用。
1、文本处理函数
一些常用的文本处理函数如下:
SELECT vend_name, Upper(vend_name) AS vend_name_upcase FROM vendors ORDER BY vend_name;
Upper()函数将文本转换为大写。
SELECT cust_name, cust_contact FROM customers WHERE Soundex(cust_contact) = Soundex('Y Lie');
要注意一下的是这个Soundex()函数。它匹配所有发音类似于Y.Lie的联系名。customers表中有一个顾客Coyote Inc.,其联系名为Y.Lee。但如果这是输入错误,此联系名实际应该是Y.Lie。Soundex()函数就是用于这种情况。
2、日期和时间处理函数
在数据库中,日期和时间采用相应的数据类型和特殊的格式存储,并不是普通的字符串。以便能快速和有效地排序或过滤,并且节省物理存储空间。以下是一些常用的日期和时间处理函数。
对于MySQL,使用日期时必须使用日期格式yyyy-mm-dd。虽然其他的日期格式可能也行,但这是首选的日期格式,因为它排除了多义性(如,04/05/06是2006年5月4日或2006年4月5日或2004年5月6日或……)。
为什么要使用日期处理函数?例如,有一个语句SELECT cust_id, order_num FROM orders WHERE order_date = '2005-09-01';,这个语句使用WHERE order_date = '2005-09-01'是不可靠的。因为事实上order_date这个时间也许是有小时和分秒的,如2005-09-01 11:30:05。而'2005-09-01'这个日期默认的是00:00:00。这样两个时间是匹配不上的,匹配失败。这个时候就要取日期处理函数,把年月日的部分取出来进行比较,这个时候就是使用Date函数。
SELECT cust_id, order_num FROM orders WHERE Date(order_date) = '2005-09-01';
3、数值处理函数
数值处理函数仅用来处理数值数据。常用数值处理函数如下:
六、汇总数据
我们经常需要汇总数据而不需要把数据实际检索出来。汇总数据的意思是,例如我们需要查看2005-01-01当天一共有多少宗交易,这个时候我们只关心交易的总数,而不关心具体的每一次交易的内容。如果只是为了得到这些汇总数据,而把表中全部数据取出来,这样很低效。为了实现汇总数据的功能,我们需要借助聚集函数。所谓聚集函数就是运行在行组上,计算和返回单个值的函数。
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的GROUP BY和HAVING子句。
1、创建分组。创建分组使用GROUP BY子句。注意,使用它有以下一些规定:
SELECT vend_id, COUNT(*) AS num_prods FROM products GROUP BY vend_id;
上面的SELECT语句指定了两个列, vend_id包含产品供应商的ID,num_prods为计算字段(用COUNT(*)函数建立)。 GROUP BY子句指示MySQL按vend_id排序并分组数据。这导致对每个vend_id而不是整个表计算num_prods一次。
要注意的是,GROUP BY虽然默认似乎会对列进行排序,但是事实上是不可靠的。这样做并不是SQL规范所规定的。一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。千万不要仅依赖GROUP BY排序数据。另外,这里也给出GROUP BY子句和ORDER BY子句的差别:
2、过滤分组。过滤分组使用的是HAVING子句。 HAVING非常类似于WHERE。HAVING的作用是对分组进行一些条件的过滤,他和WHERE的区别是WHERE是对行进行一些条件的过滤。事实上,目前为止的所有类型的WHERE子句都可以用HAVING来替代。唯一的差别是WHERE过滤行,而HAVING过滤分组。WHERE在数据分组前进行过滤, HAVING在数据分组后进行过滤。WHERE和HAVING也可以混用。
SELECT cust_id, COUNT(*) AS orders FROM orders GROUP BY cust_id HAVING COUNT(*)>=2;
SELECT vend_id, COUNT(*) AS num_prods FROM products WHERE prod_price>=10 GROUP BY vend_id HAVING COUNT(*)>=2;
八、子查询
子查询的意思是,从可以把一条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句。这样可以跨越表进行查询,或者使用复杂的嵌套查询条件。
SELECT cust_id FROM orders WHERE order_num IN(SELECT order_num FROM orderitems WHERE prod_id = 'TNT2');
在SELECT语句中,子查询总是从内向外处理。在处理上面的SELECT语句时, MySQL实际上执行了两个操作。首先,它执行SELECT order_num FROM orderitems WHERE prod_id = 'TNT2'。此查询返回两个订单号: 20005和20007。然后,这两个值以IN操作符要求的逗号分隔的格式传递给外部查询的WHERE子句。外部查询变成:SELECT cust_id FROM orders WHERE order_num IN(20005, 20007);
九、组合查询
组合查询,顾名思义就是把多个查询组合起来,以一个查询结果集的形式返回。有这两种情况的时候,需要使用组合查询:
在单个查询中从不同的表返回类似结构的数据;
对单个表执行多个查询,按单个查询返回数据。
要使用UNION操作符来进行组合查询。利用UNION,可给出多条SELECT语句,将它们的结果组合成单个结果集。UNION的使用很简单。所需做的只是给出每条SELECT语句,在各条语句之间放上关键字UNION。UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次序列出)。列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含地转换的类型(例如,不同的数值类型或不同的日期类型)。要注意的是,UNION从查询结果集中自动去除了重复的行。如果想保留重复的行,可使用UNION ALL而不是UNION。如果想对组合结果排序,在用UNION组合查询时,只能使用一条ORDER BY子句,它必须出现在最后一条SELECT语句之后。对于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY子句。
SELECT vend_id, prod_id, prod_price FROM products WHERE prod_price <=5 UNION SELECT vend_id, prod_id, prod_price FROM products WHERE vend_id IN (1001, 1002) ORDER BY vend_id, prod_price;
这条语句由前面的两条SELECT语句组成,语句中用UNION关键字分隔。UNION指示MySQL执行两条SELECT语句,并把输出组合成单个查询结果集。这条UNION在最后一条SELECT语句后使用了ORDER BY子句。虽然ORDER BY子句似乎只是最后一条SELECT语句的组成部分,但实际上MySQL将用它来排序所有SELECT语句返回的所有结果。
参考资料《MySQL必知必会》