TNS12560的一次解决数据库教程

轻松自在开心柴 分享 时间: 收藏本文

【简介】感谢网友“轻松自在开心柴”参与投稿,下面小编给大家整理了TNS12560的一次解决数据库教程(共9篇),供大家阅读参考。

篇1:TNS12560的一次解决数据库教程

解决

TNS-12560的一次解决

昨天我们公司客户服务部的一个后台数据库出现TNS-12560问题,他们的WEB维护人员搞了两天没搞定,后来打电话要我去帮他搞.刚开始以为服务器就放在我们公司,我就答应他帮他搞定,后来等我去他们部门的时候他才跟我说服务器是托管在电信的IDC中心.想着不去了的,可是看他挺可怜的(他们经理天天吹,搞不定就辞掉他).等我到了电信的IDC中心后,我后悔了,TMD,里面的服务多的数不清,可见那种辐射有多强,我就在里面呆了一个多小时,出来了以后我头狂晕,受不了!

回归正题,让我们看看TNS-12560错误这次的解决.这个错误相信经常有人碰到,一般也都知道怎么回事.只是如果出现这个错误而之前先由一个不懂的人看了论坛里或者查的一些资料乱改一些东东以后又在自己去看的时候没有注意到他改的所有的东东,这下有得受了(辐射太强).

OS:WINDOWS ADS.DB:ORACLE 8.1.7

首先我看的是服务启动没有.-------已启动

再看listener.ora的配置.-----有问题,没有配置监听数据库.在这里出现问题我就用NET8重新配置了一下listener.先删除原来的LISTENER.然后重新创建一个.记得配置数据库服务这一块.重新启动服务,可以启动.

CONN SYS/ORACLE AS SYSDBA.--------还是出现错误.跟着查看tnsnames.ora.没有发现问题.我又试着用conn sys/oracle@oracle as sysdba.不行,权限不足.再用conn system/oracle@oracle 还是不行,有错误.(没有记下错误号),然后接着查看sqlnet.ora.有问题,里面配置了一个参数ONAME(记不清).我将这一行注释掉.并修改了一下NAMES.DIRECTORY_PATH的顺序TNSNAMES, HOSTNAME, ONAMES.然后再用conn sys/oracle@oracle as sysdba.还是不行错误的用户名(那个人改了密码事先没说).再用conn system/oracle@oracle as sysdba.OK.我就奇怪了,怎么用system可以,而用sys不行呢?想到了可能是用密码文件认证方式了.将remote_login_passwordfile=EXCLUSIVE.然后重建密码文件.orapwd file=pwdoracle.ora password=oracle entries=10.然后再conn sys/oracle@oracle as sysdba.OK.可以进.

再试客户端.conn system/oracle@oracle as sysdba.不行.配置sqlnet.ora和tnsnames.ora.修改成跟服务器端一样.OK.可以进.

说明一点,上面的再配置listener.ora,tnsnames.ora,sqlnet.ora文件之后最好重启一下服务.

篇2:浅谈DataSet数据库教程

DataSet是ADO.NET开发人员为方便数据处理开发出来的,是数据的集合,是为解决DataReader的缺陷设计的,DataReader数据处理速度快,但它是只读的, 而且一旦移到下一行,就不能查看上一行的数据,DataSet则可以自由移动指针,DataSet的数据是与数据库断开的。DataSet还可用于多层应用程序中,如果应用程序运行在中间层的业务对象中来访问数据库,则业务对象需将脱机数据结构传递给客户应用程序。

DataSet的功能:浏览、排序、搜索、过滤、处理分级数据、缓存更改等。还可以与XML数据互换。DataSet中可包括多个DataTable,可将多个查询结构存到一个DataSet中,方便操作,而DataTable中又包括多个DataRow、DataColumn,可通过这些DataRow、DataColumn来查看、操作其中的数据,而需将操作结果返回给数据库的话,则可以调用DataAdapter的Update方法。

DataSet的操作:

DataSet ds=new DataSet;

DataTable dt=new DataTable(“newTable”);

ds.Tables.Add(dt);DataSet ds=new DataSet();

DataTable dt=ds.Tables.Add(“newTable”);

上述两种方法都可以在DataSet中添加一个DataTable,看需要而进行选择。添加DataTable后,需向其中添加行和列。

DataSet ds=new DataSet();

DataTable dt=ds.Tables.Add(“newTables”);

DataColumn col=dt.Columns.Add(“newColumn”,typeof(int));

col.AllowDBNull=false;

col.MaxLength=4;

col.Unique=true;

上述代码向DataSet中的DataTable中添加名为”newColumn”,类型为int且不为空,最大长度为4和唯一性为真的列。

dt.PrimaryKey=new DataColumn[]{dt.Columns[“ID”]}

这段代码是继续上面代码的,为一个DataTable中添加一个主键列,主键列是一个数据组,如有多个主键,只需在数组中添加一个列即可。如下:

dt.PrimaryKey=new DataColumns[]{dt.Columns[“OrderID”],dt.Columns[“ProductID”]}

添加外键:

ForeignKeyConstraint fk;

fk=new ForeignKeyConstraint(ds.Tables[“Customers”].Columns[“CustomerID”],ds.Tables[“Orders”].Columns[“CustomerID”]);

ds.Tables[“Orders”].Constraints.Add(fk);

//上述代码假如已经为Cusomers表和Orders创建了主键,此句为添加外键约束,

上述是根据Customers表和Orders表的CustomerID来创建约束。

下面介绍修改DataRow中的内容:

DataRow dr=ds.Tables[“Customer”].Rows.Find(“ANTON”);

if(dr==null)

else

{

dr.BeginEdit();

dr[“CompanyName”]=“newValue”;

dr[“ContactName”]=“newValue2”;

dr.EndEdit();

}

//上面代码通过Row集合的Find方法来在DataTable中的行进行定位,找到“ANTON”行,再修改“ANTON”行中CompanyName列和ContactName列的值。通过BeginEdit和EndEdit来缓存对行的修改,还可调用 CancelEdit为取消修改。

判断某列是否为空值:

DataRow dr=ds.Tables[“Customers”].Rows.Find(“aaa”);

if(dr.IsNull(“ContactName”);

..

else

dr[“ContactName”]=DBNull.Value

//这里判断ContactName列是否为空,如果不是则为其赋空值,呵,很无厘头的做法,这里只为演示为列赋空值的做法。

删除DataRow:

有两种方法可以删除DataRow,Delete方法和Remove方法和RemoveAt方法。其区别是Delete方法实际上不是从DataTable中删除掉一行,而是将其标志为删除,仅仅是做个记号,而Remove方法则是真正的从DataRow中删除一行,RemoveAt方法是根本行的索引来删除。列:

DataRow dr=ds.Tables[“table”].Rows.Find(“a”);

ds.Tables[“table”].Remove(dr);

ds.Tables[“table”].Remove(index);

//dr 为“a”所在的行,查出后将其删除,index为 “a”所在的索引号。关于DataSet中的其用法,参照MSDN

篇3:ChangeAllObjectOwner数据库教程

object

EXEC ChangeAllObjOwner @oldowner = 'John', @newowner = 'Alex'

/*

Version: SQL Server 7.0/

Created by: Alexander Chigrik

www.MSSQLCity.com/ - all about MS SQL

(SQL Server Articles, FAQ, Scripts, Tips and Test Exams).

This stored procedure can be used to run through all of a specific

database's objects owned by the 'oldowner' and change the old

owner with the new one.

You should pass the old owner name and the new owner name,

as in the example below:

EXEC ChangeAllObjOwner @oldowner = 'John', @newowner = 'Alex'

*/

IF OBJECT_ID('ChangeAllObjOwner') IS NOT NULL //line continous

DROP PROC ChangeAllObjOwner

GO

CREATE PROCEDURE ChangeAllObjOwner (

@oldowner sysname,

@newowner sysname

)

AS

DECLARE @objname sysname

SET NOCOUNT ON

--check that the @oldowner exists in the database

IF USER_ID(@oldowner) IS NULL

BEGIN

RAISERROR ('The @oldowner passed does not exist in the database',

16, 1)

RETURN

END

--check that the @newowner exists in the database

IF USER_ID(@newowner) IS NULL

BEGIN

RAISERROR ('The @newowner passed does not exist in the database',

16, 1)

RETURN

END

DECLARE owner_cursor CURSOR FOR

SELECT name FROM sysobjects WHERE uid = USER_ID(@oldowner)

OPEN owner_cursor

FETCH NEXT FROM owner_cursor INTO @objname

WHILE (@@fetch_status -1)

BEGIN

SET @objname = @oldowner + '.' + @objname

EXEC sp_changeobjectowner @objname, @newowner

FETCH NEXT FROM owner_cursor INTO @objname

END

CLOSE owner_cursor

DEALLOCATE owner_cursor

GO

篇4:sql数据库教程

掌握SQL四条最基本的数据操作语句:Insert,Select,Update和Delete,

练掌握SQL是数据库用户的宝贵财 富。在本文中,我们将引导你掌握四条最基本的数据操作语句―SQL的核心功能―来依次介绍比较操作符、选择断言以及三值逻辑。当你完成这些学习后,显然你已经开始算是精通SQL了。

在我们开始之前,先使用CREATE TABLE语句来创建一个表(如图1所示)。DDL语句对数据库对象如表、列和视进行定义。它们并不对表中的行进行处理,这是因为DDL语句并不处理数据库中实际的数据。这些工作由另一类SQL语句―数据操作语言(DML)语句进行处理。

SQL中有四种基本的DML操作:INSERT,SELECT,UPDATE和DELETE。由于这是大多数SQL用户经常用到的,我们有必要在此对它们进行一一说明。在图1中我们给出了一个名为EMPLOYEES的表。其中的每一行对应一个特定的雇员记录。请熟悉这张表,我们在后面的例子中将要用到它。

连接查询

通过连接运算符可以实现多个表查询。连接是关系数据库模型的主要特点,也是它区别于其它类型

数据库管理系统的一个标志。

在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在

一个表中。当检索数据时,通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带

来很大的灵活性,他们可以在任何时候增加新的数据类型。为不同实体创建新的表,尔后通过连接进行

查询。

连接可以在SELECT 语句的FROM子句或WHERE子句中建立,似是而非在FROM子句中指出连接时有助于

将连接操作与WHERE子句中的搜索条件区分开来。所以,在Transact-SQL中推荐使用这种方法。

SQL-92标准所定义的FROM子句的连接语法格式为:

FROM join_table join_type join_table

[ON (join_condition)]

其中join_table指出参与连接操作的表名,连接可以对同一个表操作,也可以对多表操作,对同一

个表操作的连接又称做自连接。

join_type 指出连接类型,可分为三种:内连接、外连接和交叉连接。内连接(INNER JOIN)使用比

较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行。根据所使用

的比较方式不同,内连接又分为等值连接、自然连接和不等连接三种。

外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN)、右外连接(RIGHT OUTER JOIN或RIGHT JOIN)

和全外连接(FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是,外连接不只列出与连接条件相匹

配的行,而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的

数据行。

交叉连接(CROSS JOIN)没有WHERE 子句,它返回连接表中所有数据行的笛卡尔积,其结果集合中的

数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。

连接操作中的ON (join_condition) 子句指出连接条件,它由被连接表中的列和比较运算符、逻辑

运算符等构成。

无论哪种连接都不能对text、ntext和image数据类型列进行直接连接,但可以对这三种列进行间接

连接。例如:

SELECT p1.pub_id,p2.pub_id,p1.pr_info

FROM pub_info AS p1 INNER JOIN pub_info AS p2

ON DATALENGTH(p1.pr_info)=DATALENGTH(p2.pr_info)

(一)内连接

内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的列值。内连接分

三种:

1、等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询结果中列出被连接

表中的所有列,包括其中的重复列。

2、不等连接: 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。这些

运算符包括>、>=、<=、<、!>、!<和>。

3、自然连接:在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选择列表指出查询

结果集合中所包括的列,并删除连接表中的重复列。

例,下面使用等值连接列出authors和publishers表中位于同一城市的作者和出版社:

SELECT *

FROM authors AS a INNER JOIN publishers AS p

ON a.city=p.city

又如使用自然连接,在选择列表中删除authors 和publishers 表中重复列(city和state):

SELECT a.*,p.pub_id,p.pub_name,p.country

FROM authors AS a INNER JOIN publishers AS p

ON a.city=p.city

(二)外连接

内连接时,返回查询结果集合中的仅是符合查询条件( WHERE 搜索条件或 HAVING 条件)和连接条件

的行。而采用外连接时,它返回到查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外

连接时)、右表(右外连接时)或两个边接表(全外连接)中的所有数据行。

如下面使用左外连接将论坛内容和作者信息连接起来:

SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b

ON a.username=b.username

下面使用全外连接将city表中的所有作者以及user表中的所有作者,以及他们所在的城市:

SELECT a.*,b.*

FROM city as a FULL OUTER JOIN user as b

ON a.username=b.username

(三)交叉连接

交叉连接不带WHERE 子句,它返回被连接的两个表所有数据行的笛卡尔积,返回到结果集合中的数

据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。

例,titles表中有6类图书,而publishers表中有8家出版社,则下列交叉连接检索到的记录数将等

于6*8=48行。

SELECT type,pub_name

FROM titles CROSS JOIN publishers

ORDER BY type

UNION运算符可以将两个或两个以上上SELECT语句的查询结果集合合并成一个结果集合显示,即执行联

合查询。UNION的语法格式为:

select_statement

UNION [ALL] selectstatement

[UNION [ALL] selectstatement][…n]

其中selectstatement为待联合的SELECT查询语句。

ALL选项表示将所有行合并到结果集合中。不指定该项时,被联合查询结果集合中的重复行将只保留一

行。

联合查询时,查询结果的列标题为第一个查询语句的列标题。因此,要定义列标题必须在第一个查询语

句中定义。要对联合查询结果排序时,也必须使用第一查询语句中的列名、列标题或者列序号。

在使用UNION 运算符时,应保证每个联合查询语句的选择列表中有相同数量的表达式,并且每个查询选

择表达式应具有相同的数据类型,或是可以自动将它们转换为相同的数据类型。在自动转换时,对于数值类

型,系统将低精度的数据类型转换为高精度的数据类型。

在包括多个查询的UNION语句中,其执行顺序是自左至右,使用括号可以改变这一执行顺序。例如:

查询1 UNION (查询2 UNION 查询3)

INSERT语句

用户可以用INSERT语句将一行记录插入到指定的一个表中。例如,要将雇员John Smith的记录插入到本例的表中,可以使用如下语句:

INSERT INTO EMPLOYEES VALUES

('Smith','John','1980-06-10',

'Los Angles',16,45000);

通过这样的INSERT语句,系统将试着将这些值填入到相应的列中。这些列按照我们创建表时定义的顺序排列。在本例中,第一个值“Smith”将填到第一个列LAST_NAME中;第二个值“John”将填到第二列FIRST_NAME中……以此类推。

我们说过系统会“试着”将值填入,除了执行规则之外它还要进行类型检查。如果类型不符(如将一个字符串填入到类型为数字的列中),系统将拒绝这一次操作并返回一个错误信息。

如果SQL拒绝了你所填入的一列值,语句中其他各列的值也不会填入。这是因为SQL提供对事务的支持。一次事务将数据库从一种一致性转移到另一种一致性。如果事务的某一部分失败,则整个事务都会失败,系统将会被恢复(或称之为回退)到此事务之前的状态。

