一:了解SQL
1:列是表中的字段,所有表都由一个或多个列组成的。行是表中的记录,表中的数据都按行存储。
2:表中每一行都应该有可以唯一标识自己的一列或一组列。主键(一列或一组列),其值能够唯一区分每个行。虽然并不总是都需要主键,但应该是每个表都有一个主键。
主键满足条件:任意两行不能有相同的主键值;每个行都必须具有一个主键值。
可以使用多个列作为主键,所有列值的组合必须唯一(但单个列的值可以不唯一)。
3:SQL是结构化查询语言,一种专门用来与数据库通信的语言。几乎所有的重要DBMS都支持SQL,但是事实上任意两个DBMS实现的SQL都不完全相同,本书的SQL专门针对mysql。
二:Mysql简介
DBMS,数据库管理系统,Mysql就是一种DBMS。
mysql命令,都以”;”结束。
三:使用mysql
1:mysql使用之前,必须要登陆连接数据库。mysql在内部保存自己的用户列表,并且把每个用户和各种权限关联起来。
2:最初连接数据库后,没有任何数据库打开,在执行任何数据库操作之前,必须要选择一个数据库。使用use关键字,eg:use test;
3: show databases;返回可用数据库的一个列表。
show tables;返回当前选择的数据库的所有表。
4: show columns from testtable; 或者describe testtable;返回表testtable的所有列的信息,比如:
5: show status;显示服务器状态信息;
show grants; 显示授予用户的安装权限;
show errors; show warnings; 显示服务器错误或警告信息。
四:检索数据
1:每个sql语句都是由一个或多个关键字构成的。
2:select语句,必须给出两条信息:选择什么,从什么地方选择,比如select cust_id from testtable; 得到的数据没有过滤,也没有排序。
3:多条sql语句必须以分号分隔。sql语句不区分大小写。
4:检索多个列,select后面给出多个列名,列名之间必须以逗号分隔,最后一个列名不加。比如:select cust_id, cust_name from testtable;
5:distinct关键字,可以返回不同的值,比如select distinct cust_id from testtable; distinct关键字必须直接位于列名的前面,它应用于所有列而不仅是前置它的列。
6:limit子句,返回限定数量的行。eg:select cust_id from testtable limit 5; 或 select cust_id from testtable limit 5,5; 返回从行5开始的5行。第一个数为开始位置,第二个数为行数。第一行为0.
五:排序检索数据
1:检索出的数据顺序,可能是添加到表中的数据的顺序。但是如果不明确控制的话,不能依赖该排序顺序。
2:order by子句,取一个列或多个列的名字,对输出进行排序。eg:select cust_name from testtable order by cust_name; 用非检索的列排序数据是完全合法的,eg:select cust_id from testtable order by cust_name;
可以按多个列进行排序,列名之间用逗号分隔。多列排序时,按照循序进行,eg:select * from testtable order by cust_name, cust_id; 首先按照cust_name进行排序,如果有相同的cust_name的话,则按照cust_id排序,如下图:
3:默认的排序为升序排序,可以用desc关键字指定降序排序,eg:select cust_id from testtable order by cust_id desc; desc关键字只能应用到直接位于其前面的列名,eg:select * from testtable order by cust_name desc, cust_id; 该语句只对cust_name列指定降序,而cust_id还是升序。
4:再给出order by子句的时候,应该保证它位于from子句之后,如果使用limit,则必须位于order by之后。顺序不对会出错。
六:过滤数据
1:在select语句中,数据根据where子句中指定的搜索条件,对数据进行过滤。where子句在表名(from子句)之后给出。eg;select cust_id, cust_name from testtable where cust_id = 1;
2:如果同时使用order by和 where子句,应该让order by子句位于where之后。
3:where子句支持的操作符有:
操 作 符 |
说 明 |
= |
等于 |
<> |
不等于 |
!= |
不等于 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
BETWEEN |
在指定的两个值之间 |
IS NULL |
为NULL值 |
4:举例如下:
a: select* from testtable where cust_id = 5;
b: select* from testtable where cust_id < 5;
c: select* from testtable where cust_name = 'john'; //单引号用来限定字符串,如果将值与串类型的列进行比较,则需要限定引号。用来与数值列进行比较的值不需要引号。所以,这里如果john不加引号的话,会出错。
d: select* from testtable where cust_id between 1 and 3; //使用between时,必须指定范围的两个值,这两个值必须用and关键字分隔。
5:在一个列不包含值时,称其为包含空值NULL。NULL与包含0,空字符串不同。
一个特殊的where子句,用来判断具有NULL值的列:is null;eg:select * from testtable where cust_city is null; 得到结果如下:
注意:在匹配过滤和不匹配过滤时,并不返回NULL值的列,比如下面的语句,都不会返回任何数据:
mysql> select * from testtable where cust_city = NULL; //” = null” 不等于 “is null”
Empty set (0.00 sec)
mysql> select * from testtable where cust_city != '1';
Empty set (0.00 sec)
mysql> select * from testtable where cust_city != '';
Empty set (0.00 sec)
mysql> select * from testtable where cust_city = '';
Empty set (0.00 sec)
七:数据过滤
1:mysql允许给出多个where子句,多个where子句可以通过and或者or进行组合。eg:
select * from testtable where cust_id < 5 and cust_name = ‘john’;
select* from testtable where cust_id < 5 or cust_name = ‘john’;
2:如果组合使用and和or,应该使用括号明确优先级,尽管默认情况下and具有比or更高的优先级。eg:
select * from testtable where (cust_id < 5 or cust_name = 'john') and cust_address is null;
3:in操作符,可以指定或的关系。eg: select * from testtable where cust_id in (1,3);返回如下结果:
注意:in操作符一般比or操作符更快。要注意in和between之间的区别。
4:not操作符表示否定关系,eg:
select * from testtable where not cust_id >=5 order by cust_name;
八:用通配符进行过滤
1:在搜索语句中使用通配符,必须使用like操作符,like指示mysql,后面的搜索模式利用通配符匹配,而不是直接相等匹配。
2:mysql支持两种通配符,%表示任意字符出现任意次数;_匹配单个字符。eg:
select * from testtable where cust_name like '%i%'; //注意引号
select * from testtable where cust_name like '_i%';
3:注意,通配符的搜索一般要比其他搜索花费更长的时间,所以不要过度使用通配符。
九:用正则表达式进行搜索
1:mysql用where子句对正则表达式提供初步的支持,允许指定正则表达式过滤select检索出的数据。但是,mysql仅支持多数正则表达式实现的一个很小的子集。
2:例如:select * from testtable where cust_name regexp ‘ik’; regexp后跟的就是正则表达式。注意regexp和like的区别,如果上面的例子中,regexp换成like的话,则不会输出任何结果。因为like要求字段值完全匹配,尽管’mike’包含’ik’,但是like依然不会输出任何行。但是regexp仅仅要求包含即可。
3:mysql中的正则表达式匹配不区分大小写,也就是上面的例子,可以这样写:
select* from testtable where cust_name regexp ‘IK’。如果需要区分,则可以使用binray关键字:select * from testtable where cust_name regexp binary ‘IK’。
4:mysql支持的正则表达式:
|:或的关系,匹配其中之一;
[]:匹配其中的字符之一;
[a-z]:匹配a到z的任意字符;
*:0或多个匹配
+:1或多个匹配
?:0或1个匹配;
{n}:指定书目的匹配;
{n,}:不少于指定数目的匹配;
{n,m}:从n到m个匹配;
^:文本的开始
$:文本的结尾
[[:<:]]:词的开始
[[:>:]]:词的结尾
5:mysql中的转义字符是\,比如,如果需要匹配”.”,则regexp “\.”
6:可以在用带字符串的regexp来测试,比如:select ‘hello’ regexp ‘[0-9]’
十:创建计算字段
1:有时,存储在表中的数据并不是应用程序直接需要的,需要直接从数据库中检索出转换、计算或格式化过的数据。这就是计算字段的作用。
2:计算字段并不实际存在于数据表中,它是运行时在select语句内创建的。从应用程序的角度来看,计算字段与数据库中其他列的形式是一样的。只有数据库知道select语句中哪些列是实际的表列,哪些是计算字段。
3:在sql语句中完成的转换和格式化工作,都可以直接在应用程序内完成。但一般来说,在数据库中完成这些操作要比在应用程序中完成要快很多。
4:concat()函数可以实现字符串的拼接,eg:
select concat(cust_name, '(', cust_id, ')')from testtable;
5:rtrim()函数,删除数据右侧多余的空格。eg:select rtrim(cust_name) from testtable;
类似的,ltrim()函数,删除数据左侧多余的空格;trim()函数,删除串两边多余的空格。
6:计算字段默认没有名字,可以使用别名。这样在应用程序中就可以引用它了。别名使用as关键字。eg:
select concat(cust_name, '(', cust_id, ')') as cust_nameid from testtable;
这样,任何应用程序都可以按cust_nameid这个别名引用这个列,就像它是一个实际的表列一样。
7:计算字段,还可以对检索出的数据进行数学运算,比如:select cust_id * 2 from testtable;
可以得到所有cust_id 乘以2之后的值。mysql支持的算术操作有:+, -, *, /。
8:select语句,可以省略from子句,来测试和实验函数。比如:select 3*2; 将返回6. select trim(‘ abc ’); 将返回’abc’。
十一:使用数据处理函数
1:sql支持利用函数来处理数据。但是函数的可移植性不强,这点要注意。
2:大多数sql实现支持以下类型的函数:处理字符串的函数;处理数值的函数;处理日期和时间的函数;返回DBMS信息的函数。
3:upper(),将文本转换为大写。比如:select upper(cust_name) as nocasename from testtable;
其他的文本处理函数有:left(), length(), locate(), lower(), ltrim(), right(), rtrim(), substring(), upper(), soundex()等。可以使用help函数名,查看函数用法,比如:help left;
4:mysql中,日期和时间,采用特殊的格式存储,以便能够快速和有效的排序或过滤,并节省内存。在应用程序中,一般都要使用日期和时间函数来对数据库中的日期时间进行处理。
5:mysql使用的日期格式,不管是插入或更新表值,还是使用where子句进行过滤,日期必须为yyyy-mm-dd格式。比如:select * from testtable where cust_date = '2014-10-01 10:21:34';
虽然其他的日期格式可能也行,但是这是首选的日期格式,因为它排除了多义性。
6:日期时间格式的数据,包含日期和时间。date()函数,可以从日期时间格式的数据中,提取出日期。比如:
select * from testtable where date(cust_date) = '2014-12-01';
其他的日期时间处理函数:adddate(), addtime(),curdate(), curtime(), date(), datediff(), date_add(), date_format(), day(),dayofweek(), hour(), minute(), month(), now(), second(), time(), year()。
7:数值处理函数,这些函数一般用于代数,三角或几何运算。因此没有字符处理或日期时间处理函数使用那么频繁。常用的数值处理函数有:abs(), cos()等。
十二:汇总数据
1:mysql提供了汇总数据的函数,比如统计行数,计算最大值,最小值,平均值等。
2:avg()函数计算某列的平均值。比如:select avg(cust_num) from testtable;
avg还可以计算特定行的平均值,比如:select avg(cust_num) from testtable where cust_name = ‘john’;
avg函数只能确定单个列的平均值;avg函数忽略列值为NULL的行。
3:count()函数,统计行数。它有两种使用方式:
count(*) 对行数进行统计,不管列中包含的是空值NULL,还是非空值;
count(column)对特定列中具有值的行进行统计,忽略NULL值的行。
比如:select count(*) from testtable;返回 9;
select count(*) from testtable where cust_name = 'john';返回2;
select count(cust_name) from testtable where cust_name ='john';返回2;
select count(cust_email) from testtable ;返回0.
4:max()函数,返回指定列中的最大值,max必须指定列名,比如:
select max(cust_num) from testtable;返回98
select max(cust_num) from testtable where cust_name = 'john';返回98
select max(cust_num) from testtable where cust_name = 'tom';返回34
max函数也可以用在时间日期格式,或者字符串上。比如:
max函数忽略值为NULL的行
min函数返回最小值,与max相反,不再赘述。
5:sum()函数,用来返回指定列值的和。比如:
select sum(cust_num) from testtable; 返回359
select sum(cust_num) from testtable where cust_name = 'john'; 返回152
sum忽略NULL值的列。
6:distinct关键字,可以只对具有不同值行进行计算。它只适用于mysql 5及以后版本。all关键字是它的反义,不过all是默认的,所以可以不加。
比如:select avg(distinctcust_num) from testtable; 返回43.3750
select sum(distinct cust_num) from testtable; 返回347
select count(distinct cust_name) from testtable; 返回5
可见加了distinct关键字之后,返回结果的不同。注意,distinct 不能用于count(*),也就是不能count(distinct *),会出错。
7:组合使用,比如:
select count(*) as cust_count,
min(cust_num) as cust_minnum, max(cust_num) as cust_maxnum,
avg(cust_num) as cust_avgnum from testtable;
十三:分组数据
1:目前为止,所有的计算都是在表的所有数据或匹配特定的where子句的数据上进行的,比如:select count(*) as num_pords from products where vend_id = 1003; products表是产品表,每行一个产品,该表中记录了产品ID,供应商ID,产品名,价格,描述等信息,如下:
上面那句sql语句,就是在该表中,查询供应商1003所供应产品的总数。
但是,如果需要返回所有供应商提供的产品数怎么办?此时需要分组,分组允许把数据分为多个逻辑组,以便能够对每个组进行聚集计算。
2:分组通过select语句中的group by子句建立。比如:select vend_id, count(*) as num_prods from products group by vend_id; 该语句返回结果如下:
group by子句,指示mysql分组数据,然后对每个组而不是整个结果集进行聚集运算。所以上面的例子中,group by子句指示mysql按vend_id排序并分组数据,因而count(*)是针对每个分组进行计算。
3:除了聚集计算之外,select语句中的每个列都必须在group by子句中给出;如果分组列中具有NULL值,则NULL将作为一个分组返回,如果列中有多行NULL值,它们将分为一组。
4:group by必须在where子句之后,order by子句之前。
5:mysql允许过滤分组,规定包括哪些分组,排除哪些分组。但是因为where没有分组的概念,where过滤的是行而不是分组,所以,提供了having子句。
having子句非常类似于where,它们的句法是相同的,只是关键字有差别。
6:比如:
select cust_id, count(*) as orders from orders group by cust_id having count(*) >=2;
orders表存储顾客订单。每个订单具有订单号,订单日期,订单顾客id信息。如下:
所以,上面的sql语句,返回订单数超过2个的客户id,结果如下:
7:where是在分组前进行过滤,having是在分组后进行过滤。where排除的行不包括在分组中。下面是一个组合使用where和having的例子:
select vend_id, count(*) as num_prods from products where prod_price >= 10 group by vend_id having count(*) >= 2;
该语句返回具有2个以上,并且价格在10以上的产品的供应商。结果如下:
上面的语句,首先通过where prod_price >= 10对整体数据进行过滤,然后在过滤的基础上,找到提供两个产品以上的供应商ID。执行的顺序就是:首先where过滤,然后通过vend_id进行分组,然后having进行分组过滤。
8:虽然group by分组的数据可能以分组顺序给出,但不能保证情况总是这样,因为这不是sql规范要求的。所以,如果需要按序输出,则应该提供明确的order by语句。比如:
select order_num, sum(quantity * item_price) as ordertotal from orderitems group by order_num having sum(quantity *item_price) >= 50;
orderitmes表存储每个订单的实际物品,每个订单的每个物品站一行,其中记录了订单号,订单物品,物品数量,物品价格等。如下:
所以,上面的sql语句,返回,所有订单总价超过50的订单,结果如下:
如果需要按照总价排序,可以这样:
select order_num, sum(quantity*item_price) as ordertotal from orderitems group by order_num having sum(quantity*item_price) >= 50 order by ordertotal; 结果如下:
9:迄今为止,所有的子句顺序如下:
子句 |
说明 |
是否必须使用 |
SELECT |
要返回的列或表达式 |
是 |
FROM |
从中检索数据的表 |
仅在从表选择数据时使用 |
WHERE |
行级过滤 |
否 |
GROUP BY |
分组说明 |
仅在按组计算聚集时使用 |
HAVING |
组级过滤 |
否 |
ORDER BY |
输出排序顺序 |
否 |