MySQL权威指南读书笔记(1)。
第一章,MySQL和SQL入门(一,对数据库的理解)
这一章共78页,我看了两周。
很少有技术类书籍一开篇就讨论SELECT语句的,这是此书给我的第一个印象,本章把这个语句讲解得十分详细。我觉得第一章主要是讨论两个问题:1,通过两个样板数据库讲解对数据库理论的理解;2,基本命令的使用。比较高深的东西好像不多。
一,对数据库的理解
为什么要使用数据库呢,我们都看过许多说法,不外于其提供了强大的数据处理能力,如果仅仅是简单的数据列表,电子表格就完全胜任了。在数据的世界里,MySQL被划分为关系数据库管理系统的范畴内,我们可以把这个短语划分为以下几个部分: 数据库:存放信息的的资料库,其构造既简单又遵守一定的规律 数据库内的数据都存放在数据表(table)里 数据表是由数据行(row)和数据列(colume)构成的 一个数据行就是数据表内的一条记录(record) 一个记录一般包含多个信息,数据表中的每一个数据列都对应着一个信息 关系:把存放在某个数据表内的信息和存放在另一个数据表内的信息通过某种方式关联起来,而这种关联就是通过查找两个数据表有无共同的元素来实现的。
先看一个简单的例子:
你的站点上有大量的广告,每当客户浏览你的网页时,一旦点击你的广告,你就需要做记录来计算自己得到了多少广告费。从数据库的角度来说,你应该建立三个数据表。
1,各个广告的来源company表 Company Company_num Address phone SONY 13 马家沟 0451-21 CANON 14 新阳路 0451-22 2,各个广告的数据ad表 Company_num Ad_num Hit_fee 12 48 001 13 49 003 14 50 004 3,每个广告的点击率hit表 Ad_num Date 48 3.12 49 3.14 48 3.12 50 3.14
看看我们能通过这三个数据表查询到什么信息?
1,你为多少家公司做广告? 查一查company表有多少行就知道了 2,你一共有多少个广告? 查一查ad表有多少行就知道了 3,在3.12这一天,你一共显示了多少个广告? 查一查hit表里面Date数据列的值是3.12的数据行共有多少? 4,在3.14这一天,CANON的广告被点击了几次? 首先在company表中找到CANON公司的公司代码(14),根据这个代码在ad数据表中找到CANON公司的广告代码(50实际上可能是很多个),然后分别在hit表中找到3.12日这个代码的点击率。
看到这里我们就能明白,为什么要费力气地把数据“分散”到多个表里面,而不是放到一个大表格中,数据库的非凡能力就表现在这里,把分散在多个数据表中的数据用一种相互匹配的方式迅速地搜集到一起得出结果,这就是数据库式的思维方式。我们必须慢慢习惯,然后自觉运用。是不是很简单?
如何让读者更好地理解这种“关系”的概念,书中给了两个样板数据库,听我慢慢道来: 1,你现在是美国历史研究会的秘书,这个协会是由一些对美国历史感兴趣的人自发地组织起来的,由于各人的爱好,他们将定期地交纳一定的会费来维持其会员资格,交上来的会费主要用于支付研究会的各种开支,如印刷会员刊物等。这个研究会目前建有一个小型的互联网站点,但这个站点还没有得到充分的开发利用,如果你的主要工作是用字处理来打印会员名录的话,数据库就没有必要的,但是许多的工作要求很复杂的操作:如,你希望能够根据不同的情况把会员名录输出为其它格式的资料;根据特定的条件来查询会员;能够根据某种限制得到统计数字,等等,如果把研究会的会员资料放到网站上,那么对会员自己来说,以在线方式修改自己的资料,查询其他人的资料就会容易得多。甚至会员们可以互相发布消息,写电子邮件,等等。 2,你是一名教师,在每学期中,你负责考试和测验,记录各种分数,期末时,你要对学生们的成绩进行总评。并把学生们的总评成绩和出勤情况上报给校方,如果不使用数据库,你就必须每学期都手工完成这些工作。
如果用传统方法,手工来统计考试积分表,你的表格可能就会是这样:
学生
ID 姓名 分数 Q Q T Q Q T 9/3 9/6 9/9 9/16 9/23 10/1 … 1 2 3 4 … 李寻欢 荆无命 吕奉先 王怜花 … 14 17 15 14 … 10 10 10 13 … 73 68 78 85 … 14 17 12 13 … 15 14 17 19 … 67 73 82 79 … … … … … …
毫无疑问,从关系的角度来说,这样的表格是无法直接放到数据库里面的。我们必须对其进行分解。当然,分数表(score表 )是最先必须建立的。最简单的模型可能是这样:
Name Date Score 李寻欢 9.3 14 荆无命 9.3 17 吕奉先 9.3 15 王怜花 9.3 14 李寻欢 9.6 10 荆无命 9.6 10 吕奉先 9.6 10 王怜花 9.6 10 我们很快就能发现这个表的问题:它丢失了一些数据,我们不能知道当天进行的是考试还是测验。看来需要加入一个数据列: Name Date Score Type 李寻欢 9.3 14 Q 荆无命 9.3 17 Q 吕奉先 9.3 15 Q 王怜花 9.3 14 Q 李寻欢 9.6 10 T 荆无命 9.6 10 T 吕奉先 9.6 10 T 王怜花 9.6 10 T 我们又发现了一个问题,那就是本表的多余数据太多了,同一天里面的Type数据列的值是相同的,这种数据冗余是完全没有必要的。把表拆分一下如何: score表 Name Date Score 李寻欢 9.3 14 荆无命 9.3 17 吕奉先 9.3 15 王怜花 9.3 14 李寻欢 9.6 10
荆无命 9.6 10 吕奉先 9.6 10 王怜花 9.6 10 event表 date Type 9.3 Q 9/6 Q 9/9 Q 9/16 T 9/23 T 10/1 T 看起来好多了,虽然数据表的数据多了一个,但对数据库来说完全不是问题,我们可以继续考虑我们的成绩表:如果一天内有两场考试怎么办?我们需要在同一天内记录两组分数!既然如此,可以为每一次考试或者测验分配一个独一无二的编号,用这个编号来关联两个表,这样就避开了日期重复的问题了。 score表 Name Event_id Score 李寻欢 1 14 荆无命 1 17 吕奉先 1 15 王怜花 1 14 李寻欢 2 10
荆无命 2 10 吕奉先 2 10 王怜花 2 10 event表 Event_id date Type 1 9.3 Q 2 9/6 Q 3 9/9 Q 4 9/16 T 5 9/23 T 6 10/1 T 这样就完成了数据表的规划吗?好像还不行,考虑一下,如果有重名的学生如何处理呢?看来,用学号来解决这个问题就行了。 1,student表 Name Sex Student_id 李寻欢 F 1 荆无命 F 2 吕奉先 F 3 王怜花 F 4 2,score表 Student_id Event_id Score 1 1 14 2 1 17 3 1 15 4 1 14 1 2 10 2 2 10 3 2 10 4 2 10 3,event表 Event_id Date 9.3 Q 9/6 Q 9/9 Q 9/16 T 9/23 T 10/1 T
终于完成了考试积分表的规划!我们学到了什么呢?数据库应该是什么样子,它应该包含哪些数据表,各个数据表应该有什么内容以及数据应该如何表示?我们看到的许多理论性很强的教材,就在讲述“数据库的分析与设计”,“实体关系图”,“规范化过程”,“第三范式”等等很枯燥的概念。至少在本章中作者没有提到这些东西,而是用一种比较自然,直观的途径让我们了解了一些数据库的基本知识,我觉得这一点很值得推崇。
数据库上 1最常见的操作是对现有数据进行检索; 2比较常见的操作是插入新数据; 3比较不常见的操作是创建数据表; 4最不常见的操作是创建数据库。 我们学习的步骤是从4到1, ①先看看如何建立我们的样板数据库,一个命令就行了 CREATE DATABASE sampdb; ②建立各个数据表 1建立“美国历史研究会”里面的总统表 CREATE TABLE president ( last_name VARCHAR(15) NOT NULL, first_name VARCHAR(15) NOT NULL, suffix VARCHAR(5) NULL, city VARCHAR(20) NOT NULL, state VARCHAR(2) NOT NULL, birth DATE NOT NULL, death DATE NULL ); 2建立“美国历史研究会”里面的会员表 CREATE TABLE member ( member_id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (member_id), last_name VARCHAR(20) NOT NULL, first_name VARCHAR(20) NOT NULL, suffix VARCHAR(5) NULL, expiration DATE NULL DEFAULT '0000-00-00', email VARCHAR(100) NULL, street VARCHAR(50) NULL, city VARCHAR(50) NULL, state VARCHAR(2) NULL, zip VARCHAR(10) NULL, phone VARCHAR(20) NULL, interests VARCHAR(255) NULL ); 3,建立记分系统中的student表 CREATE TABLE student ( name VARCHAR(20) NOT NULL, sex ENUM('F','M') NOT NULL, student_id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (student_id) ); 4,建立记分系统中的score表 CREATE TABLE score ( student_id INT UNSIGNED NOT NULL, event_id INT UNSIGNED NOT NULL, PRIMARY KEY (event_id, student_id), score INT NOT NULL ); 5,建立记分系统中的event表 CREATE TABLE event ( date DATE NOT NULL, type ENUM('T','Q') NOT NULL, event_id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (event_id) ); ③插入新数据,操作命令可以有多种格式,我们用最简单的student表举个例子 INSERT INTO student VALUES ('Megan','F',1); INSERT INTO student VALUES ('Joseph','M',2); INSERT INTO student VALUES ('Kyle','M',3); INSERT INTO student VALUES ('Katie','F',4); INSERT INTO student VALUES ('Abby','F',5);
需要详细介绍的是数据的检索,本章中,作者列举了10种情况,我将在下次贴出,并对本章作个总结。
To be continued… 二,select 的使用
现在,数据表都已经创建起来了,假设我们已经插入了许多的数据,我们就可以用自己喜欢的方式对数据表里面的信息进行检索和显示了,比如说:可以象下面这样把整个数据表内的内容都显示出来 select * from president; 也可以只选取某一个数据行里的某一个数据列 select birth from president where last_name=’Eisenhower’; select语句的通用形式如下: select 你要的信息 from 数据表(一个或多个) where 满足的条件 select语句有几个子句,他们的各种搭配能帮你查出最感兴趣的信息,这些子句可以很简单,也可以很复杂,看看作者是如何详细讲解的
1, 用各种操作符来设定检索条件 要想让select语句只把满足特定条件的记录检索出来,就必须给它加上where字句来设置数据行的检索条件。只有这样,才能有选择地把数据列的取值满足特定要求的那些数据行挑选出来。可以针对任何类型的值进行查找,比如说,对数值进行搜索 select * from score where score>95; //显示所有分数在95分以上的信息 也可以针对字符串值进行查找 select last_name,first_name from president where last_name=’Tom’; //找出所有姓tom的总统 还可以对不同类型的值进行组合查找 select last_name,first_name,birth,state from president where birth<’1950-1-1’ and (state=’VA’ or state=’BA’); //找出1950年前出生于VA州或BA州的总统 可见 where子句中可以使用的是算术操作符(+-*/%),比较操作符(<>=)以及逻辑运算符,我们应该熟练理解这些操作符的含义(都很简单)
2, NULL 值的特别处理 这是一种不属于任何类型的值。它通常用来表示“没有数据”“数据未知”“数据缺失”“数据超出取值范围”“与本数据列无关”“与本数据列的其它值不同”等多种含义。在许多情况下,NULL 值是非常有用的。 我们的各种操作符是不能对NULL 值进行处理的,如果相对NULL 值进行查找,用的是 is null 或 is not null 来进行判断,举例如下: select last_name,first_name,birth,state from president where death is null; //找出所有没死的总统 在某些情况下,NULL 值是很有用的类型,大家慢慢就会理解的。
3, 查询结果进行排序 一般说来,如果创建了一个数据表并向里面插入了一些记录,当发出一条select * from name命令的时候,数据记录在查询结果中的先后顺序通常与它们被插入时的先后顺序一样.这当然符合我们的思维习惯.但这只是一种"想当然"的假设而已,事实上,但记录被删除时,数据库中会产生一些空的区域,MYSQL会用新的记录来填补这些区域,也就是说,这个时候本假设就不正确了.因此我们必须记住一点,从服务器返回的记录行的先后顺序是没有任何保证的!如果想要按照一定的顺序,就必须使用order by 子句来设置这个顺序. select last_name,first_name,birth,state from president order by last_name; //让总统们的名字按字母顺序排列 还可以设置排列的升序降序 select last_name,first_name from president order by state DESC,last_name ASC; //先按照出生地的降序排列,同出生地的按照姓氏的升序排列 注意:如果结果中含有NULL 值,默认情况下他们总是出现在查询结果的开头。
4, 限制查询结果中数据行个数 这个简单,只要用limit 子句就可以了,看两个例子: select last_name,first_name,birth,state from president order by birth limit 5; //只想看前5个 order by birth limit 10,5; //返回从第11个记录开始的5个记录(跳过了10个) 小技巧:从president表中随机找出一个总统来玩: select last_name,first_name,birth,state from president order by rand() limit 1; //这是用了表达式求值的方法,在哪里都管用
5, 对输出列进行求值和命名 为了提高效率,MYSQL还可以把表达式的计算结果当作输出列的值。表达式可以很简单,也可以很复杂。例如:下面这个查询有两个输出列,前一个输出列对应一个非常简单的表达式(一个常数),而后一个输出列则对应着一个使用了多个算术运算符和两个函数调用的复杂表达式。 Select 17,format(sqrt(3*3+4*4),0)) 输出:17 5 再看这个命令:把两个输出列合并成一个 select concat(first_namem,’ ‘,last_name),concat(city,’,’,state) from president; 如果合并之后输出列的标题过长,则可以给其一个别名,如: select concat(first_namem,’ ‘,last_name) as name, concat(city,’,’,state) as birth place from president;这样就比较美观了。
6, 和日期有关的问题 首先记住:在MYSQL中,年份是放到最前面的!我们通常对日期进行下列操作: 按日期进行排序 查找某个日期或日期范围 提取日期中的年,元,日各个部分 计算两个日期的间隔 用一个日期求出另外一个日期 看例子: select * from event where date=’2002-10-01’ //看看这天有何考试信息? select last_name,first_name,birth,state from president where death>’1900-01-01’ and death<’2000-01-01’; //看看上个世纪死了几个? 三个函数year,month,dayofmonth可以分别分离出日期中的年月日来。 select last_name,first_name,birth from president where month(birth)=3; //谁生在3月 ? where month(birth)=7 and dayofmonth(birth) =6; //谁生在7月6日?(汤姆克鲁斯?) 函数to_days可以把日期转换为天数。 select last_name,first_name,birth to_days(death)-to_days(birth) as age from president 可以看看这帮家伙都活了多少天!你自己把它改为年吧。 日期值的减法运算还能帮我们计算出现在距离某个特定日期还有多长的时间,这正是我们用来找到需要在近期内缴纳会费的会员的办法: select last_name,first_name,expiration from member where (to_days(expiration)-to_days(curdate())<60; //有些人60天内需要花钱了!
7, 模式匹配 有些情况下,模糊查询是很必要的,我们使用like和not like加上一个带通配符的字符串就可以了。共有两个通配符”_”(单个字符)和”&”(多个字符) select concat(first_namem,’ ‘,last_name) as name, where last_name like ‘W%’; //找到以W或w开头的人 where last_name like ‘%W%’; //找到名字里面W或w开头的人
8, 设置和使用SQL变量 MYSQL 3.23.6以上的版本可以使用查询结果来设置变量,我们就能够方面的把一些结果保存起来以供他用。变量的命名规格是:@name, 赋值语法是 @name:=value ( pascal?) 使用起来也简单: select @birth:=birth from president where last_name =’adsltiger’; //执行完成后我们就就会有一个@birth变量可用 用一下试试: select concat(first_namem,’ ‘,last_name) as name from president where birth<@birth order by birth; //看看那些人比我大!
*9, 生成统计信息 单纯依靠手工来生成统计信息是一项既艰苦又耗时还容易出错的工作,如果我们能熟练掌握用数据库来生成各种统计信息的技巧,他就会成为很有威力的信息处理工具。作者在这里用了许多篇幅讲这个主题,为了便于大家理解,我分解开来论述: 9.1 找出一组数据中到底有多少种不同的值是一项比较常见的统计工作,而关键字distinct就可以把查询结果中的重复数据清除掉。如 select distinct state from president //看看美国总统们都来自那些州?(重复的不计) 9.2用count()函数来统计相关记录的个数,注意其使用方法:count(*)计算所有的,NULL也要;count(数据列名称) NULL值不计算在内。 select count(*) from president; 9.3如果我们想知道班级内的男女生数目?该如何查询呢?最简单的方法是 select count(*) from student where sex=’f’; select count(*) from student where sex=’m 但是如果使用count函数结合group by关键字,一行命令就搞定了 select sex,count(*) f rom student group by sex; 我们可以看到,与反复使用彼此类似的查询来分别统计某数据列不同取值出现次数的做法相比, 把count(*)和group by字句相结合使用有许多优点,主要表现在: 在开始统计自前,不必知道被统计的数据列里面有多少种不同的取值 因为只用了一个查询命令,我们可以对输出做排序的处理 select state,count(*) as count from president group by state order by count desc limt4; //看看出生总统最多的前四个州是哪几个? 9.4除了count(),我们还用其他一些统计函数,如求出最小值的min(),求最大值的max(),求和的sum(),求平均值的avg(),在实际工作中,这些函数时经常用到的!
*10, 从多个表提取信息 我们目前的例子都是从一个表里面提取信息,但数据库的真正威力还在于用“关系”来综合多个数据表里面的记录,这种操作称之为“关联”或“结合”我们可以看到,select需要给出多个数据表里面的信息(不可重复);from需要知道从哪几个表里面做事;where则对几个表之间的关联信息作出详细的描述。 首先我们要学习最可靠的数据列引用方式:数据表名.数据列名。这样在查询中就一定不会混淆这个数据列到底在哪一个表里。 例子1:查询某一天内的学生们的考试成绩,用学号列出。 select scroe.student_id,event_date,score.score.event.type from event,score where event.date=’2003-09-12’ and event.event_id=score.event_id 首先,利用event数据表把日期映射到一个考试事件编号,在利用这个编号把score表内相匹配的考试分数找出来。关联两个表,一个查询搞定。 例子2:查询某一天内的学生们的考试成绩,用姓名列出。 select student.name event.name,score.score,event.type form event,score,student where event.date=’2003-09-12’ and event.event_id= score.event_id and scroe.student_id=student.student_id; 关联三个表,一个查询搞定。 例子3:查询一下缺席学生的名字,学号,缺席次数 select student.student_id,student_name count(absence.date) as absences from student,absence where student.student_id=absence.student_id //关联条件 group by student.student_id;
简单的关联操作就介绍到这里。事实上,对于关联的知识我们需要学的很多很多,比如说,我们怀疑某一个数据表内不存在和我们相关的数据,把么在关联查询的时候如何处理这个表呢?这就涉及到内联接,外联接,左联接,右联接的许多新概念了。不知道大家还有没有信心向下看我的笔记?在本书第四章里面,对关联进行了十分详细的论述,看来“在SQL里面,干粗活的是select”的说法再对不过了。
我们了解了select命令的如此之多的用法,感到了它的灵活性,许多字句的组合能够形成一个非常“精妙”的SQL语句,在基础没有打好之前,我等初学者目前还没有必要去钻研那些技巧性很高的东西,“一定程度的创造性是必要的,但太专业或充满技巧的代码则是各种 bug的发源地,同时也是若干个不眠之夜的前奏”
第一章 总结
78页的知识量还是比较大的,我们学会了MYSQL中最最基础的知识:创建数据库和数据表;对数据表里面的记录进行插入,检索,修改,删除等操作,并且对select语句的用法作了一些相对复杂的讲解,不知道各位好学的朋友有何感想。 我知道对一个初学者来说,一本好书,一个好老师,一个好朋友,都是很关键的。“吾生而有涯,而知也无涯,以有涯逐无涯,殆矣!”庄子的话又在我的耳边响起。让我们互相鼓励一下吧!
|