回到原来的INSERT的例子,请注意所有的整形十进制数都不需要用单引号引起来,而字符串和日期类型的值都要用单引号来区别。为了增加可读性而在数字间插入逗号将会引起错误。记住,在SQL中逗号是元素的分隔符。

同样要注意输入文字值时要使用单引号。双引号用来封装限界标识符。

对于日期类型,我们必须使用SQL标准日期格式(yyyy-mm-dd),但是在系统中可以进行定义,以接受其他的格式。当然,临近,请你最好还是使用四位来表示年份。

既然你已经理解了INSERT语句是怎样工作的了,让我们转到EMPLOYEES表中的其他部分:

INSERT INTO EMPLOYEES VALUES

('Bunyan','Paul','1970-07-04',

'Boston',12,70000);

INSERT INTO EMPLOYEES VALUES

('John','Adams','1992-01-21',

'Boston',20,100000);

INSERT INTO EMPLOYEES VALUES

('Smith','Pocahontas','1976-04-06',

'Los Angles',12,100000);

INSERT INTO EMPLOYEES VALUES

('Smith','Bessie','1940-05-02',

'Boston',5,200000);

INSERT INTO EMPLOYEES VALUES

('Jones','Davy','1970-10-10',

'Boston',8,45000);

INSERT INTO EMPLOYEES VALUES

('Jones','Indiana','1992-02-01',

'Chicago',NULL,NULL);

在最后一项中,我们不知道Jones先生的工薪级别和年薪,所以我们输入NULL(不要引号),

NULL是SQL中的一种特殊情况,我们以后将进行详细的讨论。现在我们只需认为NULL表示一种未知的值。

有时,像我们刚才所讨论的情况,我们可能希望对某一些而不是全部的列进行赋值。除了对要省略的列输入NULL外,还可以采用另外一种INSERT语句,如下:

INSERT INTO EMPLOYEES(

FIRST_NAME, LAST_NAME,

HIRE_DATE, BRANCH_OFFICE)

VALUE(

'Indiana','Jones',

'1992-02-01','Indianapolis');

这样,我们先在表名之后列出一系列列名。未列出的列中将自动填入缺省值,如果没有设置缺省值则填入NULL。请注意我们改变了列的顺序,而值的顺序要对应新的列的顺序。如果该语句中省略了FIRST_NAME和LAST_NAME项(这两项规定不能为空),SQL操作将失败。

让我们来看一看上述INSERT语句的语法图:

INSERT INTO table

[(column { ,column})]

VALUES

(columnvalue [{,columnvalue}]);

和前一篇文章中一样,我们用方括号来表示可选项,大括号表示可以重复任意次数的项(不能在实际的SQL语句中使用这些特殊字符)。VALUE子句和可选的列名列表中必须使用圆括号。

SELECT语句

SELECT语句可以从一个或多个表中选取特定的行和列。因为查询和检索数据是数据库管理中最重要的功能,所以SELECT语句在SQL中是工作量最大的部分。实际上,仅仅是访问数据库来分析数据并生成报表的人可以对其他SQL语句一窍不通。

SELECT语句的结果通常是生成另外一个表。在执行过程中系统根据用户的标准从数据库中选出匹配的行和列,并将结果放到临时的表中。在直接SQL(direct SQL)中,它将结果显示在终端的显示屏上,或者将结果送到打印机或文件中。也可以结合其他SQL语句来将结果放到一个已知名称的表中。

SELECT语句功能强大。虽然表面上看来它只用来完成本文第一部分中提到的关系代数运算“选择”(或称“限制”),但实际上它也可以完成其他两种关系运算―“投影”和“连接”,SELECT语句还可以完成聚合计算并对数据进行排序。

SELECT语句最简单的语法如下:

SELECT columns FROM tables;

当我们以这种形式执行一条SELECT语句时,系统返回由所选择的列以及用户选择的表中所有指定的行组成的一个结果表。这就是实现关系投影运算的一个形式。

让我们看一下使用图1中EMPLOYEES表的一些例子(这个表是我们以后所有SELECT语句实例都要使用的。而我们在图2和图3中给出了查询的实际结果。我们将在其他的例子中使用这些结果)。

假设你想查看雇员工作部门的列表。那下面就是你所需要编写的SQL查询:

SELECT BRANCH_OFFICE FROM EMPLOYEES;

以上SELECT语句的执行将产生如图2中表2所示的结果。

由于我们在SELECT语句中只指定了一个列,所以我们的结果表中也只有一个列。注意结果表中具有重复的行,这是因为有多个雇员在同一部门工作(记住SQL从所选的所有行中将值返回)。要消除结果中的重复行,只要在SELECT语句中加上DISTINCT子句:

SELECT DISTINCT BRANCH_OFFICE

FROM EMPLOYEES;

这次查询的结果如表3所示。

现在已经消除了重复的行,但结果并不是按照顺序排列的。如果你希望以字母表顺序将结果列出又该怎么做呢?只要使用ORDER BY子句就可以按照升序或降序来排列结果:

SELECT DISTINCT BRANCH_OFFICE

FROM EMPLOYEES

ORDER BY BRANCH_OFFICE ASC;

这一查询的结果如表4所示。请注意在ORDER BY之后是如何放置列名BRANCH _OFFICE的,这就是我们想要对其进行排序的列。为什么即使是结果表中只有一个列时我们也必须指出列名呢?这是因为我们还能够按照表中其他列进行排序,即使它们并不显示出来。列名BRANCH_ OFFICE之后的关键字ASC表示按照升序排列。如果你希望以降序排列,那么可以用关键字DESC。

同样我们应该指出ORDER BY子句只将临时表中的结果进行排序;并不影响原来的表。

假设我们希望得到按部门排序并从工资最高的雇员到工资最低的雇员排列的列表。除了工资括号中的内容,我们还希望看到按照聘用时间从最近聘用的雇员开始列出的列表。以下是你将要用到的语句:

SELECT BRANCH_OFFICE,FIRST_NAME,

LAST_NAME,SALARY,HIRE_DATE

FROM EMPLOYEES

ORDER BY SALARY DESC,

HIRE_DATE DESC;

这里我们进行了多列的选择和排序。排序的优先级由语句中的列名顺序所决定。SQL将先对列出的第一个列进行排序。如果在第一个列中出现了重复的行时,这些行将被按照第二列进行排序,如果在第二列中又出现了重复的行时,这些行又将被按照第三列进行排序……如此类推。这次查询的结果如表5所示。

将一个很长的表中的所有列名写出来是一件相当麻烦的事,所以SQL允许在选择表中所有的列时使用*号:

SELECT * FROM EMPLOYEES;

这次查询返回整个EMPLOYEES表,如表1所示。

下面我们对开始时给出的SELECT语句的语法进行一下更新(竖直线表示一个可选项,允许在其中选择一项。):

SELECT [DISTINCT]

(column [{, columns}])| *

FROM table [ {, table}]

[ORDER BY column [ASC] | DESC

[ {, column [ASC] | DESC }]];

定义选择标准

在我们目前所介绍的SELECT语句中,我们对结果表中的列作出了选择但返回的是表中所有的行。让我们看一下如何对SELECT语句进行限制使得它只返回希望得到的行:

SELECT columns FROM tables [WHERE predicates];

WHERE子句对条件进行了设置,只有满足条件的行才被包括到结果表中。这些条件由断言(predicate)进行指定(断言指出了关于某件事情的一种可能的事实)。如果该断言对于某个给定的行成立,该行将被包括到结果表中,否则该行被忽略。在SQL语句中断言通常通过比较来表示。例如,假如你需要查询所有姓为Jones的职员,则可以使用以下SELECT语句:

SELECT * FROM EMPLOYEES

WHERE LAST_NAME = 'Jones';

LAST_NAME = 'Jones'部分就是断言。在执行该语句时,SQL将每一行的LAST_NAME列与“Jones”进行比较。如果某一职员的姓为“Jones”,即断言成立,该职员的信息将被包括到结果表中(见表6)。

使用最多的六种比较

我们上例中的断言包括一种基于“等值”的比较(LAST_NAME = 'Jones'),但是SQL断言还可以包含其他几种类型的比较。其中最常用的为:

等于 =

不等于

小于 <

大于 >

小于或等于 <=

大于或等于 >=

下面给出了不是基于等值比较的一个例子:

SELECT * FROM EMPLOYEES

WHERE SALARY >50000;

这一查询将返回年薪高于$50,000.00的职员(参见表7)。

逻辑连接符

有时我们需要定义一条不止一种断言的SELECT语句。举例来说,如果你仅仅想查看Davy Jones的信息的话,表6中的结果将是不正确的。为了进一步定义一个WHERE子句,用户可以使用逻辑连接符AND,OR和NOT。为了只得到职员Davy Jones的记录,用户可以输入如下语句:

SELECT * FROM EMPLOYEES

WHERE LAST_NAME = 'Jones' AND FIRST_NAME = 'Davy';

在本例中,我们通过逻辑连接符AND将两个断言连接起来。只有两个断言都满足时整个表达式才会满足。如果用户需要定义一个SELECT语句来使得当其中任何一项成立就满足条件时,可以使用OR连接符:

SELECT * FROM EMPLOYEES

WHERE LAST_NAME = 'Jones' OR LAST_NAME = 'Smith';

有时定义一个断言的最好方法是通过相反的描述来说明。如果你想要查看除了Boston办事处的职员以外的其他所有职员的信息时,你可以进行如下的查询:

SELECT * FROM EMPLOYEES

WHERE NOT(BRANCH_OFFICE = 'Boston');

关键字NOT后面跟着用圆括号括起来的比较表达式。其结果是对结果取否定。如果某一职员所在部门的办事处在Boston,括号内的表达式返回true,但是NOT操作符将该值取反,所以该行将不被选中。

断言可以与其他的断言嵌套使用。为了保证它们以正确的顺序进行求值,可以用括号将它们括起来:

SELECT * FROM EMPLOYEES

WHERE (LAST_NAME = 'Jones'

AND FIRST_NAME = 'Indiana')

OR (LAST_NAME = 'Smith'

AND FIRST_NAME = 'Bessie');

SQL沿用数学上标准的表达式求值的约定―圆括号内的表达式将最先进行求值,其他表达式将从左到右进行求值。

篇5:MySQL教程数据库教程

现在我们已经具备了所需的所有基础知识;可以将MySQL投入工作了!本节提供一个教程,帮助熟悉MySQL,在完成这个教程时,将创建一个样例数据库和这个数据库中的表,然后增加、检索、删除和修改信息与数据库进行交互。此外,在操作这个样例数据库的过程中,将能学到下列东西:

■ 如何利用mysql客户机程序与MySQL通信。

■ SQL 语言的基本语句。(如果您曾经使用过其他RDBMS,从而熟悉SQL,那么浏览一下这个教程,看看SQL 的MySQL版与您熟悉的版本有何差别也是很好的。)正如上一节所述, MySQL采用客户机/服务器体系结构,其中服务器运行在存放数据库的机器上,而客户机通过网络连接到服务器。这个教程主要基于mysql客户机的应用。mysql读取您的SQL 查询,将它们发送给服务器,并显示结果。mysql运行在MySQL所支持的所有平台上,并提供与服务器交互的最直接的手段,因此,它首先是一个逻辑上的客户机。

在本书中,我们将用samp_db 作为样例数据库的名称。但是有可能在您完成本例子的过程中需要使用另一个数据库名。因为可能在您的系统上已经有某个人使用了samp_db 这个名称,或者管理员给您指定了另一个数据库名称。在后面的例子中,无论是哪种情况,都用数

据库的实际名称代替samp_db。表名可以像例子所显示的那样精确地使用,即使系统中的多个人都具有他们自己的样例数据库也是如此。顺便说一下,在MySQL中,如果有人使用了相同的表名也没什么关系。一旦各个用户都具有自己的数据库, MySQL将一直保留这些数据库名,防止各用户互相干扰。

1.4.1基本要求

为了试验这个教程中的例子,必须安装MySQL。特别是必须具有对MySQL客户机和某个MySQL服务器的访问权。相应的客户机程序必须位于您的机器上。至少需要有mysql程序,最好还有mysqlimport程序。服务器也可以位于您的机器上,尽管这不是必须的。实际上,只要允许连接到服务器,那么服务器位于何位置都没有关系。若服务器正巧运行在您的机器上,适当的客户机程序又已经安装,那么就可以开始试验了。如果您尚需设法搞到MySQL,可参阅附录A“获得和安装软件”的说明。如果您正自己安装MySQL,可参阅这一章,或把它给管理员看。如果网络访问是通过一个因特网服务商(ISP)进行的,那么可查看该服务商是否拥有MySQL。如果该ISP 不提供MySQL服务,可查看附录J“因特网服务商”以得到某些选择更适合的服务商的建议。

除MySQL软件外,还需要得到创建样例数据库及其表的权限。如果您没有这种权限,可以向MySQL管理员咨询。管理员可通过运行mysql并发布如下的命令提供这种权限:

MySQL与mysql的区别

为了避免混淆,应该说明,“MySQL”指的是整个MySQLRDBMS,而“mysql”代表的是一个特定的客户机程序名。它们的发音都是相同的,但可通过不同的大小写字符和字体来区分。关于发音,MySQL的发音为“my-ess-queue-ell”。我们知道这是因为MySQL参考指南中是这样发音的。而SQL 的发音为“sequel”或“ess-queue-ell”。我不认为哪个发音更好一些。愿意读哪个音都可以,不过在您对别人读的时候,他可能会用他认为是“正确”的发音对您进行纠正。

前一个命令在paul 从localhost(服务器运行在正运行的同一主机)连接时,允许它完全访问samp_db 数据库及它的所有表。它还给出了一个口令secret。第二个命令与第一个类似,但允许paul 从任何主机上连接(“%”为通配符)。也可以用特定的主机名取代“%”,使paul只能从该主机上进行连接。(如果您的服务器允许从localhost 匿名访问,由于服务器搜索授权表查找输入连接匹配的方式的原因,这样一个GRANT 语句可能是必须的。)关于GRANT语句以及设置MySQL用户账号的更详细信息,可在第11章“常规的MySQL管理”找到。

1.4.2 取得样例数据库的分发包

这个教程在某些地方要涉及来自“样例数据库分发包”中的文件。有的文件含有帮助来设置样例数据库的查询或数据。为了得到这个分发包,可参阅附录A。在打开这个分发包时,将创建一个名为samp_db 的目录,此目录中含有所需的文件。无论您在哪个地方试验与样例数据库有关的例子,建议都移入该目录。

1.4.3 建立和中止与服务器的连接

为了连接到服务器,从外壳程序(即从UNIX 提示符,或从Windows 下的DOS 控制台)激活mysql程序。命令如下:

其中的“%”在本书中代表外壳程序提示符。这是UNIX 标准提示符之一;另一个为“$”。在Windows 下,提示符类似“c:\\>”。

mysql命令行的options 部分可能是空的,但更可能的是发布一条类似如下的命令:

在激活mysql时,有可能不必提供所有这些选项;确切使用的命令请咨询MySQL管理员。此外,可能还需要至少指定一个名称和一个口令。

在刚开始学习MySQL时,大概会为其安全系统而烦恼,因为它使您难于做自己想做的事。(您必须取得创建和访问数据库的权限,任何时候连接到数据库都必须给出自己的名字和口令。)但是,在您通过数据库录入和使用自己的记录后,看法就会马上改变了。这时您会很欣赏MySQL阻止了其他人窥视(或者更恶劣一些,破坏!)您的资料。

下面介绍选项的含义:

■ -hhost_name(可选择形式:--host=host_name)

希望连接的服务器主机。如果此服务器运行在与mysql相同的机器上,这个选项一般可

省略。

■ -uuser_name(可选择的形式:--uuser=user_name)您的MySQL用户名。如果使用UNIX 且您的MySQL用户名与注册名相同,则可以省去这个选项;mysql将使用您的注册名作为您的MySQL名。在Windows 下,缺省的用户名为ODBC。这可能不一定非常有用。可在命令行上指定一个名字,也可以通过设置USER 变量在环境变量中设置一个缺省名。如用下列set 命令指定paul 的一个用户名:

■ - p(可选择的形式:--password)

这个选项告诉mysql提示键入您的MySQL口令。注意:可用-pyour_password 的形式(可选择的形式: --password =your_password)在命令行上键入您的口令。但是,出于安全的考虑,最好不要这样做。选择-p 不跟口令告诉mysql在启动时提示您键入口令。例如:

在看到Enter password: 时,键入口令即可。(口令不会显到屏幕,以免给别人看到。)请注意,MySQL口令不一定必须与UNIX 或Windows 口令相同。如果完全省略了-p 选项,mysql就认为您不需要口令,不作提示。请注意: -h 和-u选项与跟在它们后面的词有关,无论选项和后跟的词之间是否有空格。而-p 却不是这样,如果在命令行上给出口令, -p 和口令之间一定不加空格。例如,假定我的MySQL用户名和口令分别为paul 和secret,希望连接到在我注册的同一机器上运行的服务器上。下面的mysql命令能完成这项工作:

在我键入命令后, mysql显示Enter password: 提示键入口令。然后我键入口令( * * * * * *表明我键入了secret)。如果一切顺利的话, mysql显示一串消息和一个“ mysql>”提示,表示它正等待我发布查询。完整的启动序列如下所示:

为了连接到在其他某个机器上运行的服务器,需要用-h 指定主机名。如果该主机为pit-viper.snake.net,则相应的命令如下所示:

在后面的说明mysql命令行的多数例子中,为简单起见,我们打算省去- h、-u和-p 选项。并且假定您将会提供任何所需的选项。

有很多设置账号的方法,从而不必在每次运行mysql时都在连接参数中进行键入。这个问题在1. 5节“与mysql交互的技巧”中介绍。您可能会希望现在就跳到该节,以便找到一些更易于连接到服务器的办法。

在建立了服务器的一个连接后,可在任何时候键入下列命令来结束会话:

还可以键入Control-D 来退出,至少在UNIX 上可以这样。

1.4.4 发布查询

在连接到服务器后,就可以发布查询了。本节介绍有关与mysql交互应该了解的一些知识。为了在mysql中输入一个查询,只需键入它即可。在查询的结尾处,键入一个分号(“;”)并按Enter 键。分号告诉mysql该查询是完整的。(如果您喜欢键入两个字符的话,也可以使用“\\ g”终止查询。)在键入一个查询之后, mysql将其发送到服务器上。该服务器处理此查询并将结果送回mysql,mysql将此结果显示出来。下面是一个简单的查询例子和结果:

它给出当前的日期和时间。(NOW 函数本身并无多大用处,但可将其用于表达式中。如比较当前日期和其他日期的差异。)

mysql还在结果中显示行数计数。本书在例子中一般不给出这个计数。因为mysql需要见到分号才发送查询到服务器,所以在单一的行上不需要键入分号。如果有必要,可将一个查询分为几行,如下所示:

请注意,在键入查询的第一行后,提示符从‘mysql’ 变成了‘- >’;这表示mysql允许继续键入这个查询。这是一个重要的提示,因为如果在查询的末尾忘记了分号,此提示将有助于提醒您查询尚不完整。否则您会一直等下去,心里纳闷为什么mysql执行查询为什么这么长的时间还没完;而mysql也搞不清为什么结束查询的键入要花您那么多的时间!

大部分情况下,用大写字符、小写字符或大小写字符混合键入查询没什么关系。下列查询全是等价的:

本书中的例子用大写字符表示SQL 关键字和函数名,用小写字符表示数据库、表和列名。

如果在查询中调用一个函数,在函数名和后跟的圆括号中间不允许有空格,例:

这两个查询看上去差别不大,但第二个失败了,因为圆括号并没有紧跟在函数名的后面。如果已经开始键入一个多行的查询,而又不想立即执行它,可键入‘\\ c’ 来跳过(放弃)它,如:

请注意,提示符又变回了‘mysql>’,这表示mysql为键入的新查询作好了准备。可将查询存储在一个文件中并告诉mysql从文件中读取查询而不是等待键盘输入。可利用外壳程序键入重定向实用程序来完成这项工作。例如,如果在文件my_file.sql 中存放有查

询,可如下执行这些查询:

可用这种办法调用任何所需的文件。这里用后缀为“.sql”来表示该文件含有SQL 语句。执行mysql的这种方法将在输入数据到samp_db 数据库时的“增加新记录”中使用。为了装载一个表,让mysql从某个文件中读取INSERT 语句比每次用手工键入这些语句更为方便。

本教程的其余部分向您提供了许多可以自己试试的查询。这些查询以‘mysql>’ 提示为前导后跟结束分号,这些例子通常都给出了查询输出结果。可以按给出的形式键入这些查询,所得到的结果应该与自学材料中的相同。给出的查询中无提示符的或无分号语句结束符的只是用来说明某个要点,不用执行它们。(如果愿意您可以试一下,但如果试的话,请记住给语句末尾加一个分号。)本书后面的章节中,我们一般不给出‘mysql>’ 提示或SQL 语句的分号。这样做的原因是为了可以在非mysql客户机程序的语言环境(如在Perl 脚本中或PHP 脚本中)中发布查询,在这些语言环境中,既无提示符也不需要分号。在专门针对mysql输入一个查询的场合会作出相应的说明。

1.4.5 创建数据库

现在开始创建samp_db 样例数据库及其表,填充这些表并对包含在这些表中的数据进行一些简单的查询。

使用数据库涉及几个步骤:

1) 创建(初始化)数据库。

2) 创建数据库中的表。

3) 对表进行数据插入、检索、修改或删除。

检索现有数据是对数据库执行的最简单且常见的操作。另外几个最简单且常见的操作是插入新数据、更新或删除现有数据。较少使用的操作是创建表的操作,而最不常用的操作是创建数据库。

我们将从头开始,先创建数据库,再插入数据,然后对数据进行检索。为了创建一个新的数据库,用mysql连接到数据库然后发布CREATE DATABASE 语句,此语句指定了数据库名:

在创建表以及对这些表进行各种操作之前,必须先创建samp_db 数据库。创建数据库后,这个新创建的数据库并不是当前数据库。这可从执行下面的查询看出:

为了使samp_db 成为当前数据库,发布USE 语句即可:

USE 为少数几个不需要终结符的语句之一,当然,加上终结符也不会出错。HELP 是另一个不需要终结符的语句。如果想了解不需要终结符的语句有哪些,可发布HELP 语句。在发布了USE 语句后,samp_db 成为缺省数据库:

使数据库成为当前数据库的另一个方法是在激活mysql时在命令行上指定它,如下所示:

事实上,这是一个命名要使用的数据库的方法。如果需要连接参数可在数据库名前指定。例如,下列两个命令使我们能连接到在本地主机和p i t - v i per.snake.net 上的samp_db 数据库上:

除非另有指定,否则后面的例子都假定在激活mysql时,在命令行上给出samp_db 使其成为当前数据库。如果激活数据库时忘了在命令行上指定数据库,只需发布USE samp_db 语句即可。

1.4.6 创建表

本节中,我们将创建样例数据库samp_db 所需的表。我们首先考虑美国历史同盟需要的表。然后再考虑学分保存方案所需的表。在某些数据库的书籍中,在这里要大讲分析与设计、实体―关系图、标准程序以及诸如此类的东西。这里确实也可以讲这些东西,但是我宁可只讲点实用的东西,比方说,我们的数据库应该是怎样的:数据库中将包含什么内容,每个表中有哪些数据以及由决定如何表示数据而带来的一些问题。这里所作出的关于数据表示的选择并不是绝对的。在其他场合下,可能会选择不同的方式来表示类似的数据,这取决于应用的需要以及打算将数据派何用途。

1. 美国历史同盟所需的表

美国历史同盟的表设计相当简单:

■ 总统( p r e s i d e n t )表。此表含有描述每位总统的记录。同盟站点上的联机测验要使用这个表。

■ 会员( member )表。此表用来维护同盟每个会员的当前信息。这些信息将用来建立会员地址名录的书面和联机版本、发送会员资格更新提示等等。

(1) president表

president 表很简单,因此我们先讨论它。这个表将包含每位美国总统的一些基本信息:

■ 姓名。姓名在一个表中可用几种方式表示。如,可以用一个单一的列来存放完整的姓名,或者用分开的列来分别容纳名和姓。当然用单一的列更为简单,但是在使用上会带来一些限制,如:

■ 如果先输入只有名的姓名,则不可能对姓进行排序。

■ 如果先输入只有姓的姓名,就不可能对具有名的姓名进行显示。

■ 难以对姓名进行搜索。例如,如果要搜索某个特定的姓,则必须使用一个特定的模式,并且查找与这个模式匹配的姓名。这样较之只查找姓效率更低和更慢。member 表将使用单独的名和姓的列以避免这些限制。名列还存放中名(注:西方国家的姓名一般将名放在前,姓放在后,而且除了有名和姓外,有时还有中名,这是在位置上介于名和姓之间的中间名字)或首字母。这样应该不会削弱我们可能进行的任何一种排序,因为一般不可能对中名进行排序(或者甚至不会对名进行排序)。姓名即可以“ Bush, George W. ”格式显示,也可以“G e o rge W. B us h”格式显示。还有一种稍显复杂一点的情形。一个总统( Jimmy Carter)在其姓名的末尾处有一

个“ J r. ”,这时怎样做?根据名字打印的格式,这个总统的姓名显示为“ J a m e s E . C a r ter, J r.”或“C a r ter, James E., Jr.”,“J r.”与名和姓都没有关系,因此我们将建另外一个字段来存放姓名的后缀。这表明在试图确定怎样表示数据时,即使一个特殊的值也可能会带来问题。它也表明,为什么在将数据放入数据库前,尽量对数据值的类型进行了解是一个很好的想法。如果对数据了解不够,那么有可能在已经开始使用一个表后,不得不更改该表的结构。这不一定是个灾难,但通常应该避免。

■ 出生地(城市和州)。就像姓名一样,出生地也可以用单个列或多个列来表示。使用单列更为简单些,但正如姓名中的情形一样,独立的多个列使我们可以完成用单个列不方便完成的事情。例如,如果城市和州分别给出,查找各位总统出生在哪个州的记录就会更容易一些。

■ 出生日期和死亡日期。这里,唯一特殊的问题是我们不能要求都填上死亡日期,因为有的总统现在还健在。MySQL提供了一个特殊的值NULL,表示“无值”,可将其用在死亡日期列中以表示“仍然健在”。

(2) member 表

存储历史同盟会员清单的member 表在每个记录都包含单个人员的基本描述信息这一点上,类似于president 表。但是每个member 的记录所含的列更多,member 表的各列如下:

■ 姓名。使用如president 表一样的三个列来表示:姓、名(如果可能的话还有中名)、后缀。

■ ID 号。这是开始记录会员时赋给每个会员的唯一值。以前同盟未用ID 号,但现在的记录做得更有系统性,所以最好开始使用ID 号。(我希望您找到有利于使用MySQL并考虑到其他的将它用于历史同盟记录的方法。使用数字,将member 表中的记录与其他与会员有关的表中的记录相关联要更容易一些。)

■ 截止日期。会员必须定期更新他们的会员资格以免作废。对于某些应用,可能会用到最近更新的日期,但是近更新日期不适合于历史同盟。会员资格可在可变的年数内(一般为一年、二年、三年或五年)更新,而最近更新的日期将不能表示下一次更新必须在何时进行。此外,历史同盟还允许有终生会员。我们可以用未来一个很长的日期来表示终生会员,但是用NULL 似乎更为合适,因为“无值”在逻辑上对应于“永不终止”。

■ 电子邮件地址。对于有电子邮件地址的会员,这将使他们能很容易地进行相互之间的通信。作为历史同盟秘书,这使您能电子化地发送更新通知给会员,而用不着发邮政信函。这比到邮局发送信函更容易,而且也不贵。还可以用电子邮件给会员发送他们的地址名录条目的当前内容,并要求他们在有必要时更新信息。

■ 邮政地址。这是与没有电子邮件(或没有返回信息)的会员联络所需要的。将分别使用街道地址、城市、州和Zip 号。街道地址列又可以用于有诸如P.O. Box 123 而不是123 Elm St. 的会员的信箱号。我们假定所有同盟会员全都住在美国。当然,对于具有国际会员的机构,此假设过于简化了。如果希望处理多个国家的地址,还需要对不同国家的地址格式作一些工作。例如,这里的Zip 号就不是一个国际标准,有的国家有省而不是州。

■ 电话号码。与地址字段一样,这个列对于联络会员也是很有用的。

■ 特殊爱好的关键词。假定每个会员一般都对美国历史都有兴趣,但可能有的会员对某些领域有特殊的兴趣。此列记录了这些特殊的兴趣。会员可以利用这个信息来找到其他具有类似兴趣的会员。

(3) 创建表

现在我们已经作好了创建历史同盟表的准备。我们用CREATE TABLE 语句来完成这项工作,其一般格式如下:

其中tbl_name 代表希望赋予表的名称。column_specs 给出表中列的说明,以及索引的说明(如果有的话)。索引能使查找更快;我们将在第4 章“查询优化”中对其作进一步的介绍。

president 表的CREATE TABLE 语句如下所示:

如果想自己键入这条语句,则调用mysql,使samp_db 为当前数据库:

然后,键入如上所示的CREATE TABLE 语句。(请记住,语句结尾要增加一个分号,否则mysql将不知道哪儿是语句的结尾。)

为了利用来自样例数据库分发包的预先写下的描述文件来创建president 表,可从外壳程序运行下列命令:

不管用哪种方法调用mysql,都应该在命令行中数据库名的前面指定连接参数(主机名、用户名或口令)。CREATE TABLE 语句中每个列的说明由列名、类型(该列将存储的值的种类)以及一些可能的列属性组成。president 表中所用的两种列类型为VARCHAR 和DATE。VARCHAR(n)代表该列包含可变长度的字符(串)值,其最大长度为n 个字符。可根据期望字符串能有多长来选择n 值。

state 定义为VARCHAR( 2 );即所有州名都只用其两个字符的缩写来表示。其他的字符串列则需要更长一些,以便存放更长的值。

我们使用过的其他列类型为DATE。这种列类型表示该列存储的是日期值,这一点也不令人吃惊。而令人吃惊的是,日期的表示以年份开头。其标准格式为“ Y Y Y Y- M M - D D”(例如,“ - 07 - 18”)。这是日期表示的ANSI SQL 标准。我们用于president 表的唯一列属性为NULL(值可以缺少)和NOT NULL(必须填充值)。多数列是NOT NULL 的,因为我们总要有一个它们的值。可有NULL 值的两个列是s uff i x(多数姓名没有后缀)和death(有的总统仍然健在,所以没有死亡日期)。member 表的CREATE TABLE 语句如下所示:

将此语句键入mysql或执行下列外壳程序命令:

从列的类型来看,member 表并不很有趣:所有列中,除了一列之外,其他列都是可变长字符串。这个例外的列就是e x p i r a t i o n,为DATE 型。终止日期值有一个缺省值为“0 0 0 0 - 0 0 -0 0”,这是一个非NULL 的值,它表示未输入合法的日期值。这样做的原因是expiration 可以是NULL,它表示一个会员是终身会员。但是,因为此列可以为NULL,除非另外指定一个不同的值,否则它将取缺省值“ 0 0 0 0 - 0 0 - 0 0”。如果创建了一个新会员记录,但忘了指定终止日期,该会员将成为一个终身会员!通过采用缺省值“ 0 0 0 0 - 0 0 - 0 0”的方法,避免了这个问题。它还向我们提供了一种手段,即可以定期地搜索这个值,以找出过去未正确输入终止日期的记录。

请注意,我们“忘了”放入会员ID 号的列。这是专门为了以后练习使用ALTER TABLE语句而遗留下的。现在让我们来验证一下MySQL是否确实如我们所期望的那样创建了表。在mysql中,发布下列查询:

与MySQL3.23 一样,此输出还包括了显示访问权限信息的另一个列,这里没有给出,

因为它使每行太长,不易显示。

这个输出结果看上去和我们所期望的非常一致,除了state 列的信息显示它的类型为CHAR( 2 )。这就有点古怪了,我们不是定义它为VARCHAR(2) 了吗?是的,是这样定义的,但是MySQL已经悄悄地将此类型从VARCHAR 换成了CHAR。原因是为了使短字符串列的

存储空间利用更为有效,这里不多讨论。如果希望详细了解,可参阅第3 章中关于ALTE RTABLE 语句的介绍。但对这里的使用来说,两种类型没有什么差别。

如果发布一个DESCRIBE member 查询,mysql也会显示member 表的类似信息。DESCRIBE 在您忘了表中的列名、需要知道列的类型、了解列有多宽等的时候很有用。它对于了解MySQL存储表行中列的次序也很有用。列的这个存储次序在使用INSERT 或LOAD DATA 语句时非常重要,因为这些语句期望列值以缺省列的次序列出。DESCRIBE 可以省写为DESC,或者,如果您喜欢键入较多字符,则DESCRIB Etbl_name 另一个等同的语句为SHOW COLUMNS FROM tbl_name。

如果忘了表名怎么办?这时可以使用SHOW TABLE S。对于samp_db 数据库,我们目前

为止创建了两个表,其输出结果如下:

如果您甚至连数据库名都记不住,可在命令行上调用mysql而不用给出数据库名,然后发布SHOW DATABASES 查询:

数据库的列表在不同的服务器上是不同的,但是至少可以看到samp_db 和mysql;后一个数据库存放控制MySQL访问权限的授权表。DESCRIBE 与SHOW 查询具有可从外壳程序中使用的命令行等同物,如下:

% mysqlshow 与SHOW DATABASES 一样列出所有数据库

% mysqlshow db _ name 与SHOW TABLES 一样列出给定数据库的表

% mysqlshow db_name tbl_name 与DESCRIBE tbl_name 一样,列出给定表中的列

2. 用于学分保存方案的表

为了知道学分保存方案需要什么表,我们来看看在原来学分簿上是怎样记学分的。图1- 2示出学分簿的一页。该页的主体是一个记录学分矩阵。还有一些对学分有意义的必要信息。学生名和ID 号列在矩阵的一端。(为了简单好看,只列出了四个学生。)在矩阵顶端,记录了进行测验和测试的日期。图中示出9月3号、6号、16号和2 3号进行测验, 9月9号和10月1号进

行测试。

为了利用数据库来记录这些信息,需要一个学分表。这个表中应该包含什么记录呢?很明显,每一行都需要有学生名、测验或测试的日期以及学分。图1-3 示出了用这样的表表示的一些来自学分簿的学分。(日期以MySQL的表示格式“Y Y Y Y- M M - D D”表示。)

但是,以这种方式设置表似乎有点问题。好像少了点什么。请看图1- 3中的记录,我们分辨不出是测验的学分还是测试的学分。如果测验和测试的学分权重不同,在确定最终的学分等级时知道学分的类型是很重要的。或许可以试着从学分的取值范围来确定学分的类型(测验的学分一般比测试的学分少),但是这样做很不方便,因为这需要进行判断,而且在数据中也不明显。可以通过记录学分的类型来进行区分,如对学分表增加一列,此列包含“ T”或“Q”以表示是“测试”或是“测验”,如图1-4 所示。这具有使学分数据类型清析易辨的优点。不利的地方是这个信息有点冗余。显然对具有同一给定日期的记录,学分的类型列总是取相同的值。9月2 3日的学分总是为“ Q”类型,而10月1日的学分其类型总是具有“ T”类型。这样令人很不满意。如果我们以这种方式记录一组测验或测试的学分,不仅要为每个新记录输入相同的日期,而且还要一再重复地输入相同的学分类型。谁会希望一再输入冗余的信息呢?

我们可以试试另外一种表示。不在score 表中记录学分类型,而是从日期上区分它们。我们可以做一个日期列表,用它来记录每个日期发生的“学分事件”(测验或测试)。然后可以将学分与这个事件列表中的信息结合,确定学分是测验学分还是测试学分。这只要将score 表记录中的日期与event 表中的日期相匹配得出事件类型即可。图1- 5示出这个表的设计并演示了score 表记录与9月2 3日这个日期相关联的工作。通过将score 表中的记录与event 表中记录相对应,我们知道这个学分来自测验。

这比根据某些猜测来推断学分类型要好得多;我们可以根据明确记录在数据库中的数据来直接得到学分类型。这也比在score 表中记录学分类型更好,因为我们只需对每个类型记录一次。

但是,在第一次听到这种事情时(即结合使用多个表中的信息),可能会想,“嗯,这是一个好主意,但是不是要做很多工作呢?会不会使工作更复杂了?”在某种程度上,这种想法是对的。处理两个记录表比处理一个要复杂。但是再来考察一下学分簿(见图1- 2)。不是也记录了两套东西吗?考虑下列事实:

■ 在学分矩阵中用两个单元记录学分,其中每个单元都是按学生名字和日期(在矩阵的旁边和顶上)进行索引的。这代表了一组记录;与score 表的作用相同。

■ 怎样知道每个日期代表的事件类型呢?在日期上方写了字符“ T”或“Q”!因此,也在矩阵顶上记录了日期和学分类型之间的关系。它代表第二组记录;与event 表的作用相同。

换句话说,这里建议在两个表中记录信息与用学分簿记录信息所做的工作没什么不同。唯一不同的是,这两组信息在学分簿中不是那么明显地被分开。在图1- 5中所示的event 表的设计中加了一个要求,那就是日期必须是唯一的,因为要用它连接score 与event 表的记录。换句话说,同一天不能进行两次测验,或者同一天不能进行一次测验和一次测试。否则,将会在score 表中有两个记录并且在event 表中也有两个记录,全都具有相同的日期,这时就不知道应如何将score 的记录与event 的记录进行匹配。如果每天不多于一个学分事件,这就是一个永远不会出现的问题,可是事实并非如此简单。有时,一天中可能会有不止一个学分事件。我常听有的人说他们的数据,“那种古怪情况从不会出现。”然而,如果这种情况确实出现时,就必须重新设计表以适应这种情况引起的问题。最好是预先考虑以后可能出现的问题,并预先准备好怎样处理他们。因此,我们假定有时可能会需要同一天记录两组学分。我们怎样处理呢?如果出现这种情况,问题并不难解决。只要对处理数据的方式作一点小的更改,就可使同一日期上有多个事件而不会引起问题:

1) 增加一个列到event 表,并用它来给表中每个记录分配一个唯一的编号。实际上这就给了每个事件一个唯一的ID 号,因此我们称该列为event_id 列。(如果觉得这好像是做傻事,可看一下图1-2 中的学分簿,其中已经有这个特征了。事件ID 正好与学分簿分数矩阵中列号相似。这个编号可能没有清晰地写在那儿并标上“事件ID,”但是它确实在那儿。)

2) 当向score 表中输入学分时,输入的是事件ID 而不是日期。这些改变的结果如图1-6 所示。现在连接score 和event 表时,用的是事件ID 而不是日期,而且不仅用event 表来决定每个学分的类型,而且还用它来决定其日期。并且在event 表中不再有日期必须唯一这个限制,而唯一的是事件ID。这表示同一天可以有一打测试和测验,而且能够在记录里边直接保存它们。(毫无疑问,学生们听到这个一定浑身发抖。)不幸的是,从人的观点来看,图1-6 中的表设计较前一个更不能令人满意。score 表也更为抽象一些,因为它包含的从直观上可以理解的列更少。而图1-4 中此表的设计直观且容易理解,因为那个score 表具有日期和学分类型的列。当前的score 表如图1-6 所示,日期和学分类型的列都没有了。这极大地去除了作为人能够很容易考虑的一切。谁希望看到其中有“事件ID”的score 表?如果有的话,也不代表我们大多数人。

此时,可看到能够电子化地完成学分记录,且在赋予学分等级时不必做各种乏味的手工计算。但是,在考虑了如何实际在一个数据库中表示学分信息后,又会被怎样抽象和拆分组成学分信息的表示难住了。自然会产生一个问题:“根本不使用数据库可能会更好一些?或许MySQL不适合我?”正如您所猜测的那样,笔者将从否定的方面对这个问题进行回答,否则这本书就没必要再往下写了。不过,在考虑如何做一件工作时,应考虑各种情况并提问是否最好不使用数据库系统(如MySQL)而使用一些别的东西(如电子表格等):

■ 学分簿有行和列,而电子表格也有。这使学分簿和电子表格在概念上和外观上都非常类似。

■ 电子表格能够完成计算,可以利用一个计算字段来累计每个学生的学分。但是,要对测验和测试进行加权可能有点麻烦,但这也是可以办得到的。另一方面,如果希望只查看某部分数据(如只查看学分或测试),进行诸如男孩与女孩的比较,或以一种灵活的方式显示合计信息等,情况又大有不同了。电子表格的功能显得要差一些,而关系数据库系统完成这些工作相当容易。另外要考虑的一点是为了在关系数据库中进行表示而对数据进行抽象和分解,这个问题并不真的那么难以应付。只要考虑安排数据库使其不会以一种对您希望做的事无意义的方式来表示数据即可。但是,在确定了表示方式之后,就要靠数据库引擎来协调和表示数据了。您肯定不会希望将它视为一堆支离破碎的东西。

例如,在从score 表中检索学分时,不希望看到事件ID;但希望看到日期。这没有什么问题。数据库将会根据事件ID 从event 表中查找出日期。您还可能想要看看是测验的学分或测试的学分。这也不成问题。数据库将用相同的方法查找出学分类型,也是利用事件ID。请记住,这就是如像MySQL这样的关系数据库的优势所在,即,使一样东西与另一样东西相关联,以便从多个来源得出信息并以您实际想看到的形式提供出来。在学分保存数据的情况中,MySQL确实利用事件ID 将信息组合到了一起,而无需人工来完成这件事。

现在我们先来看看,如何使MySQL完成这种将一个东西与另一个东西相联系的工作。

假定希望看到9月2 3号的学分,针对某个特定日期中给出的事件的学分查询如下所示:

相当吓人,是吗?这个查询通过将score 表的记录与event 表的记录连接(关联)来检索学生名、日期、学分和学分的类型。其结果如下所示:

您肯定注意到了,它与图1-4 中给出的表设计相同,而且不需要知道事件ID 就可得出这个结果,只需指出感兴趣的日期并让MySQL查找出哪个学分记录具有该日期即可。如果您一直担心抽象和分解会使我们损失一些东西的话,看到这个世界,就不会有这种担心了。

当然,在考虑过查询后,您还可能对其他别的东西产生担心。即,这个查询看上去有点长并且也有点复杂;是不是做了很多工作写出这样的东西只是为了查找某个给定日期的学分?是的,确实是这样。但是,在每次想要发布一个查询时,有几种方法可以避免键入多行的SQL。一般情况下,一旦您决定如何执行这样一个查询并将它保存起来后,就可以按需要多次执行它。我们将在1. 5节“与mysql交互的技巧”中介绍怎样完成这项工作。

在上述查询的介绍中,我们有点超前了。不过,这个查询比起我们要实际用来得出学分的查询是有点简单了。原因是,我们还要对表的设计作更多的修改。我们将采用一个唯一的学生ID,而不在score 表中记录学生名。(即,我们将使用来自学分簿的“ ID”列的值而不是来自“ Name”列的值。)然后,创建另一个称为student 的表来存放name 和student_id 列(见图1- 7)。

为什么要作出这种修改呢?只有一个原因,可能有两个学生有相同的名字。采用唯一的学生ID 号可帮助区分他们的学分。(这与利用唯一的事件ID 而不是日期来分辨出相同日期的测试或测验完全类似。)在对表的设计作了这样的修改后,实际用来获得给定日期的学分查询变得更为复杂了一些,这个查询如下:

如果您不能立即清楚地读懂这个查询的意思的话,也不必担心。在进一步深入这个教程之后,就能看懂这个查询了。将会从图1- 7中注意到,在student 表中增加了点学分簿中没有的东西。它包含了一个性别列。这便可以做一些简单的事情,如对班级中男孩和女孩的人数计数;也可以做一些更为复杂的事情,如比较男孩和女孩的学分。我们已经设计完了学分保存的几乎所有的表。现在只需要另外

一个表来记录出勤情况即可。这个表的内容相对较为直观,即,一个学生ID 号和一个日期(见图1- 8)。表中的每行表示特定的学生在

给定的日期缺勤。在学分时段末,我们将调用MySQL的计数功能来汇总此表的内容,以便得出每个学生的缺勤数。

既然现在已经知道学分保存的各个表的结构,现在可以创建它们了。student 表的CREATE TABLE 语句如下:

将上述语句键入mysql或执行下列外壳程序命令:

CREATE TABLE 语句创建了一个名为student 的表,它含有三列,分别为: name、s e x和s t ud e n t _ i d,

name 是一个可变长的字符串列,最多可存放20 个字符。这个名字的表示比历史同盟表中所用的表示要简单,它只用了单一的列而不是分别的名和姓列。这是因为我们已经预先知道,不存在无需做另外的工作就使得在多个列上工作得更好的查询样例。sex 表示学生是男孩还是女孩。这是一个ENUM(枚举)列,表示只能取明确地列在说明中的值之一,这里列出的值为:“F”和“M”,分别表示女和男。在某列只具有一组有限值时,ENUM 类型非常有用。我们可以用CHAR(1) 来代替它,但是ENUM 更明确规定了列可以取什么值。如果对包括一个ENUM 列的表发布一条DESCRIBE tbl_name 语句,MySQL将确切地显示可取的值有哪些。顺便说一下, ENUM 列中的值不一定只是单个字符。此列还可以定义为ENUM(‘f e m a l e’,‘m a l e’)。

student_id 为一个整数型列,它将包含唯一的ID 号。通常,大概会从一个中心资料来源处(如学校办公室)取得学生的ID 号,但在这里是我们自己定的。虽然student_id 列只包含一个数,但其定义包括几个部分:

■ INT 说明此列的值必须取整数(即无小数部分)。

■ UNSIGNED 不允许负数。

■ NOT NULL 表示此列的值必须填入。(任何学生都必须有一个ID 号。)

■ AUTO_INCREMENT 是MySQL中的一个特殊的属性。其作用为:如果在创建一个新的student 表记录时遗漏了student_id 的值(或为NULL),MySQL自动地生成一个大于当前此列中最大值的唯一ID 号。在录入学生表时将用到这个这特性,录入学生表时可以只给出name 和sex 的值,让MySQL自动生成student_id 列值。

■ PRIMARY KEY 表示相应列的值为快速查找进行索引,并且列中的每个值都必须是惟一的。这样可防止同一名字的ID出现两次,这对于学生ID 号来说是一个必须的特性。(不仅如此,而且MySQL还要求每个AUTO_INCREMENT 列都具有一个惟一索引。)如果您不理解AUTO_INCREMENT 和PRIMARY KEY 的含义,只要将其想像为一种为每个学生产生ID 号的魔术方法即可。除了要求值唯一外,没有什么别的东西。请注意:如果确实打算从学校办公室取得学生ID 号而不是自动生成它们,则可以按相同的方法定义student_id 列,只不过不定义AUTO_INCREMENT 属性即可。event 表如下定义:

将此语句键入mysql或执行下列外壳程序的命令:

所有列都定义为NOT NULL,因为它们中任何一个值都不能省略。date 列存储标准的MySQLDATE 日期值,格式为“Y Y Y Y- M M - D D”(首先是年)。type 代表学分类型。像student 表中的sex 一样,type 也是一个枚举列。所允许的值为“T”和“Q”,分别表示“测试”和“测验”。event_id 是一个AUTO_INCREMENT 列,类似于student 表中的student_id 列。采用AUTO_INCREMENT 允许生成唯一的事件ID 值。正如student 表中的student_id 列一样,与值的惟一性相比,某个特定的值并不重要。score 表如下定义:

将此语句键入mysql或执行下列外壳程序的命令:

score 为一个INT (整型)列。即,假定学分值总是为一个整数。如果希望使学分值具有小数部分,如5 8 . 5,应该采用浮点列类型,如FLOAT或DECIMAL。student_id 列和event_id 列都是整型,分别表示每个学分所对应的学生和事件。通过利用它们来连接到student 和event 表,我们能够知道学生名和事件的日期。我们将两个列组成了PRIMARY KEY。这保证我们不会对同一测验或测试重复一个学生的学分。而且,这样还很容易在以后更改某个学分。例如,在发现学分录入错时,可以在利用MySQL的REPLACE语句放入一个新记录,替换掉旧的记录。不需要执行DELETE 语句与INSERT 语句;MySQL自动替我们做了。请注意,它是惟一的event_id 和student_id 的组合。在score 表中,两者自身都可能不惟一。一个event_id 值可有多个学分记录(每个学生对应一个记录),而每个student_id 值都对应多个记录(每个测验和测试有一个记录)。用于出勤情况的absence 表如下定义:

将此语句键入mysql或执行下列外壳程序的命令:

student_id 和date 列两者都定义为NOT NULL,不允许省略值。应定义这两列的组合为主键,以免不当心建立了重复的记录。重要的是不要对同一天某个学生的缺旷进行重复计数。

1.4.7 增加新记录

至此,我们的数据库及其表都已经创建了,在下一节“检索信息”中,我们将看到怎样从数据库中取出数据。现在我们先将一些数据放入表中。在数据库中加入数据有几种方法。可通过发布INSERT 语句手工将记录插入某个表中。还可以通过从某个文件读取它们来增加记录,在这个文件中,记录既可以是利用L O A DDATA 语句或mysqlimport 实用程序装入的原始数据值,也可以是预先写成可馈入mysql的INSERT 语句的形式。本节介绍将记录插入表的每种方法。您所应做的是演习各种方法以明了它们是如何起作用的。然后到本节结束处运行那儿给出的命令来清除表并重装它们。这样做,能够保证表中含有作者撰写下一节时所处理的相同记录,您也能得到相同的结果。让我们开始利用INSERT 语句来增加记录,这是一个SQL 语句,需要为它指定希望插入数据行的表或将值按行放入的表。INSERT 语句具有几种形式:

■ 可指定所有列的值

例如:

“I N TO”一词自MySQL3.22.5 以来是可选的。(这一点对其他形式的INSERT 语句也成立。)VALUES 表必须包含表中每列的值,并且按表中列的存放次序给出。(一般,这就是创建表时列的定义次序。如果不能肯定的话,可使用DESCRIBE tbl_name 来查看这个次序。)在MySQL中,可用单引号或双引号将串和日期值括起来。上面例子中的NULL值是用于student 和event 表中的AUTO_INCREMENT 列的。(插入“错误”的值将导致下一个student_id 或event_id 号的自动生成。)自3.22.5 以来的MySQL版本允许通过指定多个值的列表,利用单个的INSERT语句将几行插入一个表中,如下所示:

例如:

这比多个INSERT 语句的键入工作要少,而且服务器执行的效率也更高。

■ 可以给出要赋值的那个列,然后再列出值。这对于希望建立只有几个列需要初始设置的记录是很有用的。

例如:

自MySQL3.22.5 以来,这种形式的INSERT 也允许多个值表:

在列的列表中未给出名称的列都将赋予缺省值。

■ 自MySQL3.22 .10 以来,可以col_name = value 的形式给出列和值。

例如:

在SET 子句中未命名的行都赋予一个缺省值。使用这种形式的INSERT 语句不能插入多行。将记录装到表中的另一种方法是直接从文件读取数据值。可以用LOAD DATA 语句或用mysqlimport 实用程序来装入记录。LOAD DATA 语句起批量装载程序的作用,它从一个文件中读取数据。可在mysql内使用它,如下所示:

该语句读取位于客户机上当前目录中数据文件member.txt 的内容,并将其发送到服务器装入member 表。如果您的MySQL版本低于3 . 2 2 . 15,则LOAD DATA LOCAL 不起作用,因为那时从客户机读取数据的能力是在LOAD DATA 上的。(没有LOCAL 关键字,被读取的文件必须位于服务器主机上,并且需要大多数MySQL用户都不具备的服务器访问权限。)缺省时,LOAD DATA 语句假定列值由tab 键分隔,而行则以换行符结束。还假定各个值是按列在表中的存放次序给出的。也有可能需要读取其他格式的文件,或者指定不同的列次

序。更详细的内容请参阅附录D的LOAD DATA 的条款。mysqlimport 实用程序起LOAD DATA 的命令行接口的作用。从外壳程序调用mysqlimport ,它生成一个LOAD DATA 语句:

mysqlimport 生成一个LOAD DATA 语句,此语句使member.txt 文件被装入member 表。如果您的MySQL版本低于3 . 2 2 . 15,这个实用程序不起作用,因为--local 选项需要L O A DDATA LOCAL。正如使用mysql一样,如果您需要指定连接参数,可在命令行上数据库名前指定它们。mysqlimport 从数据文件名中导出表名(它将文件名第一个圆点前的所有字符作为表名)。例如,member.txt 将被装入member 表,而president.txt 将被装入president 表。如果您有多个需要装入单个表的文件,应仔细地选择文件名,否则mysqlimport 将不能使用正确的表名。对于如像member1.txt 与member2.txt 这样的文件名, mysqlimport 将会认为相应的表名为

member1和member 2。不过,可以使用如member.1.txt 和member.2.txt 或member.txt1和member.txt2 这样的文件名。在试用过这些记录追加的方法后,应该清除各个表并重新装载它们,以便它们的内容与下一节假定的内容相同。从外壳程序执行下列命令:

每个文件都含有一个删除可能曾经插入到表中的记录的DELETE 语句,后跟一组INSERT 语句以初始化表的内容。如果不希望分别键入这些命令,可试一下下列语句:

1.4.8 检索信息

现在各个表已经创建并装有数据了,因此让我们来看看可以对这些数据做点什么。SELECT 语句允许以一般的或特殊的方式检索和显示表中的信息。它可以显示表的整个内容:

或者只显示单个行中单个列的内容:

SELECT 语句有几个子句(部件),可以根据需要用来检索感兴趣的信息。每个子句都可简单、可复杂,从而SELECT 作为一个总的语句也繁简皆宜。但是,可以放心,本书中不会有花一个钟头来编写的长达数页的查询。(我在书中看到有很长的查询时,一般会立即跳过它们,因此我猜您也会这样。)SELECT 语句的一般形式为:SELECT 要选择的东西FROM 一个或多个表WHERE 数据必须满足的条件记住,SQL 为一个自由格式的语言,因此在您编写SELECT 查询时,语句的断行不必严格依照本书。

为了编写SELECT 语句,只需指定需要检索什么,然后再选择某些子句即可。刚才给出的子句“ FROM”、“WHERE”是最常用的,还有一些其他的子句,如GROUP BY、ORDER BY和LIMIT 等。FROM 子句一般都要给出,但是如果不从表中选择数据,也可不给出。例如,下列查询只显示某些可以直接计算而不必引用任何表的表达式的值,因此不需要用FROM 子句:

在确实使用一个FROM 子句指定了要从其中检索数据的表时, SELECT 语句的最“普通”的格式是检索所有内容。用“ *”来表示“所有列”。下面的查询将从student 表中检索所有行并显示:

各列按它们MySQL在表中存放的次序出现。该次序与发布DESCRIBE student 语句时显示的列次序相同。(例子末尾的“. . .”表示此查询返回的输出行比这里显示的还要多。)可明确地命名希望得到的一列或多列。如果只选择学生名,发布下列语句:

如果名字不止一列,可用逗号分隔它们。下列的语句与SELECT * FROM student 等价,只是明确地指出了每一列:

可按任意次序给出列:

如果有必要,同一列甚至也可以给出多次,虽然这样做一般是没有意义的。列名在MySQL中不区分大小写的。下面的查询是等同的:

数据库和表名有可能区分大小写的;这有取决服务器主机上使用的文件系统。在UNIX上运行的服务器对数据库名和表名是区分大小写的,因为UNIX 的文件名是区分大小写的。Windows 的文件名不区分大小写,因此运行在Windows 上的服务器对数据库名和表名不区分

大小写。MySQL允许您一次从多个表中选择列。我们将这个内容留到“从多个表中检索信息”小节去介绍。

1. 指定检索条件

为了限制SELECT 语句检索出来的记录集,可使用WHERE 子句,它给出选择行的条件。可通过查找满足各种条件的列值来选择行。

可查找数字值:

也可以查找串值。(注意,一般串的比较是不区分大小写的。)

可以查找日期值:

可搜索组合值:

WHERE 子句中的表达式可使用表1-1中的算术运算符、表1-2 的比较运算符和表1-3 的逻辑运算符。还可以使用圆括号将一个表达式分成几个部分。可使用常量、表列和函数来完成运算。在本教程的查询中,我们有时使用几个MySQL函数,但是MySQL的函数远不止这里

给出的这些。请参阅附录C,那里给出了所有MySQL函数的清单。

在用表达式表示一个需要逻辑运算的查询时,要注意别混淆逻辑与运算符与我们平常使用的“与”的含义。假如希望查找“出生在Vi rginia 的总统与出生在Maryland 的总统”。应该注意怎样表示“与”的关系,能写成如下的查询吗?

错了,因为这个查询的意思是“选择既出生在Vi rginia 又出生在M a r y l a n d的总统”,不可能有同时出生在两个地点的总统,因此这个查询无意义。在英语中,可以用“a n d”表示这种选择,但在SQL 中,应该用OR 来连接两个条件,如下所示:

这有时是可以觉察到的,不仅仅是在编写自己的查询时可以觉察到,而且在为他人编写查询时也可以知道。最好是在他人描述想要检索什么时仔细听,但不一定使用相同的逻辑运算符将他人的描述转录成SQL 语句。对刚才所举的例子,正确的英语等价描述为“选择出生在Vi rginia 或者出生在Maryland 的总统。”

2. NULL 值

NULL 值是特殊的;因为它代表“无值”。不可能以评估两个已知值的相同方式来将它与已知值进行评估。如果试图与通常的算术比较运算符一道使用NULL,其结果是未定义的:

为了进行NULL 值的搜索,必须采用特殊的语法。不能用= 或!= 来测试等于NULL 或不等于NULL,取而代之的是使用IS NULL 或IS NOT NULL 来测试。例如,因为我们将健在总统的死亡日期表示为NULL,那么可按如下语句查找健在的总统:

MySQL3.23 及以后的版本具有一个特殊的MySQL专有的比较运算符“ < = >”,即使是NULL 与NULL 的比较,它也是可行的。用这个比较运算符,可将前面的两个查询重写为:

3. 对查询结果进行排序

有时我们注意到,在一个表装入初始数据后,对其发布一条SELECT * FROM tbl_name查询,检索出的行与这些行 入的顺序是相同的。但不要认为这种情况是有规律的。如果在初始装入表后进行了行的删除和插入,就会发现服务器返回表的行次序被改变了。(删除记录在表中留下了未使用的“空位”,MySQL在以后插入新记录时将会试图对其填补。)缺省时,如果选择了行,服务器对返回行的次序不作任何保证。为了对行进行排序,可

使用ORDER BY 子句:

在ORDER BY 子句中,可在列名之后利用ASC 或DESC 关键字指定排序是按该列值的升序或降序进行的。例如,为了按倒序(降序)名排列总统名,可如下使用DESC:

如果在ORDER BY 子句中,对某个列名既不指定ASC 又不指定DESC,则缺省的次序为升序。在对可能包含NULL 值的列进行排序时,如果是升序排序, NULL 值出现在最前面,如果是按降序排序,NULL 值出现在最后。

查询结果可在多个列上进行排序,而每个列的升序或降序可以互相独立。下面的查询从president 表中检索行,并按出生的州降序、在每个州中再按姓氏的升序对检索结果进行排序:

4. 限制查询结果如果一个查询返回许多行,但您只想看其中的几行,则可以利用LIMIT 子句,特别是与ORDER BY 子句结合时更是如此。MySQL允许限制一个查询的输出为前n 行。下面的查询选择了5 位出生日期最早的总统:

如果利用ORDER BY birth DESC 按降序排序,将得到5 位最晚出生的总统。LIMIT 也可以从查询结果中取出中间部分。为了做到这一点,必须指定两个值。第一个值为结果中希望看到的第一个记录(第一个结果记录的编号为0 而不是1)。第二个值为希望看到的记录个数。下面的查询类似于前面那个查询,但只显示从第11行开始的5 个记录:

自MySQL3.23.2 以来,可按照一个公式来排序查询结果。例如,利用ORDER BYRAND( ) 与LIMIT 结合,从president 表中随机抽取一个记录:

5. 计算并命名输出的列值

前面的多数查询通过从表中检索值已经产生了输出结果。MySQL还允许作为一个公式的结果来计算输出列的值。表达式可以简单也可以复杂。下面的查询求一个简单表达式的值(常量)以及一个涉及几个算术运算符和两个函数调用的较复杂的表达式的值:

此查询把名和姓连接起来,中间间隔一个空格,将总统名形成一个单一字符串,而且将出生城市和州连接在一起,中间隔一个逗号,形成出生地。

在利用表达式来计算列值时,此表达式被用作列标题。如果表达式很长(如前面的一些查询样例中那样),那么可能会出现一个很宽的列。为了处理这种情况,此列可利用AS name结构来重新命名标题。这样的名称为列别名。用这种方法可使上面的输出更有意义,如下所示:

6. 使用日期

在MySQL中使用日期时要记住的是,在表示日期时首先给出年份。1999 年7 月27 日表示为“1999 - 07 - 27”,而不是像通常那样表示为“ 07 - 27 - 1999”或“27 - 07 - 1999”。MySQL提供了几种对日期进行处理的方法。可以对日期进行的一些运算如下:

■ 按日期排序。(这点我们已经看到几次了。)

■ 查找特定的日期或日期范围。

■ 提取日期值的组成部分,如年、月或日。

■ 计算日期的差。

■ 日期增加或减去一个间隔得出另一日期。

下面给出一些日期运算的例子。

为了查找特定的日期,可使用精确的日期值或与其他日期值进行比较,将一个DATE 列与有关的日期值进行比较:

为了测试或检索日期的成分,可使用诸如YEAR( )、MONTH( ) 或DAYOFMONTH( ) 这样的函数。例如,可通过查找月份值为3 的日期,找出与笔者出生在相同月份(三月)的总统。

为了更详细,详细到天,可组合测试MONTH( ) 和DAYOFMONTH( ) 以找出在笔者的生日出生的总统:

这是一种可用来生成类似报纸上娱乐部分所刊登的那种“这些人今天过生日”清单的查询。但是,不必按前面的查询那样插入一个特殊的日期。为了查找每年的今天出生的总统,只要将他们的生日与C U R R E N T _ DATE 进行比较即可:

可从一个日期减去另一个日期。这样可以知道日期间的间隔,这对于确定年龄是非常有用的。例如,为了确定哪位总统活得最长,可将其逝世日期减去出生日期。为此,可利用函数TO _ DAYS( ) 将出生日期和逝世日期转换为天数,求出差,然后除以365 得出大概的年龄:

此查询中所用的FLOOR( ) 函数截掉了年龄的小数部分,得到一个整数。得出日期之差,还可以确定相对于某个特定日期有多长时间。这样可以告诉历史同盟的会员,他们还有多久就应该更新自己的会员资格了。计算他们的截止日期和当前日期之差,如果小于某个阈值,则不久就需要更新了。下面的查询是查找需要在60 天内更新的会员:

自MySQL3.22 以来,可使用DATE_ADD( ) 或DATE_SUB( ) 从一个日期计算另一个日期。这些函数取一个日期及时间间隔并产生一个新日期。例如:

本节中前面给出的一个查询选择70 年代逝世的总统,它对选择范围的端点使用直接的日期值。该查询可以利用一个字符串日期和一个由开始日期和时间间隔计算出的结束日期来重写:

会员更新查询可根据DATE_ADD( ) 写出如下:

本章前面给出了一个查询如下,确定不久要来检查但还没来诊所的牙科病人:

现在回过头来看,读者会更清楚这个查询的含义了。

7. 模式匹配

MySQL允许查找与某个模式相配的值。这样,可以选择记录而不用提供精确的值。为了进行模式匹配运算,可使用特殊的运算符( LIKE 和NOT LIKE),并且指定一个包含通配符的串。字符“_”匹配任意单个字符,而“%”匹配任意字符序列(包括空序列)。使用L I K E或NOT LIKE 的模式匹配都是不区分大小写的。下列模式匹配以“W”或“w”开始的姓:

此查询给出了一个常见的错误,它对一个算术比较运算符使用了模式。这种比较成功的惟一可能是相应的 实包含串“ W %”或“w %”。下列模式匹配任意位置包含“W”或“w”的姓:

MySQL还提供基于扩展正规表达式的模式匹配。正规表达式在附录C 的REGEXP 运算符的介绍中描述。

8. 生成汇总

MySQL所能做的最有用的事情是浓缩大量的原始数据行并对其进行汇总。当学会了利用MySQL来生成汇总时,它就变成了用户强有力的好帮手了,因为手工进行汇总是一项冗长的、费时的、易出错的工作。汇总的一种简单的形式是确定在一组值中哪些值是唯一值。利用DISTINCT 关键字来删除结果中的重复行。例如,总统出生的各个州可按如下找出:

其他的汇总形式涉及计数,可利用COUNT( ) 函数。如果使用COUNT (*),它将给出查询所选择的行数。如果一个查询无WHERE 子句,COUNT(*) 将给出表中的行数。下列查询给出共有多少人当过美国总统:

如果查询有WHERE 子句,COUNT(*) 将给出此子句选择多少行。下面的查询给出目前为止对班级进行了多少次测试:

COUNT(*) 对选中的行进行计数。而COUNT(col_name) 只对非NULL 值进行计数。下面的查询说明了这些差异:

这表示,总共有41位总统,他们中只有一个具有名字后缀,并且大多数总统都已去世。自MySQL3.23.2 以来,可以将COUNT( ) 与DISTINCT 组合对选择结果集中不同的值进行计数。例如,为了对总统出生的不同州进行计数,可执行下列查询:

可以根据汇总列中单独的值对计数值进行分解。例如,您可能根据下列的查询结果知道班级中所有学生的人数:

但是,有多少是男孩?有多少是女孩?分别得出男孩、女孩的一种方法是分别对每种性别进行计数:

虽然这个方法可行,但是它很繁锁而且并不真正适合于可能有许多不同的值的列。考虑一下怎样以这种方式确定每个州出生的总统人数。您不得不找出有哪些州,从而不能省略(SELECT DISTINCT state FROM president),然后对每个州执行一个SELECT COUNT(*) 查询。很显然,有些事是可以简化的。所幸MySQL可以利用单个查询对一个列中不同的值进行计数。因此,针对学生表可以按如下得出男孩和女孩的人数:

如果以这种方法对值计数, GROUP BY 子句是必须的;它告诉MySQL在对值计数之前怎样进行聚集。如果将其省去,则要出错。COUNT(*) 与GROUP BY 一起用来对值进行计数比分别对每个不同的列值进行计数有更多的优点,这些优点是:

■ 不必事先知道要汇总的列中有些什么值。

■ 不用编写多个查询,只需编写单个查询即可。

■ 用单一查询就可以得出所有结果,因此可以对结果进行排序。

前两个优点对于更方便地表示查询很重要。第三个优点也较为重要,因为它提供了显示

结果的灵活性。在使用GROUP BY 子句时,其结果是在要分组的列上进行排序的,但是可以

使用ORDER BY 来按不同的次序进行排序。例如,如果想得到各州产生的总统人数,并按产

生人数最多的州优先排出,可以如下使用ORDER BY 子句:

如果希望进行排序的列是从计算得出的,则可以给该列一个别名,并在ORDER BY 子句中引用这个别名。前面的查询说明了这一点; COUNT(*) 列的别名为count。引用这样的列的另一种方法是引用它在输出结果中的位置。前面的查询可编写如下:

我不认为按位置引用列易读。如果增加、删除或重新排序输出列,必须注意检查ORDER BY子句,并且如果列号改变后还得记住它。别名就不存在这种问题。如果想与计算出来的列一道使用GROUP BY,正如ORDER BY 一样,应该利用别名或列位置来引用它。下面的查询确定在一年的每个月中出生的总统人数:

如果不想用LIMIT 子句来限制查询输出,而是利用查找特定的COUNT( ) 值来达到这个目的,可使用HAVING 子句。下面的查询给出了产生两个以上总统的州:

从更为普遍的意义上说,这是一种在要查找的列中重复值时执行的查询类型。HAVING 类似于WHERE,但它是在查询结果已经选出后才应用的,用来缩减服务器实际送到客户机的结果。除了COUNT( ) 外还有许多汇总函数。MIN( )、MAX( )、SUM( ) 和AVG( ) 函数在确定列的最大、最小、总数和平均值时都非常有用,甚至可以同时使用它们。下面的查询得出给定的测试和测验的各种数字特性。它还给出有多少学分参与了每个值的计算(有的学生可能缺旷或未计入)。

当然,如果您知道这些信息是来自测验的还是测试的,则它们就会更有意义。但是,为了产生那样的信息,还需要参考event 表;我们将在下一节“从多个表中检索信息”讨论这个查询。汇总信息是很有意思的,因为它们是那么有用,但不太好控制,容易走样。请看下列查询:

此查询选择已经去世的总统,按出生地对他们进行分组,并计算出他们逝世时的年龄,计算出平均年龄(每个州的),然后按平均年龄进行排序。换句话说,此查询按所出生地确定已故总统的平均寿命。但这说明了什么呢?它仅仅说明您可写该查询,当然并不说明此查询是否值得写。并不是用一个数据库可以做的所有事情都同样有意义;但是,人们有时在发现可以利用自己的数据库进行查询时感到很开心。这可能说明关于转播运动会的不断增加的深奥的(空洞的)统计数据在过去几年里正在不断增多的原因。运动统计者可以使用他们的数据库来计算出某个队的历史纪录,而这些数字你可能感兴趣,也可能毫无兴致。

9. 从多个表中检索信息

到目前为止,我们所编写的查询都是从单个表中得到数据的。现在,我们将进行一件更为有趣的工作。以前笔者曾经提到过,关系DBMS 的强大功能在于它能够将一样东西与另一样东西相关联,因为这样使得能够结合多个表中的信息来解答单个表不能解答的问题。本节介绍怎样编写这种查询。在从多个表中选择信息时,需要执行一种称为连接( j o i n)的操作。这是因为需要将一个表中的信息与其他表中的信息相连接来得出查询结果。即通过协调各表中的值来完成这项工作。

我们来研究一个例子。在前面的“学分保存方案”小节中,给出了一个检索特定日期的测验或测试学分的查询,但没有解释。现在可以进行解释了。这个查询实际涉及到三种连接方法,因此我们分两步进行研究。第一步,我们构造一个对特定日期的学分进行选择的查询,如下所示:

此查询找出具有给定日期的记录,然后利用该记录中的事件ID 查找具有相同事件ID 的学分。对于每个匹配的事件记录和学分记录组合,显示学生ID、学分、日期和事件类型。此查询在两个重要方面不同于我们曾经编写过的其他查询。它们是:

■ FROM 子句给出了不止一个表名,因为我们要检索的数据来自不止一个表:

FROM event,score

■ WHERE 子句说明event 和score 表是由每个表中的event_id 值的匹配连接起来的:

where event.event_id=score.event_id

请注意,我们是怎样利用tbl_name.col_name 语法引用列,以便MySQL知道引用的是哪些表的列。(event_id 出现在两个表中,如果不用表名来限定它的话将会出现混淆。)此查询中的其他列( date、score、type)可单独使用而不用表名限定符,因为它们在表中只出现一次,从而不会出现含混。但是,一般在连接中我们对每个列都进行限定以便清晰地表示出每个列是属于哪个表。在完全限定的形式下,查询如下:

从现在起,我们将使用完全限定的形式。第二步,我们利用student 表完成查询以便显示学生名。(第一步中查询的输出给出了student_id 字段,但是名字更有意义。)名字显示是利用score 表和student 表两者都具有student_id 列,使它们中的记录可被连接这个事实来完成的。最终的查询如下:

此查询与前一个查询的差别在于:

■ student 表被增加到了FROM 子句中,因为除了event 表和score 表外还用到了它。

■ student_id 列现在不明确了(因为现在有两个引用到的表都含有此列),因此必须限定为score.student_id 或student.student_id 以表明使用的是哪个表。

■ WHERE 子句有一个附加项,它说明根据学生ID 将score 表记录与student 表记录进行匹配。

■ 此查询是显示学生名而不是学生ID。(当然,如果愿意的话,可以两者都显示。)利用此查询,可以加入任意日期,得到该日期的学分,用学生名和学分类型完善查询结果。不一定要了解关于学生ID 或事件ID 的情况。MySQL小心地得出相关的ID 值并利用它们自动地使各表的行相配。

学分保存方案涉及的另一项工作是汇总学生的缺勤情况。缺勤情况是按学生ID 和日期在absence 表中记录的。为得到学生名(而不仅仅是ID),我们需要根据student_id 的值将absence 表连接到student 表。下面的查询给出了学生的ID 号和名字以及缺勤计数:

注意:虽然我们在GROUP BY 子句中应用了一个限定符,但对于这个查询来说不是必须的。因为GROUP BY 子句只引用选择表中(此查询的前两行)的列。在该处只有一个名为student_id 的列,因此MySQL知道应该用哪个列。这个规则对ORDER BY 子句也成立。如果我们希望只了解哪些学生缺过勤,则此查询所产生的输出也是有用的。但是,如果我们将此清单交给学校办公室,他们可能会说,“其他的学生呢?我们需要每个学生的情况。”这是一个稍微有点不同的问题。它表示需要知道学生的缺勤数,即使没有缺勤的学生也需要知道。因为问题的不同,查询也应该不同。

为了解决上述问题,使用LEFT JOIN 而不涉及WHERE 子句中的学生ID。LEFT JOIN要求MySQL对从连接首先给出的表中选择每行生成一个输出行(即LEFT JOIN 关键字左边给出的表)。由于首先给出student 表,我们得到了每个学生的输出结果,即使是那些在absence 表中未给出的学生也都包括在输出中。此查询如下:

前面,在“生成汇总”一节中,我们执行了一个查询,它生成score 表中数据的数值特征。该查询的输出列出了事件ID,但不包括学分日期或类型,因为我们不知道怎样将score 表连接到event 表以得到学分的日期和类型。现在可以做到了。下面的查询类似于早先的那个,但是它给出了学分的日期和类型而不只是简单的数字事件ID:

可利用诸如COUNT( ) 和AVG( ) 这样的函数生成多个列上的汇总,即使这些列来自不同的表也是如此。下面的查询确定学分数,以及事件日期与学生性别的每种组合的平均学分。

我们可以使用一个类似的查询来完成学分保存方案的一个任务,即在学期末计算每个学生的总学分。相应的查询如下:

不一定要求连接必须用两个不同的表来完成。这似乎有点奇怪,但是确实可以将一个表连接到其自身。例如,可通过针对每个总统的出生地查看其他各个总统的出生地,确定几个总统是否出生在相同城市。此查询如下:

此查询有两个技巧性的东西:

■ 我们需要使用同一表的两个实例,因此建立了表的别名( p 1、p 2),并利用它们无歧义地引用表列。

■ 每个总统的记录与自身相匹配,但是我们不希望在输出中看到同一总统出再现两次。WHERE 子句的第二行保证比较的记录为不同总统的记录,使记录不与自身匹配。可以编写一个查找出生在同一天的总统的类似查询。出生日期不能直接比较,因为那样会错过出生在不同年份的总统。我们用MONTH( ) 和DAYOFMONTH( ) 来比较出生日期的月和日,相应的查询如下:

利用DAYOFYEAR( ) 而不是MONTH( ) 和DAYOFMONTH( ) 将得出一个更为简单的查询,但是在比较闰年日期与非闰年日期时将会得出不正确的结果。迄今所执行的连接结合了来自那些在某种意义上具有逻辑关系的表中的信息,但是只有您知道该关系无意义。MySQL并不知道(或不关心)所连接的表相互之间是否相关。例如,可将event 表连接到president 表以找出在某个总统生日那天是否进行了测验或测试,此查询如下:

它产生了您所想要的东西。但说明了什么呢?这说明MySQL将愉快地制造出结果,至于这些结果是否有意义它不管。这是因为您使用的是计算机,所以它不能自动地判断查询的结果有用或无用。无论如何,我们都必须为自己所做的事负责。

篇6:Solaris 系统维护数据库教程

5 系统维护

5.1 Solaris 系统

涉及的服务器 Account1(218.29.0.239), Account2(218.29.0.240), Oradb1(218.29.0.244) ,Oradb2(218.29.0.245)

5.1.1 系统概况

1. 操作系统基本信息: uname -a 将依次显示 操作系统名称,hostname,操作系统大版本信息,操作系统小版本信息,硬件类型,cpu类型,平台信息,

2. 内核信息:修改/etc/system 文件更改缺省的内核参数,modinfo显示模块信息,sysdef显示驱动和内核参数信息。

3. CPU 信息:/usr/sbin/psrinfo -v 显示cpu 状态,数量,cpu类型和主频。

4. 内存信息:prtconf | grep “Memory size” 显示内存大小。

5. 硬盘/光驱信息:iostat -En 显示硬盘和光驱的硬件地址,各种状态信息,型号及硬盘大小。可以通过该命令检查是否出现过各类错误。

6. 网卡信息:ifconfig -a ,显示网卡名称,状态,最大传输单元mtu,ip 地址,子网掩码,广播地址。

7. HA 信息:

8. 网络信息:netstat -rn 显示静态路由和缺省路由。

9. 包pkg 信息 :pkginfo -l pkgname。通过它判定某个pkg 是否安装,相关的还有pkgadd,pkgrm,pkgchk。

10. 补丁patch 信息: showrev -p 。安装某些软件需要特定的patch,通过它判断。

11. 文件系统信息: df -k 监测capacity 判断是否出现空间不足。

12. 用户信息:who 显示当前在线用户。whodo 显示当前用户进行的操作。 通过useradd,groupadd或admintool 进行用户的管理。id 显示当前用户的login name,login id, group name group name。

5.1.2系统监控

1. cpu 性能监控 :vmstat /mpstat 和sar ,vmstat 和sar 在SMP系统中显示汇总信息,而mpstat 可以显示每颗CPU的负载。vmstat 监控procs 下的r和b 数目,它反映目前处于运行和阻滞状态的进程数目,如果r数目长期大于cpu 数目同时cpu 下的us+sy >80,表示cpu处理能力不足。

2. 内存监控: vmstat ,监控memory 下的free和page下的pi/po,pi/po反映每秒钟以k为单位换进/换出的数量。如果free的数目很小同时pi/po数目长时间大于0 ,则表示内存不足。

3. 硬盘监控:iostat -d 和sar -d ,可以看到每块硬盘及硬盘的每个分区的工作情况

4. 交换区监控:swap -l 和swap -s ,应尽量将交换区的放置在负载较轻的硬盘上。同时交换区的使用百分比不应过高。

5. HA 系统监控:

6. 网络监控: netstat -in 显示每块网卡的工作负载情况。Ipkts/Opkts发送包和接受包的情况。Ierrs/oerrs,collis 表示发生的错误和冲突。

Ierrs/Ipkts或Oerrs/Opkts >2%,表示网络冲突严重。

7. 端口监控:监控某个服务是否启动或正常运行,

netstat -an |grep portnum 或tcpdump |grep portnum。

8. 硬件故障诊断:/usr/platform/`uname -m`/sbin/prtdiag -v 通过输出结果判定有无硬件故障。

9. syslogd日志监控,, syslogd后台进程将系统运行中的信息写入 /var/adm/messages 文件中,通过它来监控有无异常的软硬件错误信息的出现。

较早一点的信息存在同一目录下的messages.0,message.1 文件中。

10. mail 监控:某些用户通过crontab 定时执行任务,任务完成后,cron进程会给用户发mail,通过察看mail 判断任务是否完成。在系统出异常的情况下,syslogd会发给root用户mail。因此root 用户的mail需要特别注意。

11. 进程监控:通过ps,prstat 命令或/usr/dt/bin/sdtprocess 工具监控特别消耗资源的进程。sdtprocess是图形工具,可以按照cpu和mem的使用及执行时间排序。prstat 统计系统的活动进程并按需要的顺序排序。

12. 系统运行时间监控 :uptime衡量一个机器开时长及系统负载的命令。uptime 显示的时间越大,说明机器越稳定。

13. 系统运行级别监控:who -r 显示当前机器的运行级。

5.2 Linux 系统

涉及的前端机:Fep1(218.29.0.227), Fep2(218.29.0.228), Fep3(218.29.0.229), Fep4(218.29.0.230), Fep5(218.29.0.231), Fep6(218.29.0.232), Fep7(218.29.0.233),Fep8(218.29.0.234),Fep9(218.29.0.235),Fep10(218.29.0.236),Fep11(218.29.0.237),Fep12(218.29.0.238)

5.2.1系统概况

1. 操作系统基本信息: uname -a

2. 内核信息:/proc/sys/kernel 目录下的文件记录各个内核参数,modinfo显示模块信息,sysdef显示驱动和内核参数信息。

3. CPU 信息:。

4. 内存信息:prtconf | grep “Memory size” 显示内存大小。

5. 硬盘/光驱信息:。

6. 网卡信息:ifconfig -a ,显示网卡名称,状态,最大传输单元mtu,ip 地址,子网掩码,广播地址。

7. 网络信息:netstat -rn 显示静态路由和缺省路由。

8. 包pkg 信息 :rpm(Redhat Package Manager)。通过它判定某个pkg 是否安装,rpm -q 查询,rpm -i 安装

9. 补丁patch 信息:文件系统信息: df -k 监测capacity 判断是否出现空间不足。

10. 用户信息:who 显示当前在线用户。通过KDE的user manager工具添加用户。id 显示当前用户的login name,login id, group name group name。

5.2.1系统监控

1. Cpu 性能监控 :vmstat和sar 。vmstat 监控procs 下的r和b 数目,它反映目前处于运行和阻滞状态的进程数目,如果r数目长期大于cpu 数目同时cpu 下的us+sy >80,表示cpu处理能力不足。

2. 内存监控: vmstat ,监控memory 下的free和page下的pi/po,pi/po反映每秒钟以k为单位换进/换出的数量。如果free的数目很小同时pi/po数目长时间大于0 ,则表示内存不足。

3. 硬盘监控:iostat -d 和sar -d

4. 交换区监控swap -l

5. 网络监控 netstat -in

6. 端口监控:监控某个服务是否启动 netstat -an |grep portnum 或tcpdump |grep portnum

7. 硬件故障诊断:。

8. syslogd日志监控,, syslogd后台进程将系统运行中的信息写入 /var/adm/messages 文件中,通过它来监控有无异常的软硬件错误信息的出现。

较早一点的信息存在同一目录下的messages.0,message.1 文件中。

9. mail 监控:某些用户通过crontab 定时执行任务,任务完成后,cron进程会给用户发mail,通过察看mail 判断任务是否完成。在系统出异常的情况下,syslogd会发给root用户mail。因此root 用户的mail需要特别注意。

10. 进程监控:通过ps 或top监控特别消耗资源的进程。

6.数据库维护

6.1 数据库的安装

6.1.1 操作系统的准备工作

1. 内核参数的修改

编辑修改/etc/system 文件

set shmsys:shminfo_shmmax=4294967295

set shmsys:shminfo_shmmin=1

set shmsys:shminfo_shmmni=100

set shmsys:shminfo_shmseg=10

set semsys:seminfo_semmns=

set semsys:seminfo_semmsl=1000

set semsys:seminfo_semmni=100

set semsys:seminfo_semopm=100

set semsys:seminfo_semvmx=32767

2. 检查需要的操作系统patch

通过sunsolve.sun.com 上下载特定的版本的patch集并安装,可以满足Oracle 的安装需要。

3. 操作系统用户/组的添加

l groupadd dba

l groupadd oinstall

l useradd -c “Oracle DBA” -d /home/oracle -g oinstall -G dba

l 修改/etc/passwd文件 ,更改用户登陆shell 或其他信息

4. 用户环境变量的设定

6.1.2 数据库的安装

6.1.3数据库patch 安装

1. Patch 下载:Oracle 数据库的patch 从Oracle的metalink网站下载,登录成功后,在patch页面中的Product Family下拉框中选择“Oracle Databse”,Product 下拉框中选择“RDBMS Server”;Release 下拉框中 选择“Oracle 8.1.7.4”;Platform. 下拉框中选择“Sun Solaris OS( SPARC) 64-bit”,Language下拉框中选择“American English (US)” ,Limit Search to下拉框中选择“Latest Product Patches or Minipacks”,点击Submit提交。在随后出现的页面中点击Download下载patch。

2. Patch 安装:Patch 下载后首先需要解压缩, 安装与数据库安装方法类似,启动runInstaller安装程序,选择解压缩完成后patch目录下的product文件进行安装。特别注意patch安装时,数据库必须处于正常关闭状态。

3. 安装完成后检查:select * from v$version; 检查各个产品的版本号是否从8.1.7.0升级成为8.1.7.4。

6.2数据库基本信息汇总

1. 版本信息

SQL>select * from v$version;

2. 数据库名称,归档方式 状态

SQL>col name format a12

SQL>select name,LOG_MODE,open_mode from v$database;

3. 数据库安装选项

SQL>col parameter format a50

SQL>select * from v$option;

4. 数据库文件包括三类:控制文件control file, 日志文件log file , 数据文件datafile

l 控制文件

SQL>select name from v$controlfile;

l 日志文件名称及大小

col group# format 999     heading 'Group'

col member format a45 heading 'Member' justify c

col status format a10 heading 'Status' justify c

col archived format a10 heading 'Archived'

col fsize format 999 heading 'Size|(MB)'

select l.group#, member, archived, l.status, (bytes/1024/1024) fsize from   v$log l, v$logfile f where f.group# = l.group# order by 1 ;

l.数据文件名称,所在表空间及大小

col   FILE_NAME format a50 heading 'DataFile Name'

col   tablespace_name format a18 heading 'Related Tablespace'

select substr(FILE_ID,1,3) “ID#”,

FILE_NAME,

TABLESPACE_NAME,

BYTES/(1024*1024) “M”,

STATUS from sys.dba_data_files where tablespace_name'PERFSTAT' order by TABLESPACE_NAME, FILE_NAME;

5.表空间信息(大小 剩余空间 使用百分比)

SELECT dba.tablespace_name tablespace,

nvl(ROUND (used.bytes/(1024*1024), 0),0) “USED MB”,

nvl(ROUND (free.bytes/(1024*1024), 0),0) “FREE MB”,

ROUND ((nvl(used.bytes, 0)+nvl(free.bytes, 0))/(1024*1024), 0) total --,

--nvl(ROUND (used.bytes/(1024*1024), 0),0)/ROUND ((nvl(used.bytes, 0)+nvl(free.bytes, 0))/(1024*1024), 0) rate

from dba_tablespaces dba,

(select tablespace_name, sum(bytes) bytes

from dba_segments

group by tablespace_name) used,

(select tablespace_name, sum(bytes) bytes

from dba_free_space

Group by tablespace_name) free

where dba.tablespace_name = used.tablespace_name (+)

and dba.tablespace_name = free.tablespace_name (+)

and dba.tablespace_name'PERFSTAT'

order by dba.tablespace_name;

6.回滚段的信息察看回滚段名称,大小,所在表空间,以及状态,

select substr(sys.dba_rollback_segs.SEGMENT_ID,1,5) “ID#”,

substr(sys.dba_segments.OWNER,1,8) “Owner”,

substr(sys.dba_segments.TABLESPACE_NAME,1,17)“Tablespace Name”,

substr(sys.dba_segments.SEGMENT_NAME,1,17) “Rollback Name”,

substr(sys.dba_segments.BYTES,1,15) “Size (Bytes)”,

substr(sys.dba_segments.EXTENTS,1,6) “Extent#”,

substr(sys.dba_rollback_segs.STATUS,1,10) “Status”

from sys.dba_segments, sys.dba_rollback_segs

where sys.dba_segments.segment_name = sys.dba_rollback_segs.segment_name and

sys.dba_segments.segment_type = 'ROLLBACK' order by sys.dba_rollback_segs.segment_id;

7.数据库参数信息察看没有使用缺省值的参数

col name format a20

select name,value from v$parameter where isdefault='FALSE';

8.数据库用户信息检查是否有用户的 默认或临时表空间为system 表空间

select user_id,

substr(username,1,15) UserName,

substr(password,1,15) Password,

substr(DEFAULT_TABLESPACE,1,15) “Default TBS”,

substr(TEMPORARY_TABLESPACE,1,15) “Temporary TBS”,

CREATED, substr(profile,1,10) Profile

from sys.dba_users order by username;

6.3数据库优化及主要参数调整

Oracle数据库在启动时会读取命名规则为init.ora的初始化参数文件。这个配置文件中的各个参数设定对整个Oracle数据库性能起决定作用,先简单介绍各个参数的含义,然后给出建议配置。除特殊说明外,介绍参数的更改都需进行数据库的重启操作。根据数据库系统规模,Oracle提供小,中,大(small,medium,large)三个建议建议配置档,配置时做为参考。

l db_block_size: 数据块大小,数据存储的最小单位,这个参数在Oracle9i以前各个版本中,数据库创建完成后是不可改变的。可选范围是2K,4K,8K,16k,32k单位是byte。应用系统中,部分数据表字段较多,db_block_size设定偏小造成记录不能在单一数据块中存储,因此访问一条用户记录不能在一次物理I/O操作中完成,需要多次I/O,导致性能下降;另外较多的 varchar2类型字段和数据更新可能造成行链接和行迁移,除设定pctfree较大外,db_block_size也要设定大一些。建议配置为8K,创建数据库时指定。

l db_block_buffers: 数据高速块的多少,存放从硬盘中读出的数据块。db_block_buffers决定内存中可存放的大小为db_block_size的Oracle数据块的数量。应用程序第一次访问数据时从硬盘中读取,然后数据存储在内存中,直到数据长时间不被访问,同时内存中不能容纳更多数据时,数据被写回硬盘,下次需要访问时,再从硬盘中读取。该参数越大,Oracle在内存中找到所需数据的可能性就越大。相同数据内存读取速度要比硬盘快几十倍,因此响应速度也会大大提高。db_block_size与db_block_buffers乘积确定Oracle数据库所占内存的90%左右。 如果服务器专用运行数据库,建议两个参数的乘积占服务器物理内存的40%,通过对数据缓存的命中率进行监控后,做进一步调整。

l shared_pool_size:内存中存放sql语句访问路径,相关访问权限,表结构信息的位置,单位是字节。 建议使用默认配置的大(large)一档,为 9000000(字节)。通过sql语句监控生产机缓存命中率后进行相应调整。

l sort_area_size :可在内存中进行排序的最大字节数。应用程序向数据库发出类似orderby,group by 或创建索引时,需要进行排序操作。排序在内存中完成的速度同样快于硬盘中速度。sort_area_size越大,排序完成在内存中的可能也越大。建议使用默认配置的大(large)一档,大小为524288字节。通过对动态视图的监控进行进一步的调整。

l log_buffer: 日志缓存大小,对于数据库的dml/ddl 等语句首先写入log_buffer,然后写入日志文件。建议初始配置为512k,如果日志缓存等待明显,再加大,一般不超过为1M。

l processes: 同时访问Oracle数据库的进程数,包括后台进程(dbwr,lgwr,chpt)和Enus,Cnus 各个模块建立的到数据库的长连接数,设定大一点不会造成负面影响,如果偏小则会造成不能进行数据库连接的错误。建议配置为150。

l dml_locks: 它表示任何时间所有用户在所有表中放置锁定的最大数量。缺省值是事务最大数量的四倍。同样可以设定较大一点,不会有负面影响。建议为2000。

l open_cursors: 用户可以同时打开的游标数。设定大一点不会造成负面影响,如果偏小则会出现不能打开游表的错误。 建议配置为500。

l db_files: 数据库数据文件的数目。建议设定为200。

l rollback_segments:回滚段的名称,用户自己创建完回滚段后将名称添加在此处。在OLTP环境下,回滚段的数目为最大并发交易数的1/3~1/4。

6.4 数据库的备份/恢复

6.5数据库的监控/维护

6.5.1 推荐使用的监控方法

Statspack 的介绍: Statspack 是Oracle公司针对Oracle Enterprise Edition 8.1.6 及以上版本推出的性能监控工具包。用于替代原有的UTLBSTAT/UTLESTAT。比UTLBSTAT/UTLESTAT能够收集更多的数据,包括耗资源多的SQL。同时预先计算了许多对性能调优很有用的比值(ratios)。可以更加全面的监控数据的整体运行情况。同时可将生成的report文件发送给Oracle公司进行协助分析。

监控的范围主要是:

l 主要内存参数的大小(db_block_buffers, db_block_size, log_buffer, shared_pool_size)。

l 系统负载情况:每秒钟的生成的日志量,数据库的逻辑读,物理读,交易数量等。

l 各类缓存命中率。如库缓存命中率,字典缓存命中率,db block buffer命中率,排序在内存中进行的比率等。

l 造成系统响应时间慢的几类事件,通过优化这几类事件提高系统响应速度。

l 按照各类消耗资源排序的SQL语句。通常系统响应速度慢除与参数设置有关外,还与书写不规范的SQL语句有关。通过优化捕捉到的SQL语句,提高响应速度。

l 各个表空间及组成表空间各个文件的I/O负载情况。通过这部分内容判断各个文件I/O是否均衡。

l 回滚段信息:反映是否有回滚段的竞争,是否有回滚段的过分扩展。

l 所有没有按缺省设置的参数。

Statspack软件包的创建:

l 安装文件:Unix 操作系统下,安装文件存放在$ORACLE_HOME/rdbms/admin 目录下。

l 创建时的执行的用户和执行的文件

SQL>connect internal ;

SQL>@?/rdbms/admin/spcreate.sql;

l 安装过程中的主要动作:

1安装生成一个用户perfstat/perfstat =>spcusr.sql,

安装时输入:DEFAULT TABLESPACE/ TEMPORARY TABLESPACE

2建立需要的Table   =>spctab.sql

3生成dbms_shared_pool 和dbms_job PL/SQL 程序包=>spcpkg.sql

Statspack软件包的执行/数据采集

当系统响应速度出现明显下降的时候,可通过statspack进行系统运行数据采集,并生成报告文件,进行分析。

l 初始准备:在init.ora中设置timed_statistics=true,并让其生效。或使用alter system set timed_statistics=true;

l 使用创建的perfstat用户连接

SQL>CONNECT PERFSTAT/PERFSTAT;

l 执行第一次采集程序:

SQL>EXECUTE STATSPACK.SNAP;l

篇7:触发器概述数据库教程

在上面几节我们介绍了一般意义的存储过程,即用户自定义的存储过程和系统存储过程,

触发器概述数据库教程

。本节将介绍一种特殊的存储过程,即触发器。在余下各节中我们将对触发器的概念、作用以及对其的使用方法作详尽介绍,使读者了解如何定义触发器,创建和使用各种不同复杂程度的触发器。

12.5.1 触发器的概念及作用

触发器是一种特殊类型的存储过程,它不同于我们前面介绍过的存储过程。触发器主要是通过事件进行触发而被执行的,而存储过程可以通过存储过程名字而被直接调用。当对某一表进行诸如UPDATE、INSERT、DELETE 这些操作时,SQL Server 就会自动执行触发器所定义的SQL 语句,从而确保对数据的处理必须符合由这些SQL 语句所定义的规则。

触发器的主要作用就是其能够实现由主键和外键所不能保证的复杂的参照完整性和数据的一致性。除此之外,触发器还有其它许多不同的功能:

(1) 强化约束(Enforce restriction)

触发器能够实现比CHECK 语句更为复杂的约束。

(2) 跟踪变化Auditing changes

触发器可以侦测数据库内的操作,从而不允许数据库中未经许可的指定更新和变化。

(3) 级联运行(Cascaded operation)。

触发器可以侦测数据库内的操作,并自动地级联影响整个数据库的各项内容。例如,某个表上的触发器中包含有对另外一个表的数据操作(如删除,更新,插入)而该操作又导致该表上触发器被触发。

(4) 存储过程的调用(Stored procedure invocation),

为了响应数据库更新触,发器可以调用一个或多个存储过程,甚至可以通过外部过程的调用而在DBMS( 数据库管理系统)本身之外进行操作。

由此可见,触发器可以解决高级形式的业务规则或复杂行为限制以及实现定制记录等一些方面的问题。例如,触发器能够找出某一表在数据修改前后状态发生的差异,并根据这种差异执行一定的处理。此外一个表的同一类型(INSERT、UPDATE、DELETE)的多个触发器能够对同一种数据操作采取多种不同的处理。

总体而言,触发器性能通常比较低。当运行触发器时,系统处理的大部分时间花费在参照其它表的这一处理上,因为这些表既不在内存中也不在数据库设备上,而删除表和插入表总是位于内存中。可见触发器所参照的其它表的位置决定了操作要花费的时间长短。

12.5.2 触发器的种类

SQL Server 2000 支持两种类型的触发器:AFTER 触发器和INSTEAD OF 触发器。其中AFTER 触发器即为SQL Server 2000 版本以前所介绍的触发器。该类型触发器要求只有执行某一操作(INSERT UPDATE DELETE) 之后,触发器才被触发,且只能在表上定义。可以为针对表的同一操作定义多个触发器。对于AFTER 触发器,可以定义哪一个触发器被最先触发,哪一个被最后触发,通常使用系统过程sp_settriggerorder 来完成此任务。

INSTEAD OF 触发器表示并不执行其所定义的操作(INSERT、UPDATE、DELETE),而仅是执行触发器本身。既可在表上定义INSTEAD OF 触发器,也可以在视图上定义INSTEAD OF 触发器,但对同一操作只能定义一个INSTEAD OF 触发器。

篇8:Python数据库操作手册数据库教程

数据|数据库

数据库的操作在现在的Python里面已经变得十分的好用,有了一套API标准.下面的就是讲讲如何的去使用这套框架定义.此框架包含以下部分模块接口 连接对象 游标对象 DBI辅助对象 数据类型与定义 如何实现的提示 从1.0到2.0的变化 例子 模块接口

connect(parameters...) 其中的参数格式如下:

dsn 数据源名称user用户名(可选)password 密码(可选)host主机名(可选)database 数据库名(可选)举个例子: connect(dsn='myhost:MYDB',user='guido',password='234$')又或者 connect('218.244.20.22','username','password','databasename')

此标准规定了以下的一些全局变量:

apilevel:

表示了DB-API的版本,分'1.0'和'2.0'.如果没有定义,默认为'1.0'

threadsafety:

0 Threads may not share the module.1 Threads may share the module, but not connections.2 Threads may share the module and connections.3 Threads may share the module, connections and cursors.

paramstyle.:

用于表示参数的传递方法,分为以下五种:'qmark' 问号标识风格. e.g '... WHERE name=?''numeric' 数字,占位符风格. e.g '... WHERE name=:1''named' 命名风格. e.g 'WHERE name=:name''format' ANSI C printf风格. e.g '... WHERE name=%s''pyformat' Python扩展表示法. e.g '... WHERE name=%(name)s'

异常类:

StandardError|__Warning|__Error |__InterfaceError |__DatabaseError |__DataError |__OperationalError |__IntegerityError |__InternalError |__ProgrammingError |__NotSupportedError

连接对象

连接对象包含如下方法:

.close 关闭连接 .commit() 用于事务处理里面的提交操作 .rollback() 用于事务处理里面的回滚操作 .cursor() 获得一个游标 游标对象

游标对象包含如下属性和方法:

.description 一个列表(name,type_code,display_size,internal_size,precision,scale,null_ok) 此属性只有在取得了数据之后才有,不然会是null值 .rowcount 表示返回值的行数.如果没有执行executeXXX()方法或者此模块没有实现这个方法,就会返回-1 .callproc(procname[,parameters]) (此为可选方法,应为不是所有的数据库都支持存储过程的) .close() 关闭游标 .execute(operation[,parameters]) 准备并执行一个数据库操作(包括查询和命令) .executemany(operation,seq_of_parameters) 准备一个数据库命令,然后根据参数执行多次命令 .fetchone() 返回第一行的查询结果 .fetchmany([size=cursor.arraysize]) 返回指定个多个行的值 .fetchall() 返回所有的查询结果 .arraysize 这个参数值表示fetchmany默认情况之下获取的行数 数据类型与定义定义一些常用的数据类型.但是目前用不到,就先不分析备注

当然,我们要知道的是,这个只是一个标准,一般来说标准里面定义了的会实现,但还有很多特定的实现,我们也需要去掌握哪些东西,不过如果我们将这些标准的掌握了,那么操作一般的就不会有问题了.

下面给出几个数据库相关的网址

Database Topic Guide Python的数据库使用向导,有相当不错的资料,包括API定义,驱动联结等等 MSSQL 驱动 就是MSSQL的驱动程序 例子

下面举的例子是以MSSQL为样板的,但是换成其他的驱动也一样可以做,这个就和Perl的数据库操作十分的类似,可以让我们很方便的实现不同数据库之间的移植工作.

1. 查询数据

import MSSQLdb = MSSQL.connect('SQL Server IP', 'username', 'password', 'db_name')c = db.cursor()sql = 'select top 20 rtrim(ip), rtrim(dns) from detail'c.execute(sql)for f in c.fetchall(): print “ip is %s, dns is %s” % (f[0], f[1])

2. 插入数据

sql = 'insert into detail values('192.168.0.1', 'www.dns.com.cn')c.execute(sql)

3. ODBC的一个例子

import dbi, odbc # ODBC modulesimport time # standard time moduledbc = odbc.odbc( # open a database connection 'sample/monty/spam' # 'datasource/user/password' )crsr = dbc.cursor() # create a cursorcrsr.execute( # execute some SQL “”“ SELECT country_id, name, insert_change_date FROM country ORDER BY name ”“” )print 'Column descriptions:' # show column descriptionsfor col in crsr.description: print ' ', colresult = crsr.fetchall()# fetch the results all at onceprint '\\nFirst result row:\\n ', result[0] # show first result rowprint '\\nDate conversions:' # play with dbiDate bjectdate = result[0][-1]fmt = ' %-25s%-20s'print fmt % ('standard string:', str(date))print fmt % ('seconds since epoch:', float(date))timeTuple = time.localtime(date)print fmt % ('time tuple:', timeTuple)print fmt % ('user defined:', time.strftime('%d %B %Y', timeTuple))-------------------------------output--------------------------------Column descriptions: ('country_id', 'NUMBER', 12, 10, 10, 0, 0) ('name', 'STRING', 45, 45, 0, 0, 0) ('insert_change_date', 'DATE', 19, 19, 0, 0, 1)First result row: (24L, 'ARGENTINA', )Date conversions: standard string: Fri Dec 19 01:51:53 seconds since epoch: 882517913.0 time tuple: (1997, 12, 19, 1, 51, 53, 4, 353, 0) user defined:19 December 1997

回本栏首页

篇9:关于dirty buffer数据库教程

其实大家对v$bh这个视图可能一直重视不够.

我们来看一下这个视图主要字段说明:

STATUS

VARCHAR2(1)

Status of the buffer:

FREE - not currently in use XCUR - exclusive SCUR - shared current CR - consistent read READ - being read from disk MREC - in media recovery mode IREC - in instance recovery mode ?

DIRTY

VARCHAR2(1)

Y - block modified

TEMP

VARCHAR2(1)

Y - temporary block

PING

VARCHAR2(1)

Y - block pinged

STALE

VARCHAR2(1)

Y - block is stale

DIRECT

VARCHAR2(1)

Y - direct block

?

我们注意到v$dirty代表的就是block modified

?

如果我们进一步向下追溯:

SQL>select VIEW_DEFINITION from v$fixed_view_definition where VIEW_NAME = 'GV$BH';

VIEW_DEFINITION

--------------------------------------------------------------------------------

select bh.inst_id, file#, dbablk, class, decode(state,0,'free',1,'xcur',2,'scur'

,3,'cr', 4,'read',5,'mrec',6,'irec',7,'write',8,'pi'), x_to_null, forced_reads,

forced_writes, bh.le_addr, name,le_class, decode(bitand(flag,1), 0, 'N', 'Y'), d

ecode(bitand(flag,16), 0, 'N', 'Y'), decode(bitand(flag,1536), 0, 'N', 'Y'), dec

ode(bitand(flag,16384), 0, 'N', 'Y'), decode(bitand(flag,65536), 0, 'N', 'Y'), '

N', obj, ts# from x$bh bh, x$le le where bh.le_addr = le.le_addr (+)

我们可以看到v$bh的底层表示x$bh

其中v$bh中的dirty/temp/ping/stale/direct五个字段来源于x$bh中的flag字段

格式化一下输出:

SELECT bh.inst_id, file#, dbablk, CLASS,

?????? DECODE (state,

?????????????? 0, 'free',

?????????????? 1, 'xcur',

?????????????? 2, 'scur',

?????????????? 3, 'cr',

?????????????? 4, 'read',

?????????????? 5, 'mrec',

?????????????? 6, 'irec',

?????????????? 7, 'write',

?????????????? 8, 'pi'

????????????? ),

?????? x_to_null, forced_reads, forced_writes, bh.le_addr, NAME, le_class,

?????? DECODE (BITAND (flag, 1), 0, 'N', 'Y'),

?????? DECODE (BITAND (flag, 16), 0, 'N', 'Y'),

?????? DECODE (BITAND (flag, 1536), 0, 'N', 'Y'),

?????? DECODE (BITAND (flag, 16384), 0, 'N', 'Y'),

?????? DECODE (BITAND (flag, 65536), 0, 'N', 'Y'), 'N', obj, ts#

? FROM x$bh bh, x$le le

?WHERE bh.le_addr = le.le_addr(+)

由此我们得以窥视x$bh.flag的深层含义.