赞
踩
SQL语言快速入门之一...................................................................................... 2
SQL语言快速入门之二...................................................................................... 3
SQL语言快速入门之三...................................................................................... 5
SQL数据操作基础(初级)1............................................................................... 10
SQL数据操作基础(初级)2............................................................................... 13
SQL数据操作基础(初级)3............................................................................... 16
SQL数据操作基础(初级)4............................................................................... 20
SQL数据操作基础(初级)5............................................................................... 25
SQL数据操作基础(中级)1............................................................................... 27
SQL数据操作基础(中级)2............................................................................... 28
SQL数据操作基础(中级)3............................................................................... 31
SQL数据操作基础(中级)4............................................................................... 35
SQL数据操作基础(中级)5............................................................................... 39
SQL是英文Structured Query Language的缩写,意思为结构化查询语言。SQL语言的主要功能就是同各种数据库建立联系,进行沟通。按照ANSI(美国国家标准协会)的规定,SQL被作为关系型数据库管理系统的标准语言。SQL语句可以用来执行各种各样的操作,例如更新数据库中的数据,从数据库中提取数据等。目前,绝大多数流行的关系型数据库管理系统,如Oracle, Sybase, Microsoft SQL Server, Access等都采用了SQL语言标准。虽然很多数据库都对SQL语句进行了再开发和扩展,但是包括Select, Insert, Update, Delete, Create,以及Drop在内的标准的SQL命令仍然可以被用来完成几乎所有的数据库操作。下面,我们就来详细介绍一下SQL语言的基本知识。
数据库表格
一个典型的关系型数据库通常由一个或多个被称作表格的对象组成。数据库中的所有数据或信息都被保存在这些数据库表格中。数据库中的每一个表格都具有自己唯一的表格名称,都是由行和列组成,其中每一列包括了该列名称,数据类型,以及列的其它属性等信息,而行则具体包含某一列的记录或数据。以下,是一个名为天气的数据库表格的实例。
城市 最高气温 最低气温
北京 10 5
上海 15 8
天津 8 2
重庆 20 13
该表格中“城市”, “最高气温”和“最低气温”就是三个不同的列,而表格中的每一行则包含了具体的表格数据。
数据查询
在众多的SQL命令中,select语句应该算是使用最频繁的。Select语句主要被用来对数据库进行查询并返回符合用户查询标准的结果数据。Select语句的语法格式如下:
select column1[, column2,etc] from tablename
[wherecondition];
([]表示可选项)
select语句中位于select关键词之后的列名用来决定那些列将作为查询结果返回。用户可以按照自己的需要选择任意列,还可以使用通配符“*”来设定返回表格中的所有列。
select语句中位于from关键词之后的表格名称用来决定将要进行查询操作的目标表格。
Select语句中的where可选从句用来规定哪些数据值或哪些行将被作为查询结果返回或显示。
在where条件从句中可以使用以下一些运算符来设定查询标准:
=等于
>大于
<小于
>=大于等于
<=小于等于
<>不等于
除了上面所提到的运算符外,LIKE运算符在where条件从句中也非常重要。LIKE运算符的功能非常强大,通过使用LIKE运算符可以设定只选择与用户规定格式相同的记录。此外,我们还可以使用通配符“%”用来代替任何字符串。举例如下:
selectfirstname, lastname, city
fromemployee
wherefirstname LIKE ‘E%’;
(注意,字符串必须被包含在单括号内)
上述SQL语句将会查询所有名称以E开头的姓名。或者,通过如下语句:
select *from employee
wherefirstname = ‘May’;
查询所有名称为May的行。
创建表格
SQL语言中的create table语句被用来建立新的数据库表格。Create table语句的使用格式如下:
create tabletablename
(column1data type,
column2 datatype,
column3 datatype);
如果用户希望在建立新表格时规定列的限制条件,可以使用可选的条件选项:
create tabletablename
(column1data type [constraint],
column2 datatype [constraint],
column3 datatype [constraint]);
举例如下:
create tableemployee
(firstnamevarchar(15),
lastnamevarchar(20),
agenumber(3),
addressvarchar(30),
cityvarchar(20));
简单来说,创建新表格时,在关键词create table后面加入所要建立的表格的名称,然后在括号内顺次设定各列的名称,数据类型,以及可选的限制条件等。注意,所有的SQL语句在结尾处都要使用“;”符号。
使用SQL语句创建的数据库表格和表格中列的名称必须以字母开头,后面可以使用字母,数字或下划线,名称的长度不能超过30个字符。注意,用户在选择表格名称时不要使用SQL语言中的保留关键词,如select, create, insert等,作为表格或列的名称。
数据类型用来设定某一个具体列中数据的类型。例如,在姓名列中只能采用varchar或char的数据类型,而不能使用number的数据类型。
SQL语言中较为常用的数据类型为:
char(size):固定长度字符串,其中括号中的size用来设定字符串的最大长度。Char类型的最大长度为255字节。
varchar(size):可变长度字符串,最大长度由size设定。
number(size):数字类型,其中数字的最大位数由size设定。
Date:日期类型。
number(size,d):数字类型,size决定该数字总的最大位数,而d则用于设定该数字在小数点后的位数。
最后,在创建新表格时需要注意的一点就是表格中列的限制条件。所谓限制条件就是当向特定列输入数据时所必须遵守的规则。例如,unique这一限制条件要求某一列中不能存在两个值相同的记录,所有记录的值都必须是唯一的。除unique之外,较为常用的列的限制条件还包括not null和primary key等。Not null用来规定表格中某一列的值不能为空。Primary key则为表格中的所有记录规定了唯一的标识符。
向表格中插入数据
SQL语言使用insert语句向数据库表格中插入或添加新的数据行。Insert语句的使用格式如下:
insert intotablename
(first_column,...last_column)
values(first_value,...last_value);
例如:
insert intoemployee
(firstname,lastname, age, address, city)
values (‘Li’, ‘Ming’, 45, ‘No.77 Changan Road’, ‘Beijing”);
简单来说,当向数据库表格中添加新记录时,在关键词insert into后面输入所要添加的表格名称,然后在括号中列出将要添加新值的列的名称。最后,在关键词values的后面按照前面输入的列的顺序对应的输入所有要添加的记录值。
更新记录
SQL语言使用update语句更新或修改满足规定条件的现有记录。Update语句的格式为:
updatetablename
setcolumnname = newvalue [, nextcolumn = newvalue2...]
wherecolumnname OPERATOR value [and|or column OPERATOR value];
例如:
updateemployee
set age =age+1
wherefirst_name= ‘Mary’and last_name= ‘Williams’;
使用update语句时,关键一点就是要设定好用于进行判断的where条件从句。
删除记录
SQL语言使用delete语句删除数据库表格中的行或记录。Delete语句的格式为:
delete fromtablename
wherecolumnname OPERATOR value [and|or column OPERATOR value];
例如:
delete fromemployee
wherelastname = May;
简单来说,当需要删除某一行或某个记录时,在delete from关键词之后输入表格名称,然后在where从句中设定删除记录的判断条件。注意,如果用户在使用delete语句时不设定where从句,则表格中的所有记录将全部被删除。
删除数据库表格
在SQL语言中使用drop table命令删除某个表格以及该表格中的所有记录。Drop table命令的使用格式为:
drop tabletablename;
例如:
drop tableemployee;
如果用户希望将某个数据库表格完全删除,只需要在drop table命令后输入希望删除的表格名称即可。Drop table命令的作用与删除表格中的所有记录不同。删除表格中的全部记录之后,该表格仍然存在,而且表格中列的信息不会改变。而使用drop table命令则会将整个数据库表格的所有信息全部删除。
以上,我们对SQL语言主要的命令和语句进行了较为详细的介绍。应该说SQL语句的语法结构和风格还是相当简单和直观的,只要用户结合实践多加练习,一定会在短期内迅速掌握。
我们日常使用SQL语言的工作过程中,使用最多的还是从已经建立好的数据库中查询信息。下面,我们就来详细介绍一下如何使用SQL语言实现各种数据库查询操作。
SELECT…FROM
为方便讲解,我们在数据库中创建名为Store_Information的如下数据表。
Store_Information
Store_Name
Sales
Date
Los Angeles
$1500
Jan-10-2000
San Diego
$250
Jan-11-2000
Los Angeles
$300
Jan-12-2000
Boston
$700
Jan-12-2000
SQL语言中用于数据库查询的最简单的命令就是SELECT…FROM,语法格式为:
SELECT"column_name" FROM "table_name"
例如,如果我们希望查询Store_Information数据表中所有的商店名称时,可以使用如下命令:
SELECTstore_name FROM Store_Information
查询结果显示为:
Store_Name
Los Angeles
San Diego
Los Angeles
Boston
如果用户希望一次查询多个字段,可以将所要查询的字段名称依次加入SELECT关键字之后,中间用“,”隔开即可。
DISTINCT
SELECT关键字支持用户查询数据表中指定字段的所有数据,但是这样有时就会不可避免的出现重复信息。如果用户希望只查询那些具有不同记录值的信息的话,可以使用SQL语言的DISTINCT关键字。语法格式如下:
SELECTDISTINCT "column_name"
FROM "table_name"
例如,我们可以使用以下命令查询Store_Information数据表具有不同记录值的所有记录。
SELECTDISTINCT Store_Name FROM Store_Information
查询结果如下:
Store_Name
Los Angeles
San Diego
Boston
WHERE
除了选择具有不同记录值的记录之外,有时我们可能还会需要根据某些条件对数据库中的数据进行查询。例如,我们可能需要查询Store_Information数据表中销售额超过1000美圆的商店。为此,我们可以使用SQL语言的WHERE关键字设定查询条件。语法格式如下:
SELECT"column_name"
FROM"table_name"
WHERE"condition"
由此,我们可以使用如下命令查询销售额超过1000美圆的商店信息:
SELECTstore_name FROM Store_Information WHERE Sales > 1000
查询结果显示为:
store_name
Los Angeles
运算函数
现在,我们已经了解到在使用SQL语言进行数据库查询操作时可以通过对数值的判断设定灵活的查询条件。为了增强对运算的支持能力,SQL提供了众多实用的运算函数供广大用户使用。例如,我们可以直接在SQL命令中调用SUM或AVG这两个分别用于计算总数和平均数的函数。语法格式如下:
SELECT"function type"("column_name")
FROM"table_name"
如果我们希望查询Store_Information数据表中所有商店的总销售额的话,可以使用如下命令:
SELECTSUM(Sales) FROM Store_Information
查询结果显示为:
SUM(Sales)
$2750
COUNT
除了SUM和AVG函数之外,COUNT函数是SQL语言中另一个较为常用的运算函数。COUNT函数可以用来计算数据表中指定字段所包含的记录数目。语法格式为:
SELECTCOUNT("column_name")
FROM"table_name"
例如,如果我们希望查询Store_Information数据表中的有关商店的记录条数时,可以使用如下命令:
SELECTCOUNT(store_name)
FROMStore_Information
查询结果显示为:
Count(store_name)
4
COUNT函数可以和DISTINCT关键字一起使用从而可以查询数据表中指定字段中所有具有不同记录值的记录数目。例如,如果我们希望查询Store_Information数据表中不同商店的数目时,可以使用如下命令:
SELECTCOUNT(DISTINCT store_name)
FROMStore_Information
查询结果显示为:
Count(DISTINCTstore_name)
3
GROUP BY
下面我们来进一步看一下SQL语言中的集合函数。上文中,我们曾使用SUM函数计算所有商店的销售总额,如果我们希望计算每一家商店各自的总销售额时该怎么办呢?要实现这一目的我们需要做两件事:首先,我们需要查询商店名称和销售额两个字段;然后,我们使用SQL语言的GROUP BY命令将销售额按照不同的商店进行分组,从而计算出不同商店的销售总额。GROUP BY命令的语法格式为:
SELECT"column_name1", SUM("column_name2")
FROM"table_name"
GROUP BY"column_name1"
我们可以使用如下命令实现上述查询目的:
SELECTstore_name, SUM(Sales)
FROMStore_Information
GROUP BYstore_name
查询结果显示为:
store_nameSUM(Sales)
Los Angeles$1800
San Diego$250
Boston $700
小注:
GROUP BY关键字一般应用于同时查询多个字段并对字段进行算术运算的SQL命令中。
HAVING
用户在使用SQL语言的过程中可能希望解决的另一个问题就是对由sum或其它集合函数运算结果的输出进行限制。例如,我们可能只希望看到Store_Information数据表中销售总额超过1500美圆的商店的信息,这时我们就需要使用HAVING从句。语法格式为:
SELECT"column_name1", SUM("column_name2")
FROM"table_name"
GROUP BY"column_name1"
HAVING(arithematic function condition)
(GROUP BY从句可选)
由此,我们可以使用如下命令实现上述查询目的:
SELECTstore_name, SUM(sales)
FROMStore_Information
GROUP BYstore_name
HAVINGSUM(sales) > 1500
查询结果显示为:
store_nameSUM(Sales)
Los Angeles$1800
小注:
SQL语言中设定集合函数的查询条件时使用HAVING从句而不是WHERE从句。通常情况下,HAVING从句被放置在SQL命令的结尾处。
ALIAS
下面,我们重点介绍一下如何在SQL命令中设定别名。SQL语言中一般使用两种类型的别名,分别为字段别名和数据表别名。
简单的说,使用字段别名可以帮助我们有效的组织查询的输出结果。例如,上文所列举的多个实例中,当我们计算商店销售总额时,显示结果中就会出现SUM(sales)。虽然SUM(sales)并不会对我们理解查询结果带来不便,但是如果我们需要在查询中使用多项复杂运算时,显示结果就不会这么直观了。如果这时我们使用字段别名就会极大的提高查询结果的可读性。
对于数据表别名,我们可以通过将别名直接放置在FROM从句中数据表名称的后面设定。数据表别名在我们下面将要讲述的连接多个数据表进行查询的操作中极为有用。
字段和数据表别名的语法格式如下:
SELECT"table_alias"."column_name1" "column_alias"
FROM"table_name" "table_alias"
即别名都直接放置在各自对应名称的后面,中间用空格分开。
以Store_Information数据表为例,我们可以在GROUP BY一节中所使用的SQL命令中设置如下字段和数据表别名:
SELECTA1.store_name Store, SUM(Sales) "Total Sales"
FROMStore_Information A1
GROUP BYA1.store_name
查询结果显示为:
Store TotalSales
Los Angeles$1800
San Diego$250
Boston $700
连接多个数据表
最后,我们来看一下如果使用SQL语言连接多个数据表,实现对多个数据表的查询。为方便讲解,我们在数据库中分别创建了两个名为Store_Information和Region的数据表。
Store_Information
Store_Name
Sales
Date
Los Angeles
$1500
Jan-10-2000
San Diego
$250
Jan-11-2000
Los Angeles
$300
Jan-12-2000
Boston
$700
Jan-12-2000
Region
Region_Name
Store_Name
East
Boston
East
New York
West
Los Angeles
West
San Diego
下面,我们就来看一下通过数据表的连接实现按不同区域查询销售额。
我们注意到在名为Region的数据表中包含区域和商店两个字段信息,而在名为Store_Information的数据表中则包含每一家商店的销售信息。因此,为了得到按区域划分的销售信息,我们需要将两个不同数据表的信息结合在一起进行查询。通过对上述两个数据表的分析,我们发现每个数据表中都包含一个名为Store_Name的字段,因此,我们可以使用如下命令实现查询目的:
SELECTA1.region_name REGION, SUM(A2.Sales) SALES
FROMGeography A1, Store_Information A2
WHEREA1.store_name = A2.store_name
GROUP BYA1.region_name
查询结果显示为:
REGION SALES
East $700
West $2050
说明:
上述查询命令的前两行用于指定所要查询的目标字段,分别为Region数据表中的Region_Name字段和Store_Information数据表中Sales字段的记录值总数。这里,我们设定两个字段的别名分别为REGION和SALES,两个数据表的别名分别为A1和A2。如果我们只使用字段别名而不设定数据表别名的话,上述SQL命令的第一行就变成 如下形式:
SELECTRegion.Region_Name REGION, SUM(Store_Information.Sales) SALES
由此我们可以看出有效的使用数据表别名,可以极大的简化对多个数据表进行操作的SQL命令。
上述查询命令的第3行为WHERE从句,正是该从句设定了两个数据表的连接条件。因为我们希望确保Region数据表中的Store_Name字段能够与Store_Information数据表中的同名字段相对应,所以我们规定两个字段的记录值应当相等。在连接多个数据表时,一定要准确设定数据表的连接条件,如果WHERE从句设定不正确,则可能导致查询结果中出现众多不相关的数据
为了建立交互站点,你需要使用数据库来存储来自访问者的信息。例如,你要建立一个职业介绍服务的站点,你就需要存储诸如个人简历,所感兴趣的工作等等这样的信息。创建动态网叶也需要使用数据库,如果你想显示符合来访者要求的最好的工作,你就需要从数据库中取出这份工作的信息。你将会发现,在许多情况下需要使用数据库。
在这一章里,你将学会怎样使用“结构化查询语言”(SQL〕来操作数据库。SQL语言是数据库的标准语言。在Active SeverPages中,无论何时你要访问一个数据库,你就要使用SQL语言。因此,掌握好SQL对ASP编程是非常重要的。
注意:
你可以把“SQL”读作“sequel”,也可以按单个字母的读音读作S-Q-L。 两种发音都是正确的,每种发音各有大量的支持者。在本书里,认为“SQL”读作“sequel”。
通过这一章的学习,你将理解怎样用SQL实现数据库查询,你将学会怎样使用这种查询从数据表中取出信息,最后,你将学会怎样设计和建立自己的数据库。
注意:
通过下面几章对SQL的介绍,你将对SQL有足够的了解,从而可以有效地使用Active Sever Pages。但是,SQL是一种复杂的语言,本书不可能包括它的全部细节。要全面掌握SQL语言,你需要学习在Microsoft SQL Sever中使用SQL。你可以到附近的书店去买一本Microsoft SQL Sever 6.5。
SQL介绍:
本书假设你是在SQL操作Microsoft SQL Sever的数据库。你也可以用SQL操作许多其它类型的数据库。SQL是操作数据库的标准语言。(事实上,关于SQL语言有一个专门的ANSI标准〕
注意:
不要在你的站点上试图用Microsoft Access代替Microsoft SQL Sever。SQL Sever可以同时服务于许多用户,如果你希望你的站点有较高的访问率,MSAccess是不能胜任的。
在学习SQL的细节之前,你需要理解它的两大特点。一个特点容易掌握,另一个掌握起来有点困难。
第一个特点是所有SQL数据库中的数据都存储在表中。一个表由行和列组成。例如,下面这个简单的表包括name和e-mail
address:
Name EmailAddress
................................................................
Bill Gatesbillg@microsoft.com
presidentClinton president@whitehouse.com
StephenWalther swalther@somewhere.com
这个表有两列(列也称为字段,域〕:Name和Email Address。有三行,每一行包含一组数据。一行中的数据组合在一起称为一条记录。
无论何时你向表中添加新数据,你就添加了一条新记录。一个数据表可以有几十个记录,也可以有几千甚至几十亿个记录。虽然你也许永远不需要存储十亿个Email地址,但知道你能这样做总是好的,也许有一天你会有这样的需要。
你的数据库很有可能包含几十个表,所有存储在你数据库中的信息都被存储在这些表中。当你考虑怎样把信息存储在数据库中时,你应该考虑怎样把它们存储在表中。
SQL的第二个特点有些难于掌握。这种语言被设计为不允许你按照某种特定的顺序来取出记录,因为这样做会降低SQL Sever取记录的效率。使用SQL,你只能按查询条件来读取记录。
当考虑如何从表中取出记录时,自然会想到按记录的位置读取它们。例如,也许你会尝试通过一个循环,逐个记录地扫描,来选出特定的记录。在使用SQL时,你必须训练自己,不要有这种思路。
假如你想选出所有的名字是“Bill Gates”的记录,如果使用传统的编程语言,你也许会构造一个循环,逐个查看表中的记录,看名字域是否是“Bill Gates”。
这种选择记录的方法是可行的,但是效率不高。使用SQL,你只要说,“选择所有名字域等于BillGates的记录”,SQL就会为你选出所有符合条件的记录。SQL会确定实现查询的最佳方法。
建设你想取出表中的前十个记录。使用传统的编程语言,你可以做一个循环,取出前十个记录后结束循环。但使用标准的SQL查询,这是不可能实现的。从SQL的角度来说,在一个表中不存在前十个记录这种概念。
开始时,当你知道你不能用SQL实现某些你感觉应该能实现的功能,你会受到挫折。你也许会以头撞墙甚至想写恶毒的信件给SQL的设计者们。但后来你会认识到,SQL的这个特点不仅不是个限制,反而是其长处。因为SQL不根据位置来读取记录,它读取记录可以很快。
综上所述,SQL有两个特点:所有数据存储在表中,从SQL的角度来说,表中的记录没有顺序。在下一节,你将学会怎样用SQL从表中选择特殊的记录。
使用SQL从表中取记录。
SQL的主要功能之一是实现数据库查询。如果你熟悉Internet引擎,那么你已经熟悉查询了。你使用查询来取得满足特定条件的信息。例如,如果你想找到有ASP信息的全部站点,你可以连接到 Yahoo!并执行一个对Active Sever Pages的搜索。在你输入这个查询后,你会收到一个列表,表中包括所有其描述中包含搜索表达式的站点。
多数Internet引擎允许逻辑查询。在逻辑查询中,你可以包括特殊的运算符如AND、OR和NOT,你使用这些运算符来选择特定的记录。例如,你可以用AND来限制查询结果。如果你执行一个对Active Sever Pages ANDSQL的搜索。你将得到其描述中同时包含Active Sever Pages和SQL的记录。当你需要限制查询结果时,你可以使用AND。
如果你需要扩展查询的结果,你可以使用逻辑操作符OR。例如,如果你执行一个搜索,搜索所有的其描述中包含Active SeverPages OR SQL的站点,你收到的列表中将包括所有其描述中同时包含两个表达式或其中任何一个表达式的站点。
如果你想从搜索结果中排除特定的站点,你可以使用NOT。例如,查询“Active Sever Pages ”AND NOT “SQL”将返回一个列表,列表中的站点包含Active Sever Pages,但不包含SQL。当必须排除特定的记录时,你可以使用NOT。
用SQL执行的查询与用Internet搜索引擎执行的搜索非常相似。当你执行一个SQL查询时,通过使用包括逻辑运算符的查询条件,你可以得到一个记录列表。此时查询结果是来自一个或多个表。
SQL查询的句法非常简单。假设有一个名为email_table的表,包含名字和地址两个字段,要得到Bill Gates的e_mail地址,你可以使用下面的查询:
SELECT emailfrom email_table WHERE name="Bill Gates"
当这个查询执行时,就从名为email_table的表中读取Bill Gates的e_mail地址。这个简单的语句包括三部分:
■ SELECT语句的第一部分指名要选取的列。在此例中,只有email列被选取。当执行 时,只显示email列的值 billg@microsoft.com。
■ SELECTT语句的第二部份指明要从哪个(些)表中查询数据。在此例中,要查询的表名为email_table。
■最后,SELECT语句的WHERE子句指明要选择满足什么条件的记录。在此例中,查询条件为只有name列的值为Bill Gates的记录才被选取。
Bill Gates很有可能拥有不止一个email地址。如果表中包含Bill Gates的多个email地址。用上述的SELECT语句可以读取他所有的email地址。SELECT语句从表中取出所有name字段值为Bill Gates的记录的email字段的值。
前面说过,查询可以在查询条件中包含逻辑运算符。假如你想读取Bill Gates或Clinton总统的所有email地址,你可以使用下面的查询语句:
SELECT emailFROM email_table WHERE name="Bill Gates" OR
name="presidentClinton"
此例中的查询条件比前一个复杂了一点。这个语句从表email_table中选出所有name列为Bill Gates或president Clinton的记录。如果表中含有Bill Gates或president Clinton的多个地址,所有的地址都被读取。
SELECT语句的结构看起来很直观。如果你请一个朋友从一个表中为你选择一组记录,你也许以非常相似的方式提出你的要求。在SQL SELECT语句中,你“SELECT特定的列FROM一个表WHERE某些列满足一个特定的条件”。
下一节将介绍怎样执行SQL查询来选取记录。这将帮助你熟悉用SELECT语句从表中取数据的各种不同方法。
使用ISQL执行SELECT查询
当你安装SQL Sever时,你同时安装了一个叫作ISQL/w的应用程序。ISQL/w允许你执行交互的SQL查询。在把查询包括到你的ASP网页中之前,用ISQL/w对其进行测试是非常有用的。
注意:
在这本书的第一部份,你学习了怎样安装和配置Microsoft SQL Sever。如果没有安装SQL Sever或者SQL Sever不能运行,请参阅第三章“安装和使用SQL Sever”。
选择任务上SQL Sever程序组中的ISQL_w以启动该程序。程序启动时,首先会出现一个对话框,要求输入服务器信息和登录信息。在Sever框中,输入你的SQL服务器的名字。如果服务器正运行在本地计算机上,服务器名字就是你计算机的名字。在登录信息框中,输入一个登录帐号和密码或选择使用“可信连接”,然后单击Connect按钮。
注意:
如果你将SQL Sever配置为使用完整安全或混合安全,那么你可以使用可信连接。如果你使用标准安全,你则需要提供用户帐号和密码。要了解更多信息,参见第三章。
如果一切正常,在你单击连接按钮后会出现一个查询窗口。(如果有异常,请参考第三章)
在执行查询之前,你需要选择数据库。安装 SQL Sever时你已为自己创建了一个数据库,SQL Sever还有许多系统数据库,如master,model,msdb,和tempdb。
方便的是,SQL Sever带有一个特殊的名为pubs的例子数据库。库 pubs中包含供一个虚拟的出版商使用的各个表。文档中所有的例子程序都是针对这个库来设计的。本书中的许多例子也使用这个数据库。
在查询窗口顶部的DB下拉框中选择数据库pubs,这样你就选择了数据库。你所有的查询都将针对这个库中的各个表来执行。现在你可以执行你的第一个查询了。这真让人兴奋!
你的第一个查询将针对一个名为autrors的表,表中包含所有为某个虚拟出版商工作的作者的相关数据。单击查询窗口并输入以下的语句:
SELECT phoneFROM authors WHERE au_name="Ringer"
输入完成后,单击执行查询按钮(一个绿色三角形,看起来像VCR播放键)。单击此按钮后,任何出现在查询窗口中的语句均会被执行。查询窗口会自动变成结果显示窗口,你可以看到查询的结果。
在SQL Sever的不同版本中,库pubs中的数据会有所不同。对SQL Sever 6.5来说,将会找到两条记录。结果显示窗口中应显示如下内容:
phone
……………….
801 826_0752
801 826_0752
(2 row(s)affected)
你所执行的SELECT语句从表authors中取出所有名字为Ringer的作者的电话号码。你通过在WHERE子句中使用特殊的选择条件来限制查询的结果。你也可以忽略选择条件,从表中取出所有作者的电话号码。要做到这一点,单击Query标签,返回到查询窗口,输入以下的SELECT语句:
SELECT PhoneFROM authors
这个查询执行后,会取出表authors中的所有电话号码(没有特定的顺序)。如果表authors中包含一百个电话号码,会有一百个记录被取出,如果表中有十亿个电话号码,这十亿条记录都会被取出(这也许需要一些时间)。
表authrs的字段包括姓,名字,电话号码,地址,城市,州和邮政编码。通过在SELECT语句的第一部份指定它们,你可以从表中取出任何一个字段。你可以在一个SELECT语句中一次取出多个字段,比如:
SELECTau_fname ,au_lname, phone FROM authors
这个SELECT语句执行后,将取出这三个列的所有值。下面是这个查询的结果的一个示例(为了节省纸张,只显示查询结果的一部分,其余记录用省略号代替):
au_fnameau_lname phone
………………………………………………………………………….
JohnsonWhite 408 496_7223
MarjorieGreen 415 986_7020
CherylCarson 415 548_7723
Michael O’Leary 408 286_2428
…
(23 row(s)affected)
在SELECT语句中,你需要列出多少个字段,你就可以列出多少。不要忘了把字段名用逗号隔开。你也可以用星号(*)从一个表中取出所有的字段。这里有一个使用星号的例子:
SELECT *FROM authors
这个SELECT语句执行后,表中的所有字段的值都被取出。你会发现你将在SQL查询中频繁使用星号。
技巧:
你可以使用星号来查看一个表的所有列的名字。要做到这一点,只需要在执行完SELECT语句后看一下查询结果的列标题。
操作多个表
到现在为止,你只尝试了用一句SQL查询从一个表中取出数据。你也可以用一个SELECT语句同时从多个表中取出数据,只需在SELECT语句的FROM从句中列出要从中取出数据的表名称即可:
SELECTau_lname ,title FROM authors, titles
这个SELECT语句执行时,同时从表authors和表titles中取出数据。从表authors中取出所有的作者名字,从表titles中取出所有的书名。在ISQL/w程序中执行这个查询,看一下查询结果。你会发现一些奇怪的出乎意料的情况:作者的名字并没有和它们所著的书相匹配,而是出现了作者名字和书名的所有可能的组合,这也许不是你所希望见到的。
出了什么差错?问题在于你没有指明这两个表之间的关系。你没有通过任何方式告诉SQL如何把表和表关联在一起。由于不知道如何关联两个表,服务器只能简单地返回取自两个表中的记录的所有可能组合。
要从两个表中选出有意义的记录组合,你需要通过建立两表中字段的关系来关联两个表。要做到这一点的途径之一是创建第三个表,专门用来描述另外两个表的字段之间的关系。
表authors有一个名为au_id的字段,包含有每个作者的唯一标识。表titles有一个名为title_id的字段,包含每个书名的唯一标识。如果你能在字段au_id和字段title_id之间建立一个关系,你就可以关联这两个表。数据库pubs中有一个名为titleauthor的表,正是用来完成这个工作。表中的每个记录包括两个字段,用来把表titles和表authors关联在一起。下面的SELECT语句使用了这三个表以得到正确的结果:
SELECTau_name,title FROM authors,titles,titleauthor
WHEREauthors.au_id=titleauthor.au_id
ANDtitles.title_id=titleauthor.title_id
当这个SELECT语句执行时,每个作者都将与正确的书名相匹配。表titleauthor指明了表authors和表titles的关系,它通过包含分别来自两个表的各一个字段实现这一点。第三个表的唯一目的是在另外两个表的字段之间建立关系。它本身不包含任何附加数据。
注意在这个例子中字段名是如何书写的。为了区别表authors和表titles中相同的字段名au_id,每个字段名前面都加上了表名前缀和一个句号。名为author.au_id的字段属于表authors,名为titleauthor.au_id的字段属于表titleauthor,两者不会混淆。
通过使用第三个表,你可以在两个表的字段之间建立各种类型的关系。例如,一个作者也许写了许多不同的书,或者一本书也许由许多不同的作者共同完成。当两个表的字段之间有这种“多对多”的关系时,你需要使用第三个表来指明这种关系。
但是,在许多情况下,两个表之间的关系并不复杂。比如你需要指明表titles和表publishers之间的关系。因为一个书名不可能与多个出版商相匹配,你不需要通过第三个表来指明这两个表之间的关系。要指明表titles和表publishers之间的关系,你只要让这两个表有一个公共的字段就可以了。在数据库pubs中,表titles和表publishers都有一个名为pub_id的字段。如果你想得到书名及其出版商的一个列表,你可以使用如下的语句:
SELECTtitle,pub_name FROM titles,publishers
WHEREtitles.pub_id=publishers.pub_id
当然,如果一本书是由两个出版商联合出版的,那么你需要第三个表来代表这种关系。
通常,当你予先知道两个表的字段间存在“多对多”关系时,就使用第三个表来关联这两个表。反之,如果两个表的字段间只有“一对一”或“一对多”关系,你可以使用公共字段来关联它门。
操作字段
通常,当你从一个表中取出字段值时,该值与创建该表时所定义的字段名联系在一起。如果你从表authors中选择所有的作者名字,所有的值将会与字段名au_lname相联系。但是在某些情况下,你需要对字段名进行操作。在SELECT语句中,你可以在缺省字段名后面仅跟一个新名字来取代它。例如,可以用一个更直观易读的名字Author Last Name来代替字段名au_lname:
SELECTau_lname "Author Last Name" FROM authors
当这个SELECT语句执行时,来自字段au_lname的值会与“Author Last Name”相联系。查询结果可能是这样:
Author LastName
……………………………………………………………………..
White
Green
Carson
O’Leary
Straight
…
(23 row(s)affected)
注意字段标题不再是au_lname,而是被Author Last Name所取代。
你也可以通过执行运算,来操作从一个表返回的字段值。例如,如果你想把表titles中的所有书的价格加倍,你可以使用下面的SELECT语句:
SELECTprice*2 FROM titles
当这个查询执行时,每本书的价格从表中取出时都会加倍。但是,通过这种途径操作字段不会改变存储在表中的书价。对字段的运算只会影响SELECT语句的输出,而不会影响表中的数据。为了同时显示书的原始价格和涨价后的新价格,你可以使用下面的查询:
SELECT price"Original price", price*2 "New price" FROM titles
当数据从表titles中取出时,原始价格显示在标题Original price下面,加倍后的价格显示在标题New price下面。结果可能是这样:
originalprice new price
……………………………………………………………….
39.98
11.95 23.90
5.98
39.98
…
(18 row(s)affected)
你可以使用大多数标准的数学运算符来操作字段值,如加(+),减(-),乘(*)和除(/)。你也可以一次对多个字段进行运算,例如:
SELECTprice*ytd_sales "total revenue" FROM titles
在这个例子中,通过把价格与销售量相乘,计算出了每种书的总销售额。这个SELECT语句的结果将是这样的:
totalrevenue
……………………………………………..
81,859,05
46,318,20
55,978,78
81,859,05
40,619,68
…
(18 row(s)affected)
最后,你还可以使用连接运算符(它看起来像个加号)来连接两个字符型字段:
SELECTau_fname+" "+au_lname "author name" FROM authors
在这个例子中,你把字段au_fname和字段au_lname粘贴在一起,中间用一个逗号隔开,并把查询结果的标题指定为author name。这个语句的执行结果将是这样的:
author names
…………………………………………………………
JohnsonWhite
MarjorieGreen
CherylCarson
Michael O’Leary
Dean Straight
…
(23 row(s)affected)
可以看到,SQL为你提供了对查询结果的许多控制。你应该在ASP编程过程中充分利用这些优点。使用SQL来操作查询结果几乎总是比使用有同样作用的脚本效率更高。
排序查询结果
本章的介绍中曾强调过,SQL表没有内在的顺序。例如,从一个表中取第二个记录是没有意义的。从SQL的角度看来,没有一个记录在任何其他记录之前。
然而,你可以操纵一个SQL查询结果的顺序。在缺省情况下,当记录从表中取出时,记录不以特定的顺序出现。例如,当从表authors中取出字段au_lname时,查询结果显示成这样:
au_lname
…………………………………….
White
Green
Carson
O’Leary
Straight
…
(23 row(s)affected)
看一列没有特定顺序的名字是很不方便的。如果把这些名字按字母顺序排列,读起来就会容易得多。通过使用ORDER BY子句,你可以强制一个查询结果按升序排列,就像这样:
SELECTau_lname FROM authors ORDER BY au_lname
当这个SELECT语句执行时,作者名字的显示将按字母顺序排列。ORDER BY子句将作者名字按升序排列。
你也可以同时对多个列使用ORDER BY子句。例如,如果你想同时按升序显示字段au_lname和字段au_fname,你需要对两个字段都进行排序:
SELECTau_lname,au_fname FROM authors ORDER BY au_lname ,au_fname
这个查询首先把结果按au_lname字段进行排序,然后按字段au_fname排序。记录将按如下的顺序取出:
au_lnameau_fname
…………………………………………………………………….
BennetAbraham
RingerAlbert
Ringer Anne
SmithMeander
…
(23 row(s)affected)
注意有两个作者有相同的名字Ringer。名为Albert Ringer的作者出现名为Anne Ringer的作者之前,这是因为姓Albert按字母顺序应排在姓Anne之前。
如果你想把查询结果按相反的顺序排列,你可以使用关键字DESC。关键字DESC把查询结果按降序排列,如下例所示:
SELECTau_lname,au_fname FROM authors
WHEREau_lname=”Ringer” ORDER BYau_lname ,au_fname DESC
这个查询从表authors中取出所有名字为Ringer的作者记录。ORDER BY子句根据作者的名字和姓,将查询结果按降序排列。结果是这样的:
au_lnameau_fname
……………………………………………………………………………………….
Ringer Anne
RingerAlbert
(2 row(s)affectec)
注意在这个表中,姓Anne出现在姓Albert之前。作者名字按降序显示。
你也可以按数值型字段对一个查询结果进行排序。例如,如果你想按降序取出所有书的价格,你可以使用如下的SQL查询:
SELECT priceFROM titles ORDER BY price DESC
这个SELECT语句从表中取出所有书的价格,显示结果时,价格低的书先显示,价格高的书后显示。
警告:
不是特别需要时,不要对查询结果进行排序,因为服务器完成这项工作要费些力气。这意味着带有ORDER BY子句的SELECT语句执行起来比一般的SELECT语句花的时间长。
取出互不相同的记录
一个表有可能在同一列中有重复的值。例如,数据库pubs的表authors中有两个作者的名字是Ringer。如果你从这个表中取出所有的名字,名字Ringer将会显示两次。
在特定情况下,你可能只有兴趣从一个表中取出互不相同的值。如果一个字段有重复的值,你也许希望每个值只被选取一次,你可以使用关键字DISTINCT来做到这一点:
SELCETDISTINCT au_lname FROM authors WHERE au_lname="Ringer"
当这个SELECT语句执行时,只返回一个记录。通过在SELECT语句中包含关键字DISTINCT,你可以删除所有重复的值。例如,假设有一个关于新闻组信息发布的表,你想取出所有曾在这个新闻组中发布信息的人的名字,那么你可以使用关键字DISTINCT。每个用户的名字只取一次——尽管有的用户发布了不止一篇信息。
警告:
如同ORDER BY子句一样,强制服务器返回互不相同的值也会增加运行开销。福气不得不花费一些时间来完成这项工作。因此,不是必须的时候不要使用关键字DISTINCT。
创建新表
前面说过,数据库中的所有数据存储在表中。数据表包括行和列。列决定了表中数据的类型。行包含了实际的数据。
例如,数据库pubs中的表authors有九个字段。其中的一个字段名为为au_lname,这个字段被用来存储作者的名字信息。每次向这个表中添加新作者时,作者名字就被添加到这个字段,产生一条新记录。
通过定义字段,你可以创建一个新表。每个字段有一个名字和一个特定的数据类型(数据类型在后面的“字段类型”一节中讲述),例如字段au_lname存储的是字符型数据。一个字段也可以存储其它类型的数据。
使用SQL Sever,创建一个新表的方法是很多的。你可以可执行一个SQL语句或使用SQL事务管理器(SQL Enterprise Manager)来创建一个新表。在下一节里,你将学会如何用SQL语句来创建一个新表。
用SQL创建新表
注意:
如果你还没有建立自己的数据库,现在就跳回到第三章创建这个库。你绝不能向master,tempdb或任何其他任何系统数据库中添加数据。
从SQL Sever程序组(在任务栏中)中启动ISQL/w程序。出现查询窗口后,从窗口顶部的下拉列表中选择你在第三章所创建的数据库。下一步,在查询窗口中键入下面的SQL语句,单击执行查询按钮,执行这个语句:
CREATE TABLEguestbook (visitor VARCHAR(40),comments TEXT,entrydate
DATETIME)
如果一切正常,你会在结果窗口中看到如下的文字(如果出现异常,请参阅第三章):
This commanddit not return data ,and it did not return any rows
祝贺你,你已经建立了你的第一个表!
你所创建的表名为guestbook,你可以使用这个表来存储来字你站点访问者的信息。你是用REEATETABLE语句创建的这个表,这个语句有两部分:第一部份指定表的名子;第二部份是括在括号中的各字段的名称和属性,相互之间用逗号隔开。
表guestbook有三个字段:visitor,comments和entrydate。visitor字段存储访问者的名字,comments字段存储访问者对你站点的意见,entrydate字段存储访问者访问你站点的日期和时间。
注意每个字段名后面都跟有一个专门的表达式。例如,字段名comments后面跟有表达式TEXT。这个表达式指定了字段的数据类型。数据类型决定了一个字段可以存储什么样的数据。因为字段comments包含文本信息,其数据类型定义为文本型。
字段有许多不同的数据类型。下一小节讲述SQL所支持的一些重要的数据类型。
字段类型
不同的字段类型用来存放不同类型的数据。创建和使用表时,更你应该理解五种常用的字段类型:字符型,文本型,数值型,逻辑性和日期型。
字符型数据
字符型数据非常有用。当你需要存储短的字符串信息时,你总是要用到字符型数据。例如,你可以把从HTML form的文本框中搜集到的信息放在字符型字段中。
要建立一个字段用来存放可变长度的字符串信息,你可以使用表达式 VARCHAR。考虑你前面创建的表guestbook:
CREATE TABLEguestbook (visitor VARCHAR(40),comments TEXT,entrydate
DATETIME)
在这个例子中,字段visitor的数据类型为VARCHAR。注意跟在数据类型后面的括号中的数字。这个数字指定了这个字段所允许存放的字符串的最大长度。在这个例子中,字段visitor能存放的字符串最长为四十个字符。如果名字太长,字符串会被截断,只保留四十个字符。
VARCHAR类型可以存储的字符串最长为255个字符。要存储更长的字符串数据,可以使用文本型数据(下一节中讲述)。
另一种字符型数据用来存储固定长度的字符数据。下面是一个使用这种数据类型的例子:
CREATE TABLEguestbook (visitor CHAR(40),comments TEXT,entrydate
DATETIME)
在这个例子中,字段visitor被用来存储四十个字符的固定长度字符串。表达式CHAR指定了这个字段应该是固定长度的字符串。
VARCHAR型和CHAR型数据的这个差别是细微的,但是非常重要。假如你向一个长度为四十个字符的VARCHAR型字段中输入数据Bill Gates。当你以后从这个字段中取出此数据时,你取出的数据其长度为十个字符——字符串Bill Gates的长度。
现在假如你把字符串输入一个长度为四十个字符的CHAR型字段中,那么当你取出数据时,所取出的数据长度将是四十个字符。字符串的后面会被附加多余的空格。
当你建立自己的站点时,你会发现使用VARCHAR型字段要比CHAR型字段方便的多。使用VARCHAR型字段时,你不需要为剪掉你数据中多余的空格而操心。
VARCHAR型字段的另一个突出的好处是它可以比CHAR型字段占用更少的内存和硬盘空间。当你的数据库很大时,这种内存和磁盘空间的节省会变得非常重要。
文本型数据
字符型数据限制了字符串的长度不能超过255个字符。而使用文本型数据,你可以存放超过二十亿个字符的字符串。当你需要存储大串的字符时,应该使用文本型数据。
这里有一个使用文本型数据的例子:
CREATE TABLEguestbook (visitor VARCHAR(40),comments TEXT,entrydate
DATETIME)
在这个例子中,字段comments被用来存放访问者对你站点的意见。注意文本型数据没有长度,而上一节中所讲的字符型数据是有长度的。一个文本型字段中的数据通常要么为空,要么很大。
当你从HTML form的多行文本编辑框(TEXTAREA)中收集数据时,你应该把收集的信息存储于文本型字段中。但是,无论何时,只要你能避免使用文本型字段,你就应该不适用它。文本型字段既大且慢,滥用文本型字段会使服务器速度变慢。文本型字段还会吃掉大量的磁盘空间。
警告:
一旦你向文本型字段中输入了任何数据(甚至是空值),就会有2K的空间被自动分配给该数据。除非删除该记录,否则你无法收回这部分存储空间。
数值型数据
SQL Sever支持许多种不同的数值型数据。你可以存储整数、小数、和钱数。
通常,当你需要在表中的存放数字时,你要使用整型(INT)数据。INT型数据的表数范围是从-2,147,483,647到2,147,483,647的整数。下面是一个如何使用INT型数据的例子:
CREATE TABLEvisitlog (visitor VARCHAR(40),numvisits INT)
这个表可以用来记录你站点被访问的次数。只要没有人访问你的站点超过2,147,483,647次,nubvisits字段就可以存储访问次数。
为了节省内存空间,你可以使用SMALLINT型数据。SMALLINT型数据可以存储从-32768到32768的整数。这种数据类型的使用方法与INT型完全相同。
最后,如果你实在需要节省空间,你可以使用TINYINT型数据。同样,这种类型的使用方法也与INT型相同,不同的是这种类型的字段只能存储从0到255的整数。TINYINT型字段不能用来存储负数。
通常,为了节省空间,应该尽可能的使用最小的整型数据。一个TINYINT型数据只占用一个字节;一个INT型数据占用四个字节。这看起来似乎差别不大,但是在比较大的表中,字节数的增长是很快的。另一方面,一旦你已经创建了一个字段,要修改它是很困难的。因此,为安全起见,你应该预测以下,一个字段所需要存储的数值最大有可能是多大,然后选择适当的数据类型。
为了能对字段所存放的数据有更多的控制,你可以使用NUMERIC型数据来同时表示一个数的整数部分和小数部分。NUMERIC型数据使你能表示非常大的数——比INT型数据要大得多。一个NUMERIC型字段可以存储从-1038到1038范围内的数。NUMERIC型数据还使你能表示有小数部分的数。例如,你可以在NUMERIC型字段中存储小数3.14。
当定义一个NUMERIC型字段时,你需要同时指定整数部分的大小和小数部分的大小。这里有一个使用这种数据类型的例子:
CREATE TABLEnumeric_data (bignumber NUMERIC(28,0),
fractionNUMERIC (5,4) )
当这个语句执行时,将创建一个名为numeric_data的包含两个字段的表。字段bignumber可以存储直到28位的整数。字段fraction可以存储有五位整数部分和四位小数部分的小数。
一个NUMERIC型数据的整数部分最大只能有28位,小数部分的位数必须小于或等于整数部分的位数,小数部分可以是零。
你可以使用INT型或NUMERIC型数据来存储钱数。但是,专门有另外两种数据类型用于此目的。如果你希望你的网点能挣很多钱,你可以使用MONEY型数据。如果你的野心不大,你可以使用SMALLMONEY型数据。MONEY型数据可以存储从-922,337,203,685,477.5808到922,337,203,685,477.5807的钱数。如果你需要存储比这还大的金额,你可以使用NUMERIC型数据。
SMALLMONEY型数据只能存储从-214,748.3648到214,748.3647的钱数。同样,如果可以的话,你应该用SMALLMONEY型来代替MONEY型数据,以节省空间。下面的例子显示了如何使用这两种表示钱的数据类型:
CREATE TABLEproducts (product VARCHAR(40),price MONEY,
Discount_priceSMALLMONEY)
这个表可以用来存储商品的折扣和普通售价。字段price的数据类型是MONEY,字段discount_price的数据类型是SMALLMONEY。
存储逻辑值
如果你使用复选框(CHECKBOX)从网页中搜集信息,你可以把此信息存储在BIT型字段中。BIT型字段只能取两个值:0或1。这里有一个如何使用这种字段的例子:
CREATE TABLEopinion (visitor VARCHAR(40),good BIT)
这个表可以用来存放对你的网点进行民意调查所得的信息。访问者可以投票表示他们是否喜欢你的网点。如果他们投YES,就在BIT型字段中存入1。反之,如果他们投NO,就在字段中存入0(在下一章里,你将学会如何计算投票)。
当心,在你创建好一个表之后,你不能向表中添加BIT型字段。如果你打算在一个表中包含BIT型字段,你必须在创建表时完成。
存储日期和时间
当你建立一个网点时,你也许需要记录在一段时间内的访问者数量。为了能够存储日期和时间,你需要使用DATETIME型数据,如下例所示:
CREATE TABLvisitorlog( visitor VARCHAR (40), arrivaltime DATETIME ,
departuretimeDATETIME)
这个表可以用来记录访问者进入和离开你网点的时间和日期。一个DATETIME型的字段可以存储的日期范围是从1753年1月1日第一毫秒到9999年12月31日最后一毫秒。
如果你不需要覆盖这么大范围的日期和时间,你可以使用SMALLDATETIME型数据。它与DATETIME型数据同样使用,只不过它能表示的日期和时间范围比DATETIME型数据小,而且不如DATETIME型数据精确。一个SMALLDATETIME型的字段能够存储从1900年1月1日到2079年6月6日的日期,它只能精确到秒。
DATETIME型字段在你输入日期和时间之前并不包含实际的数据,认识这一点是重要的。在下一章,你将学习怎样使用大量的SQL函数来读取和操作日期和时间(参见下面的“缺省值”一节)。你也可以在VBScript和JScript中使用日期和时间函数来向一个DATETIME型字段中输入日期和时间。
字段属性
上一节介绍了如何建立包含不同类型字段的表。在这一节中,你将学会如何使用字段的三个属性。这些属性允许你控制空值,缺省值和标识值。
允许和禁止空值
大多数字段可以接受空值(NULL)。当一个字段接受了空值后,如果你不改变它,它将一直保持空值。空值(NULL)和零是不同的,严格的说,空值表示没有任何值。
为了允许一个字段接受空值,你要在字段定义的后面使用表达式NULL。例如,下面的表中两个字段都允许接受空值:
CREATE TABLEempty (empty1 CHAR (40) NULL,empty2 INT NULL(
注意:
BIT型数据不能是空值。一个这种类型的字段必须取0或者1。
有时你需要禁止一个字段使用空值。例如,假设有一个表存储着信用卡号码和信用卡有效日期,你不会希望有人输入一个信用卡号码但不输入有效日期。为了强制两个字段都输入数据,你可以用下面的方法建立这个表:
CREATE TABLEcreditcards (creditcard_number CHAR(20) NOT NULL,
Creditcard_expireDATETIME NOT NULL)
注意字段定义的后面跟有表达式NOT NULL。通过包含表达式NOT NULL,你可以禁止任何人只在一个字段中插入数据,而不输入另一个字段的数据。
你将会发现,在你建设自己的网点过程中,这种禁止空值的能力是非常有用的。如果你指定一个字段不能接受空值,那么当你试图输入一个空值时,会有错误警告。这些错误警告可以为程序调试提供有价值的线索。
缺省值
假设有一个存储地址信息的表,这个表的字段包括街道、城市、州、邮政编码和国家。如果你预计地址的大部分是在美国,你可以把这个值作为country字段的缺省值。
为了在创建一个表时指定缺省值,你可以使用表达式DEFAULT。请看下面这个在创建表时使用缺省值的例子:
CREATE TABLEaddresses (street VARCHAR(60) NULL,
cityVARCHAR(40) NULL,
stateVARCHAR(20) NULL
zipVARCHAR(20) NULL,
countryVARCHAR(30) DEFAULT ‘USA’)
在这个例子中,字段country的缺省值被指定为美国。注意单引号的使用,引号指明这是字符型数据。为了给非字符型的字段指定缺省值,不要把该值扩在引号中:
CREATE TABLEorders(price MONEY DEFAULT $38.00,
quantity INTDEFAULT 50,
entrydateDATETIME DEFAULT GETDATE())
在这个CREATE TABLE语句中,每个字段都指定了一个缺省值。注意DATETIME型字段entrydate所指定的缺省值,该缺省值是函数Getdate()的返回值,该函数返回当前的日期和时间。
标识字段
每个表可以有一个也只能有一个标识字段。一个标识字段是唯一标识表中每条记录的特殊字段。例如,数据库pubs中的表jobs包含了一个唯一标识每个工作标识字段:
job_idjob_desc
…………………………………………………………….
New Hire Jobnot specified
ChiefExecutive officer
BushnessOperations Manager
ChiefFinancial Officier
Publisher
字段job_id为每个工作提供了唯一的一个数字。如果你决定增加一个新工作,新增记录的job_id字段会被自动赋给一个新的唯一值。
为了建立一个标识字段,你只需在字段定义后面加上表达式IDENTITY即可。你只能把NUMERIC型或INT型字段设为标识字段,这里有一个例子:
CREATE TABLEvisitorID (theID NUBERIC(18) IDENTITY,name VARCHAR(40))
这个语句所创建的表包含一个名为theid的标识字段。每当一个新的访问者名字添加到这个表中时,这个字段就被自动赋给一个新值。你可以用这个表为你的站点的每一个用户提供唯一标识。
技巧:
建立一个标示字段时,注意使用足够大的数据类型。例如你使用TINYINT型数据,那么你只能向表中添加255个记录。如果你预计一个表可能会变得很大,你应该使用NUMERIC型数据。
标识字段的存在会使你想尝试许多不可能的事情。例如,你也许想利用标识字段来对记录进行基于它们在表中位置的运算。你应该抛弃这种意图。每个记录的标识字段的值是互不相同的,但是,这并不禁止一个标识字段的标识数字之间存在间隔。例如,你永远不要试图利用一个表的标识字段来取出表中的前十个记录。这种操作会导致失败,比如说6号记录和7号记录根本不存在。
使用SQL事务管理器创建新表
你可以使用前面几节所讲的方法创建新表。但是,使用事务管理器创建新表会更容易。这一节介绍如何使用这个程序创建新表。
从任务栏的SQL Sever程序组中选择SQL Enterprise Manager,启动该程序。浏览服务管理器窗口中的树形结构,选择名为Database的文件夹。打开文件夹Database后,选择你在第三章中所建立的数据库。
注意:
如果你还没有创建自己的数据库,回到第三章创建它。你决不要向master,tempdb或任何其它系统数据库中添加数据。
在选择了数据库之后,你会看到一个名为Group/users的文件夹和一个名为objects的文件夹。打开文件夹objects,你会看到许多文件夹,其中一个名为Tables。用右键单击文件夹Tables并选择New table。
你可以使用Manager Tables窗口来创建一个新表。Manager Tables窗口有7个列:Key,Column,Name,Datatype,Size,Nulls和Default。Manager Tables窗口中的每一行标明表中一个字段的信息。
要建立一个新表,你至少要输入一行信息。在名为Column Name的列下面键入mycolumn。下一步,选择Datatype列,并从下拉列表中选择CHAR。在这两个列中输入信息。
你已经建立了一个只有一个字段的简单的表。单击保存按扭保存这个新表。当要求你输入新表的名字时,输入mytable并单击OK。现在这个表已经保存到了你的数据库中。
如果你打开服务管理器窗口中的文件夹Tables,你会看到你所建立的新表被列出。你可以双击该表的图表来编辑它,这时ManagerTables窗口会重新出现,你可以增加新的字段并重新保存。
用SQL事务管理器可以做的工作,你都可以用SQL语句来实现。但是,事务管理器使得建表过程变得更加简单。
向表中添加数据
下一章将讨论如何使用SQL向一个表中插入数据。但是,如果你需要向一个表中添加许多条记录,使用SQL语句输入数据是很不方便的。幸运的是,Microsoft SQL Sever带有一个称为Microsoft Query的客户端应用程序,这个程序使得向表中添加数据变得容易了。
启动位于任务栏SQL Sever程序组中的Microsoft Query程序。从窗口顶部的菜单中选择File|New Query。这时会显示一个Select Data Source对话框。选择你的数据源名字并单击Use。
输入你的登录帐号和密码后,程序要求你选择一个表和一个数据库。选择你在上一节中所建立的表(mytable ),单击按钮Add,然后单击按钮Close关闭该对话框。
在窗口的左上角会出现一个对话框,框中是取自表mytable的一列字段名。你可以双击任何一个字段,把它添加到主窗口中。如果你双击星号(*)字符,所有的字段都会被添加到主窗口中。
如果你的表中有记录,它们现在已经出现在主窗口的字段标题下面了。但是,因为你刚刚建立了这个表,表还是空的。要添加新记录,选择Records|Allow Editing,主窗口中就会出现一条新记录。输入一行数据完成这个记录,就向表中添加了一条新记录。
当你转到下一条新记录时,你向上一条记录中输入的值会自动被保存。如果你需要,你可以用Microsoft Query向表中输入几百条记录。
删除和修改表
你应该在建立表之前仔细设计它们,因为你在改变一个已经存在的表时会受到很大的限制。例如,一旦已经建立了一个表,你就不能删除表中的字段或者改变字段的数据类型。在这种情况你所能做的是删除这个表,然后重头开始(参见第十一章“中级SQL”中的“使用SQL创建记录和表”一节)。
要删除一个表,你可以使用SQL语句DROP TABLE。例如,又从数据库中彻底删除表mytable,你要使用如下的语句:
DROP TABLEmytable
警告:
使用DROP TABLE命令时一定要小心。一旦一个表被删除之后,你将无法恢复它。
当你建设一个站点时,你很可能需要向数据库中输入测试数据。而当你准备向世界提供你的网点时,你会想清空表中的这些测试信息。如果你想清除表中的所有数据但不删除这个表,你可以使用TRUNCATE TABLE语句。例如,下面的这个SQL语句从表mytable中删除所有数据:
TRUNCATETABLE mytable
虽然你不能删除和修改已经存在的字段,但你可以增加新字段。最容易的实现方法是使用SQL事务管理器中的Manager Tables窗口。你也可以使用SQL语句ALTER TABLE。下面是一个如何使用这种语句的例子:
ALTER TABLEmytable ADD mynewcolumn INT NULL
这个语句向表mytable中增加了一个新字段mynewcolumn。当你增加新字段时,你必须允许它接受空值,因为表中原来可能已经有了许多记录。
总结
这一章向你介绍了SQL。使用SQL,你可以操作MicrosoftSQL Sever数据库。你已经学会了使用SELECT语句从数据库中取出数据,你还学会了怎样使用CREATE TABLE语句和SQL事务管理器来创建新表。最后,你学会了如何指明一系列重要的字段属性。
下一章将介绍如何使用索引来增强SQL查询的操作。还将通过许多其它的SQL语句和函数,使你的SQL知识得到进一步扩充。
第十章“SQL基础”向你初步介绍了SQL。你学会了如何用SELECT语句进行查询,你还学会了如何建立自己的表。在这一章里,你将加深你的SQL知识。你将学习如何建立索引来加快查询速度。你还将学会如果用更多的SQL语句和函数来操作表中的数据。
建立索引
假设你想找到本书中的某一个句子。你可以一页一页地逐页搜索,但这会花很多时间。而通过使用本书的索引,你可以很快地找到你要搜索的主题。
表的索引与附在一本书后面的索引非常相似。它可以极大地提高查询的速度。对一个较大的表来说,通过加索引,一个通常要花费几个小时来完成的查询只要几分钟就可以完成。因此没有理由对需要频繁查询的表增加索引。
注意:
当你的内存容量或硬盘空间不足时,也许你不想给一个表增加索引。对于包含索引的数据库,SQL Sever需要一个可观的额外空间。例如,要建立一个聚簇索引,需要大约1.2倍于数据大小的空间。要看一看一个表的索引在数据库中所占的空间大小,你可以使用系统存储过程sp_spaceused,对象名指定为被索引的表名。
聚簇索引和非聚簇索引
假设你已经通过本书的索引找到了一个句子所在的页码。一旦已经知道了页码后,你很可能漫无目的翻寻这本书,直至找到正确的页码。通过随机的翻寻,你最终可以到达正确的页码。但是,有一种找到页码的更有效的方法。
首先,把书翻到大概一半的地方,如果要找的页码比半本书处的页码小,就书翻到四分之一处,否则,就把书翻到四分之三的地方。通过这种方法,你可以继续把书分成更小的部分,直至找到正确的页码附近。这是找到书页的非常有效的一种方法。
SQL Sever的表索引以类似的方式工作。一个表索引由一组页组成,这些页构成了一个树形结构。根页通过指向另外两个页,把一个表的记录从逻辑上分成和两个部分。而根页所指向的两个页又分别把记录分割成更小的部分。每个页都把记录分成更小的分割,直至到达叶级页。
索引有两种类型:聚簇索引和非聚簇索引。在聚簇索引中,索引树的叶级页包含实际的数据:记录的索引顺序与物理顺序相同。在非聚簇索引中,叶级页指向表中的记录:记录的物理顺序与逻辑顺序没有必然的联系。
聚簇索引非常象目录表,目录表的顺序与实际的页码顺序是一致的。非聚簇索引则更象书的标准索引表,索引表中的顺序通常与实际的页码顺序是不一致的。一本书也许有多个索引。例如,它也许同时有主题索引和作者索引。同样,一个表可以有多个非聚簇索引。
通常情况下,你使用的是聚簇索引,但是你应该对两种类型索引的优缺点都有所理解。
每个表只能有一个聚簇索引,因为一个表中的记录只能以一种物理顺序存放。通常你要对一个表按照标识字段建立聚簇索引。但是,你也可以对其它类型的字段建立聚簇索引,如字符型,数值型和日期时间型字段。
从建立了聚簇索引的表中取出数据要比建立了非聚簇索引的表快。当你需要取出一定范围内的数据时,用聚簇索引也比用非聚簇索引好。例如,假设你用一个表来记录访问者在你网点上的活动。如果你想取出在一定时间段内的登录信息,你应该对这个表的DATETIME型字段建立聚簇索引。
对聚簇索引的主要限制是每个表只能建立一个聚簇索引。但是,一个表可以有不止一个非聚簇索引。实际上,对每个表你最多可以建立249个非聚簇索引。你也可以对一个表同时建立聚簇索引和非聚簇索引。
假如你不仅想根据日期,而且想根据用户名从你的网点活动日志中取数据。在这种情况下,同时建立一个聚簇索引和非聚簇索引是有效的。你可以对日期时间字段建立聚簇索引,对用户名字段建立非聚簇索引。如果你发现你需要更多的索引方式,你可以增加更多的非聚簇索引。
非聚簇索引需要大量的硬盘空间和内存。另外,虽然非聚簇索引可以提高从表中取数据的速度,它也会降低向表中插入和更新数据的速度。每当你改变了一个建立了非聚簇索引的表中的数据时,必须同时更新索引。因此你对一个表建立非聚簇索引时要慎重考虑。如果你预计一个表需要频繁地更新数据,那么不要对它建立太多非聚簇索引。另外,如果硬盘和内存空间有限,也应该限制使用非聚簇索引的数量。
索引属性
这两种类型的索引都有两个重要属性:你可以用两者中任一种类型同时对多个字段建立索引(复合索引);两种类型的索引都可以指定为唯一索引。
你可以对多个字段建立一个复合索引,甚至是复合的聚簇索引。假如有一个表记录了你的网点访问者的姓和名字。如果你希望根据完整姓名从表中取数据,你需要建立一个同时对姓字段和名字字段进行的索引。这和分别对两个字段建立单独的索引是不同的。当你希望同时对不止一个字段进行查询时,你应该建立一个对多个字段的索引。如果你希望对各个字段进行分别查询,你应该对各字段建立独立的索引。
两种类型的索引都可以被指定为唯一索引。如果对一个字段建立了唯一索引,你将不能向这个字段输入重复的值。一个标识字段会自动成为唯一值字段,但你也可以对其它类型的字段建立唯一索引。假设你用一个表来保存你的网点的用户密码,你当然不希望两个用户有相同的密码。通过强制一个字段成为唯一值字段,你可以防止这种情况的发生。
用SQL建立索引
为了给一个表建立索引,启动任务栏SQL Sever程序组中的ISQL/w程序。进入查询窗口后,输入下面的语句:
CREATE INDEXmycolumn_index ON mytable (myclumn)
这个语句建立了一个名为mycolumn_index的索引。你可以给一个索引起任何名字,但你应该在索引名中包含所索引的字段名,这对你将来弄清楚建立该索引的意图是有帮助的。
注意:
在本书中你执行任何SQL语句,都会收到如下的信息:
This commanddid not return data,and it did not return any rows
这说明该语句执行成功了。
索引mycolumn_index对表mytable的mycolumn字段进行。这是个非聚簇索引,也是个非唯一索引。(这是一个索引的缺省属性)
如果你需要改变一个索引的类型,你必须删除原来的索引并重建一个。建立了一个索引后,你可以用下面的SQL语句删除它:
DROP INDEXmytable.mycolumn_index
注意在DROP INDEX语句中你要包含表的名字。在这个例子中,你删除的索引是mycolumn_index,它是表mytable的索引。
要建立一个聚簇索引,可以使用关键字CLUSTERED。)记住一个表只能有一个聚簇索引。(这里有一个如何对一个表建立聚簇索引的例子:
CREATECLUSTERED INDEX mycolumn_clust_index ON mytable(mycolumn)
如果表中有重复的记录,当你试图用这个语句建立索引时,会出现错误。但是有重复记录的表也可以建立索引;你只要使用关键字ALLOW_DUP_ROW把这一点告诉SQL Sever即可:
CREATECLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn)
WITHALLOW_DUP_ROW
这个语句建立了一个允许重复记录的聚簇索引。你应该尽量避免在一个表中出现重复记录,但是,如果已经出现了,你可以使用这种方法。
要对一个表建立唯一索引,可以使用关键字UNIQUE。对聚簇索引和非聚簇索引都可以使用这个关键字。这里有一个例子:
CREATEUNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)
这是你将经常使用的索引建立语句。无论何时,只要可以,你应该尽量对一个对一个表建立唯一聚簇索引来增强查询操作。
最后,要建立一个对多个字段的索引──复合索引──在索引建立语句中同时包含多个字段名。下面的例子对firstname和lastname两个字段建立索引:
CREATE INDEXname_index ON username(firstname,lastname)
这个例子对两个字段建立了单个索引。在一个复合索引中,你最多可以对16个字段进行索引。
用事务管理器建立索引
用事务管理器建立索引比用SQL语句容易的多。使用事务管理器,你可以看到已经建立的索引的列表,并可以通过图形界面选择索引选项。
使用事务管理器你可以用两种方式建立索引:使用Manage Tables窗口或使用Manage Indexes窗口。
要用Manage Tables窗口建立一个新索引,单击按钮Advanced Options(它看起来象一个前面有一加号的表)。这样就打开了Advanced Options对话框。这个对话框有一部分标名为Primary Key。
要建立一个新索引,从下拉列表中选择你想对之建立索引的字段名。如果你想建立一个对多字段的索引,你可以选择多个字段名。你还可以选择索引是聚簇的还是非聚簇的。在保存表信息后,索引会自动被建立。在Manage Tables窗口中的字段名旁边,会出现一把钥匙。
你已经为你的表建立了“主索引”。主索引必须对不包含空值的字段建立。另外,主索引强制一个字段成为唯一值字段。
要建立没有这些限制的索引,你需要使用Manage Indexes窗口。从菜单中选择Manage|Indexes,打开Manage Indexes窗口。在Manage Indexes窗口中,你可以通过下拉框选择表和特定的索引。要建立一个新索引,从Index下拉框中选择New Index.,然后就可以选择要对之建立索引的字段。单击按钮Add,把字段加人到索引中。
你可以为你的索引选择许多不同的选项。例如,你可以选择该索引是聚簇的还是非聚簇的。你还可以指定该索引为唯一索引。设计好索引后,单击按钮Build,建立该索引。
注意:
唯一索引是指该字段不能有重复的值,而不是只能建立这一个索引。
SQL核心语句
在第十章,你学会了如何用SQL SELECT语句从一个表中取数据。但是,到现在为止,还没有讨论如何添加,修改或删除表中的数据。在这一节中,你将学习这些内容。
插入数据
向表中添加一个新记录,你要使用SQL INSERT语句。这里有一个如何使用这种语句的例子:
INSERT mytable(mycolumn) VALUES (‘some data’)
这个语句把字符串’some data’插入表mytable的mycolumn字段中。将要被插入数据的字段的名字在第一个括号中指定,实际的数据在第二个括号中给出。
INSERT语句的完整句法如下:
INSERT[INTO] {table_name|view_name} [(column_list)] {DEFAULT VALUES |
Values_list| select_statement}
如果一个表有多个字段,通过把字段名和字段值用逗号隔开,你可以向所有的字段中插入数据。假设表mytable有三个字段first_column,second_column,和third_column。下面的INSERT语句添加了一条三个字段都有值的完整记录:
INSERTmytable (first_column,second_column,third_column)
VALUES (‘some data’,’somemore data’,’yet more data’)
注意:
你可以使用INSERT语句向文本型字段中插入数据。但是,如果你需要输入很长的字符串,你应该使用WRITETEXT语句。这部分内容对本书来说太高级了,因此不加讨论。要了解更多的信息,请参考Microsoft SQL Sever的文档。
如果你在INSERT语句中只指定两个字段和数据会怎么样呢?换句话说,你向一个表中插入一条新记录,但有一个字段没有提供数据。在这种情况下,有下面的四种可能:
如果该字段有一个缺省值,该值会被使用。例如,假设你插入新记录时没有给字段third_column提供数据,而这个字段有一个缺省值’some value’。在这种情况下,当新记录建立时会插入值’some value’。
如果该字段可以接受空值,而且没有缺省值,则会被插入空值。
如果该字段不能接受空值,而且没有缺省值,就会出现错误。你会收到错误信息:
The columnin table mytable may not be null.
最后,如果该字段是一个标识字段,那么它会自动产生一个新值。当你向一个有标识字段的表中插入新记录时,只要忽略该字段,标识字段会给自己赋一个新值。
注意:
向一个有标识字段的表中插入新记录后,你可以用SQL变量@@identity来访问新记录
的标识字段的值。考虑如下的SQL语句:
INSERTmytable (first_column) VALUES(‘some value’)
INSERTanothertable(another_first,another_second)
VALUES(@@identity,’some value’)
如果表mytable有一个标识字段,该字段的值会被插入表anothertable的another_first字段。这是因为变量@@identity总是保存最后一次插入标识字段的值。
字段another_first应该与字段first_column有相同的数据类型。但是,字段another_first不能是应该标识字段。Another_first字段用来保存字段first_column的值。
删除记录
要从表中删除一个或多个记录,需要使用SQL DELETE语句。你可以给DELETE语句提供WHERE子句。WHERE子句用来选择要删除的记录。例如,下面的这个DELETE语句只删除字段first_column的值等于’Delete Me’的记录:
DELETEmytable WHERE first_column=’Deltet Me’
DELETE语句的完整句法如下:
DELETE[FROM] {table_name|view_name} [WHERE clause]
在SQL SELECT语句中可以使用的任何条件都可以在DELECT语句的WHERE子句 中使用。例如,下面的这个DELETE语句只删除那些first_column字段的值为’goodbye’或second_column字段的值为’so long’的记录:
DELETEmytable WHERE first_column=’goodby’ ORsecond_column=’so long’
如果你不给DELETE语句提供WHERE子句,表中的所有记录都将被删除。你不应该有这种想法。如果你想删除应该表中的所有记录,应使用第十章所讲的TRUNCATE TABLE语句。
注意:
为什么要用TRUNCATE TABLE语句代替DELETE语句?当你使用TRUNCATE TABLE语句时,记录的删除是不作记录的。也就是说,这意味着TRUNCATETABLE要比DELETE快得多
更新记录
要修改表中已经存在的一条或多条记录,应使用SQL UPDATE语句。同DELETE语句一样,UPDATE语句可以使用WHERE子句来选择更新特定的记录。请看这个例子:
UPDATEmytable SET first_column=’Updated!’WHERE second_column=’Update Me!’
这个UPDATE语句更新所有second_column字段的值为’Update Me!’的记录。对所有被选中的记录,字段first_column的值被置为’Updated!’。
下面是UPDATE语句的完整句法:
UPDATE {table_name|view_name}SET [{table_name|view_name}]
{column_list|variable_list|variable_and_column_list}
[,{column_list2|variable_list2|variable_and_column_list2}…
[,{column_listN|variable_listN|variable_and_column_listN}]]
[WHEREclause]
注意:
你可以对文本型字段使用UPDATE语句。但是,如果你需要更新很长的字符串,应使用UPDATETEXT语句。这部分内容对本书来说太高级了,因此不加讨论。要了解更多的信息,请参考Microsoft SQL Sever的文档。
如果你不提供WHERE子句,表中的所有记录都将被更新。有时这是有用的。例如,如果你想把表titles中的所有书的价格加倍,你可以使用如下的UPDATE语句:
你也可以同时更新多个字段。例如,下面的UPDATE语句同时更新first_column,second_column,和third_column这三个字段:
UPDATEmytable SET first_column=’Updated!’
Second_column=’Updated!’
Third_column=’Updated!’
WHEREfirst_column=’Update Me1’
技巧:
SQL忽略语句中多余的空格。你可以把SQL语句写成任何你最容易读的格式。
用SELECT创建记录和表
你也许已经注意到,INSERT语句与DELETE语句和UPDATE语句有一点不同,它一次只操作一个记录。然而,有一个方法可以使INSERT语句一次添加多个记录。要作到这一点,你需要把INSERT语句与SELECT语句结合起来,象这样:
INSERTmytable (first_column,second_column)
SELECTanother_first,another_second
FROManothertable
WHEREanother_first=’Copy Me!’
这个语句从anothertable拷贝记录到mytable.只有表anothertable中字段another_first的值为’Copy Me!’的记录才被拷贝。
当为一个表中的记录建立备份时,这种形式的INSERT语句是非常有用的。在删除一个表中的记录之前,你可以先用这种方法把它们拷贝到另一个表中。
如果你需要拷贝整个表,你可以使用SELECT INTO语句。例如,下面的语句创建了一个名为newtable的新表,该表包含表mytable的所有数据:
SELECT *INTO newtable FROM mytable
你也可以指定只有特定的字段被用来创建这个新表。要做到这一点,只需在字段列表中指定你想要拷贝的字段。另外,你可以使用WHERE子句来限制拷贝到新表中的记录。下面的例子只拷贝字段second_columnd的值等于’Copy Me!’的记录的first_column字段。
SELECTfirst_column INTO newtable
FROM mytable
WHEREsecond_column=’Copy Me!’
使用SQL修改已经建立的表是很困难的。例如,如果你向一个表中添加了一个字段,没有容易的办法来去除它。另外,如果你不小心把一个字段的数据类型给错了,你将没有办法改变它。但是,使用本节中讲述的SQL语句,你可以绕过这两个问题。
例如,假设你想从一个表中删除一个字段。使用SELECT INTO语句,你可以创建该表的一个拷贝,但不包含要删除的字段。这使你既删除了该字段,又保留了不想删除的数据。
如果你想改变一个字段的数据类型,你可以创建一个包含正确数据类型字段的新表。创建好该表后,你就可以结合使用UPDATE语句和SELECT语句,把原来表中的所有数据拷贝到新表中。通过这种方法,你既可以修改表的结构,又能保存原有的数据。
集合函数
到现在为止,你只学习了如何根据特定的条件从表中取出一条或多条记录。但是,假如你想对一个表中的记录进行数据统计。例如,如果你想统计存储在表中的一次民意测验的投票结果。或者你想知道一个访问者在你的站点上平均花费了多少时间。要对表中的任何类型的数据进行统计,都需要使用集合函数。
MicrosoftSQL支持五种类型的集合函数。你可以统计记录数目,平均值,最小值,最大值,或者求和。当你使用一个集合函数时,它只返回一个数,该数值代表这几个统计值之一。
注意:
要在你的ASP网页中使用集合函数的返回值,你需要给该值起一个名字。要作到这一点,你可以在SELECT语句中,在集合函数后面紧跟一个字段名,如下例所示:
SELECTAVG(vote) ‘the_average’ FROMopinion
在这个例子中,vote的平均值被命名为 the_average。现在你可以在你的ASP网页的数据库方法中使用这个名字。
统计字段值的数目
函数COUNT()也许是最有用的集合函数。你可以用这个函数来统计一个表中有多少条记录。这里有一个例子:
SELECTCOUNT(au_lname) FROM authors
这个例子计算表authors中名字(last name)的数目。如果相同的名字出现了不止一次,该名字将会被计算多次。如果你想知道名字为某个特定值的作者有多少个,你可以使用WHERE子句,如下例所示:
SELECTCOUNT(au_lname) FROM authors WHERE au_lname=’Ringer’
这个例子返回名字为’Ringer’的作者的数目。如果这个名字在表authors中出现了两次,则次函数的返回值是2。
假如你想知道有不同名字的作者的数目。你可以通过使用关键字DISTINCT来得到该数目。如下例所示:
SELECTCOUNT(DISTINCT au_lname) FROM authors
如果名字’Ringer’出现了不止一次,它将只被计算一次。关键字DISTINCT决定了只有互不相同的值才被计算。
通常,当你使用COUNT()时,字段中的空值将被忽略。一般来说,这正是你所希望的。但是,如果你仅仅想知道表中记录的数目,那么你需要计算表中所有的记录─不管它是否包含空值。下面是一个如何做到这一点的例子:
SELECTCOUNT(*) FROM authors
注意函数COUNT()没有指定任何字段。这个语句计算表中所有记录所数目,包括有空值的记录。因此,你不需要指定要被计算的特定字段。
函数COUNT()在很多不同情况下是有用的。例如,假设有一个表保存了对你站点的质量进行民意调查的结果。这个表有一个名为vote的字段,该字段的值要么是0,要么是1。0表示反对票,1表示赞成票。要确定赞成票的数量,你可以所有下面的SELECT语句:
SELECTCOUNT(vote) FROM opinion_table WHERE vote=1
计算字段的平均值
使用函数COUNT(),你可以统计一个字段中有多少个值。但有时你需要计算这些值的平均值。使用函数AVG(),你可以返回一个字段中所有值的平均值。
假如你对你的站点进行一次较为复杂的民意调查。访问者可以在1到10之间投票,表示他们喜欢你站点的程度。你把投票结果保存在名为vote的INT型字段中。要计算你的用户投票的平均值,你需要使用函数AVG():
SELECTAVG(vote) FROM opinion
这个SELECT语句的返回值代表用户对你站点的平均喜欢程度。函数AVG()只能对数值型字段使用。这个函数在计算平均值时也忽略空值。
计算字段值的和
假设你的站点被用来出售卡片,已经运行了两个月,是该计算赚了多少钱的时候了。假设有一个名为orders的表用来记录所有访问者的定购信息。要计算所有定购量的总和,你可以使用函数SUM():
SELECTSUM(purchase_amount) FROM orders
函数SUM()的返回值代表字段purchase_amount中所有值的平均值。字段purchase_amount的数据类型也许是MONEY型,但你也可以对其它数值型字段使用函数SUM()。
返回最大值或最小值
再一次假设你有一个表用来保存对你的站点进行民意调查的结果。访问者可以选择从1到10的值来表示他们对你站点的评价。如果你想知道访问者对你站点的最高评价,你可以使用如下的语句:
SELECTMAX(vote) FROM opinion
你也许希望有人对你的站点给予了很高的评价。通过函数MAX(),你可以知道一个数值型字段的所有值中的最大值。如果有人对你的站点投了数字10,函数MAX()将返回该值。
另一方面,假如你想知道访问者对你站点的的最低评价,你可以使用函数MIN(),如下例所示:
SELECTMIN(vote) FROM opinion
函数MIN()返回一个字段的所有值中的最小值。如果字段是空的,函数MIN()返回空值。
其它常用的SQL表达式,函数,和过程
这一节将介绍一些其它的SQL技术。你将学习如何从表中取出数据,其某个字段的值处在一定的范围,你还将学习如何把字段值从一种类型转换成另一种类型,如何操作字符串和日期时间数据。最后,你将学会一个发送邮件的简单方法。
通过匹配一定范围的值来取出数据
假设你有一个表用来保存对你的站点进行民意调查的结果。现在你想向所有对你的站点的评价在7到10之间的访问者发送书面的感谢信。要得到这些人的名字,你可以使用如下的SELECT语句:
SELECTusername FROM opinion WHERE vote>6 and vote<11
这个SELECT语句会实现你的要求。你使用下面的SELECT语句也可以得到同样的结果:
SELECTusername FROM opinion WHERE vote BETWEEN 7 AND 10
这个SELECT语句与上一个语句是等价的。使用哪一种语句是编程风格的问题,但你会发现使用表达式BETWEEN的语句更易读。
现在假设你只想取出对你的站点投了1或者10的访问者的名字。要从表opinion中取出这些名字,你可以使用如下的SELECT语句:
SELECTusername FROM opinion WHERE vote=1 or vote
这个SELECT语句会返回正确的结果,没有理由不使用它。但是,存在一种等价的方式。使用如下的SELECT可以得到相同的结果:
SELECTusername FROM opinion WHERE vote IN (1,10)
注意表达式IN的使用。这个SELECT语句只取出vote的值等于括号中的值之一的记录。
你也可以使用IN来匹配字符数据。例如,假设你只想取出Bill Gates或President Clinton的投票值。你可以使用如下的SELECT语句:
SELECT voteFROM opinion WHERE username IN (‘Bill Gates’,’President Clinton’)
最后,你可以在使用BETWEEN或IN的同时使用表达式NOT。例如,要取出那些投票值不在7到10之间的人的名字,你可以使用如下的SELECT语句:
SELECTusername FROM opinion WHERE vote NOT BETWEEN 7 and 10
要选取那些某个字段的值不在一列值之中的记录,你可以同时使用NOT和IN,如下例所示:
SELECT voteFROM opinion
WHEREusername NOT IN (‘Bill Gates’,’President Clinton’)
你不是必须在SQL语句中使用BETWEEN或IN,但是,要使你的查询更接近自然语言,这两个表达式是有帮助的。
转换数据
SQL Sever足够强大,可以在需要的时候把大部分数值从一种类型转换为另一种类型。例如,要比较SMALLINT型和INT型数据的大小,你不需要进行显式的类型转换。SQL Sever会为你完成这项工作。但是,当你想在字符型数据和其它类型的数据之间进行转换时,你的确需要自己进行转换操作。例如,假设你想从一个MONEY型字段中取出所有的值,并在结果后面加上字符串“US Dollars”。你需要使用函数CONVERT(),如下例所示:
SELECTCONVERT(CHAR(8),price)+’US Dollars’FROM orders
函数CONVERT()带有两个变量。第一个变量指定了数据类型和长度。第二个变量指定了要进行转换的字段。在这个例子中,字段price被转换成长度为8个字符的CHAR型字段。字段price要被转换成字符型,才可以在它后面连接上字符串’US Dollars’。
当向BIT型,DATETIME型,INT型,或者NUMERIC型字段添加字符串时,你需要进行同样的转换操作。例如,下面的语句在一个SELECT语句的查询结果中加入字符串’The vote is’,该SELECT语句返回一个BIT型字段的值:
SELECT ‘The vote is’+CONVERT(CHAR(1),vote) FROMopinion
下面是这个语句的结果示例:
The vote is1
The vote is1
The vote is0
(3 row(s)affected)
如果你不进行显式的转换,你会收到如下的错误信息:
Implicitconversion from datatype ‘varchar’ to ‘bit’ is not allowec.
Use theCONVERT function to run this query.
操作字符串数据
SQL Sever有许多函数和表达式,使你能对字符串进行有趣的操作,包括各种各样的模式匹配和字符转换。在这一节中,你将学习如何使用最重要的字符函数和表达式。
匹配通配符
假设你想建立一个与Yahoo功能相似的Internet目录。你可以建立一个表用来保存一系列的站点名称,统一资源定位器(URL),描述,和类别,并允许访问者通过在HTML form中输入关键字来检索这些内容。
假如有一个访问者想从这个目录中得到其描述中包含关键字trading card的站点的列表。要取出正确的站点列表,你也许试图使用这样的查询:
SELECTsite_name FROM site_directory WHERE site_desc=’tradingcard’
这个查询可以工作。但是,它只能返回那些其描述中只有trading card这个字符串的站点。例如,一个描述为We have thegreatest collection of trading cards in the world!的站点不会被返回。
要把一个字符串与另一个字符串的一部分相匹配,你需要使用通配符。你使用通配符和关键字LIKE来实现模式匹配。下面的语句使用通配符和关键字LIKE重写了上面的查询,以返回所有正确站点的名字:
SELECTSITE_name FROM site_directory
WHEREsite_desc LIKE ‘%trading cark%’
在这个例子中,所有其描述中包含表达式trading card的站点都被返回。描述为We have the greatestcollection of trading cards in the world!的站点也被返回。当然,如果一个站点的描述中包含I am trading cardboard boxes online,该站点的名字也被返回。
注意本例中百分号的使用。百分号是通配符的例子之一。它代表0个或多个字符。通过把trading card括在百分号中,所有其中嵌有字符串trading card的字符串都被匹配。
现在,假设你的站点目录变得太大而不能在一页中完全显示。你决定把目录分成两部分。在第一页,你想显示所有首字母在A到M之间的站点。在第二页,你想显示所有首字母在N到Z之间的站点。要得到第一页的站点列表,你可以使用如下的SQL语句:
SELECTsite_name FROM site_directory WHERE site_name LIKE ‘[A-M]%’
在这个例子中使用了表达式[A-M],只取出那些首字母在A到M之间的站点。中括号([])用来匹配处在指定范围内的单个字符。要得到第二页中显示的站点,应使用这个语句:
SELECTsite_name FROM site_directory
WHEREsite_name LIKE ‘[N-Z]%’
在这个例子中,括号中的表达式代表任何处在N到Z之间的单个字符。
假设你的站点目录变得更大了,你现在需要把目录分成更多页。如果你想显示那些以A,B或C开头的站点,你可以用下面的查询来实现:
SELECTsite_name FROM site_directory WHERE site_name LIKE ‘[ABC]%’
在这个例子中,括号中的表达式不再指定一个范围,而是给出了一些字符。任何一个其名字以这些字符中的任一个开头的站点都将被返回。
通过在括号内的表达式中同时包含一个范围和一些指定的字符,你可以把这两种方法结合起来。例如,用下面的这个查询,你可以取出那些首字母在C到F之间,或者以字母Y开头的站点:
SELECTsite_name FROM site_directory WHERE site_name LIKE ‘[C-FY]%’
在这个例子中,名字为Collegescape和Yahoo的站点会被选取,而名字为Magicw3的站点则不会被选取。
你也可以使用脱字符(^)来排除特定的字符。例如,要得到那些名字不以Y开头的站点,你可以使用如下的查询:
SELECTsite_name FROM site_directory WHERE site_name LIKE ‘[^Y]%’
对给定的字符或字符范围均可以使用脱字符。
最后,通过使用下划线字符(_),你可以匹配任何单个字符。例如,下面这个查询返回每一个其名字的第二个字符为任何字母的站点:
SELECTsite_name FROM site_directory WHERE site_name LIKE ‘M_crosoft’
这个例子既返回名为Microsoft的站点,也返回名为Macrosoft的站点。但是,名字为Moocrosoft的站点则不被返回。与通配符’%’不同,下划线只代表单个字符。
注意:
如果你想匹配百分号或下划线字符本身,你需要把它们括在方括号中。如果你想匹配连字符(-),应把它指定为方括号中的第一个字符。如果你想匹配方括号,应把它们也括在方括号中。例如,下面的语句返回所有其描述中包含百分号的站点:
SELECTsite_name FROM site_directory WHERE site_desc LIKE ‘%[%]%’
匹配发音
MicrosoftSQL有两个允许你按照发音来匹配字符串的函数。函数SOUNDEX()给一个字符串分配一个音标码,函数DIFFERENCE()按照发音比较两个字符串。当你不知道一个名字的确切拼写,但多少知道一点它的发音时,使用这两个函数将有助于你取出该记录。
例如,如果你建立一个Internet目录,你也许想增加一个选项,允许访问者按照站点名的发音来搜索站点,而不是按名字的拼写。考虑如下的语句:
SELECTsite_name FROM site_directory
WHEREDIFFERENCE(site_name , ‘Microsoft’>3
这个语句使用函数DEFFERENCE()来取得其名字的发音与Microsoft非常相似的站点。函数DIFFERENCE()返回一个0到4之间的数字。如果该函数返回4,表示发音非常相近;如果该函数返回0,说明这两个字符串的发音相差很大。
例如,上面的语句将返回站点名Microsoft和Macrosoft。这两个名字的发音与Microsoft都很相似。如果你把上一语句中的大于3改为大于2,那么名为Zicrosoft和Megasoft的站点也将被返回。最后,如果你只需要差别等级大于1即可,则名为Picosoft和Minisoft的站点也将被匹配。
要深入了解函数DIFFERENCE()是如何工作的,你可以用函数SOUNDEX()来返回函数DIFFERENCE()所使用的音标码。这里有一个例子:
SELECTsite_name ‘site name’,SOUNDEX(site_name)‘sounds like’
这个语句选取字段site_name的所有数据及其音标码。下面是这个查询的结果:
site namesounds like
……………………………………………………………….
Yahoo Y000
Mahoo M000
MicrosoftM262
MacrosoftM262
MinisoftM521
MicroshoftM262
ZicrosoftZ262
ZaposoftZ121
MillisoftM421
NanosoftN521
MegasoftM221
PicosoftP221
(12 row(s)affected)
如果你仔细看一下音标码,你会注意到音标码的第一个字母与字段值的第一个字母相同。例如,Yahoo和Mahoo的音标码只有第一个字母不同。你还可以发现Microsoft和Macrosoft的音标码完全相同。
函数DIFFERENDE()比较两个字符串的第一个字母和所有的辅音字母。该函数忽略任何元音字母(包括y),除非一个元音字母是一个字符串的第一个字母。
不幸的是,使用SOUNDEX()和DIFFERENCE()有一个欠缺。WHERE子句中包含这两个函数的查询执行起来效果不好。因此,你应该小心使用这两个函数。
删除空格
有两个函数,TTRIM()和LTRIM(),可以用来从字符串中剪掉空格。函数LTRIM()去除应该字符串前面的所有空格;函数RTRIM()去除一个字符串尾部的所有空格。这里有一个任何使用函数RTRIM()的例子:
SELECTRTRIM(site_name) FROM site_directory
在这个例子中,如果任何一个站点的名字尾部有多余的空格,多余的空格将从查询结果中删去。
你可以嵌套使用这两个函数,把一个字符串前后的空格同时删去:
SELECTLTRIM(RTRIM(site_name) FROM site_directory
你会发现,在从CHAR型字段中剪掉多余的空格时,这两个函数非常有用。记住,如果你把一个字符串保存在CHAR型字段中,该字符串会被追加多余的空格,以匹配该字段的长度。用这两个函数,你可以去掉无用的空格,从而解决这个问题。
操作日期和时间
日期和时间函数对建立一个站点是非常有用的。站点的主人往往对一个表中的数据何时被更新感兴趣。通过日期和时间函数,你可以在毫秒级跟踪一个表的改变。
返回当前日期和时间
通过函数GETDATE(),你可以获得当前的日期和时间。例如,语句SELECTGETDATE()返回如下的结果:
……………………………..
NOV 30 19973:29AM
(1 row(s)affected)
显然,如果你将来使用这个函数,你得到的日期将比这个时间晚,或者梗早。
函数GETDATE()可以用来作为DATEDIME()型字段的缺省值。这对插入记录时保存当时的时间是有用的。例如,假设有一个表用来保存你站点上的活动日志。每当有一个访问者访问到你的站点时,就在表中添加一条新记录,记下访问者的名字,活动,和进行访问的时间。要建立一个表,其中的记录包含有当前的日期和时间,可以添加一个DATETIME型字段,指定其缺省值为函数GETDATE()的返回值,就象这样:
CREATE TABLEsite_log (
usernameVARCHAR(40),
useractivityVARCHAR(100),
entrydateDATETIME DEFAULT GETDATE())
转换日期和时间
你也许已经注意到,在上一节的例子中,函数GETDATE()的返回值在显示时只显示到秒。实际上,SQL Sever内部时间可以精确到毫秒级(确切地说,可以精确到3.33毫秒)。
要得到不同格式的日期和时间,你需要使用函数CONVERT()。例如,当下面的这个语句执行时,显示的时间将包括毫秒:
SELECTCONVERT(VARCHAR(30),GETDATE(),9)
注意例子中数字9的使用。这个数字指明了在显示日期和时间时使用哪种日期和时间格式。当这个语句执行时,将显示如下的日期和时间:
…………………………………..
Nov 30 19973:29:55:170AM
(1 row(s)affected)
在函数CONVERT()中你可以使用许多种不同风格的日期和时间格式。表11.1显示了所有的格式。
表11.1日期和时间的类型
类型值 标准 输出
0 Defaultmon dd yyyy hh:miAM
1 USAmm/dd/yy
2 ANSIyy.mm.dd
3British/French dd/mm/yy
4 Germandd.mm.yy
5 Italiandd-mm-yy
6 - dd monyy
7 - mondd,yy
8 - hh:mi:ss
Default +milliseconds--mon dd yyyy
hh:mi:ss:mmmAM(or)
10 USAmm-dd-yy
11 JAPANyy/mm/dd
12 ISOyymmdd
13 EuropeDefault + milliseconds--dd mon yyyy
hh:mi:ss:mmm(24h)
14 -hh:mi:ss:mmm(24h)
类型0,9,和13总是返回四位的年。对其它类型,要显示世纪,把style值加上100。类型13和14返回24小时时钟的时间。类型0,7,和13返回的月份用三位字符表示(用Nov代表November).
对表11.1中所列的每一种格式,你可以把类型值加上100来显示有世纪的年(例如,00年将显示为2000年)。例如,要按日本标准显示日期,包括世纪,你应使用如下的语句:
SELECTCONVERT(VARCHAR(30),GETDATE(),111)
在这个例子中,函数CONVERT()把日期格式进行转换,显示为1997/11/30
抽取日期和时间
在许多情况下,你也许只想得到日期和时间的一部分,而不是完整的日期和时间。例如,假设你想列出你的站点目录中每个站点被查询的月份。这时你不希望完整的日期和时间把网页弄乱。为了抽取日期的特定部分,你可以使用函数DATEPART(),象这样:
SELECTsite_name ‘Site Name’,
DATEPART(mm,site_entrydate)‘Month Posted’ FROMsite_directory
函数DATEPART()的参数是两个变量。第一个变量指定要抽取日期的哪一部分;第二个变量是实际的数据。在这个例子中,函数DATEPART()抽取月份,因为mm代表月份。下面是这个SELECT语句的输出结果:
Site NameMonth Posted
………………………………………………………………
Yahoo 2
Microsoft 5
Magicw3 5
(3 row(s)affected)
Month Posted列显示了每个站点被查询的月份。函数DATEPART()的返回值是一个整数。你可以用这个函数抽取日期的各个不同部分,如表11.2所示。
表11.2日期的各部分及其简写
日期部分 简写 值
year yy1753--9999
quarter qq1--4
month mm1--12
day of yeardy 1--366
day dd 1--31
week wk1--53
weekday dw1--7(Sunday--Saturday)
hour hh0--23
minute mi0--59
second ss0--59
milisecondms 0--999
当你需要进行日期和时间的比较时,使用函数DATEPART()返回整数是有用的。但是,上例中的查询结果(2,5)不是十分易读。要以更易读的格式得到部分的日期和时间,你可以使用函数DATENAME(),如下例所示:
SELECTsite_name ‘Site Name’
DATENAME(mm,site_entrydate)‘Month Posted’
FROMsite_directory
函数DATENAME()和函数DATEPART()接收同样的参数。但是,它的返回值是一个字符串,而不是一个整数。下面是上例该用DATENAME()得到的结果:
Site NameMonth Postec
………………………………………………………………….
YahooFebruary
MicrosoftJune
Magicw3 June
(3 row(s)affected)
你也可以用函数DATENAE()来抽取一个星期中的某一天。下面的这个例子同时抽取一周中的某一天和日期中的月份:
SELECTsite_name ‘Site Name’,
DATENAME(dw,site_entrydate)+‘-’ + DATENAME(mm,site_entrydate)
‘Day and Month Posted’ FORM site_directory
这个例子执行时,将返回如下的结果:
Site NameDay and Month Posted
………………………………………………………………………
Yahoo Friday- February
MicrosoftTuesday - June
Magicw3Monday - June
(3 row(s)affected)
返回日期和时间范围
当你分析表中的数据时,你也许希望取出某个特定时间的数据。你也许对特定的某一天中――比如说2000年12月25日――访问者在你站点上的活动感兴趣。要取出这种类型的数据,你也许会试图使用这样的SELECT语句:
SELECT *FROM weblog WHERE entrydate=”12/25/20000”
不要这样做。这个SELECT语句不会返回正确的记录――它将只返回日期和时间是12/25/200012:00:00:000AM的记录。换句话说,只有刚好在午夜零点输入的记录才被返回。
注意:
在本节的讨论中,假设字段entrydate是DATETIME型,而不是SMALLDATETIME型。本节的讨论对SMALLDATETIME型字段也是适用的,不过SMALLDATETIME型字段只能精确到秒。
问题是SQL Sever将用完整的日期和时间代替部分日期和时间。例如,当你输入一个日期,但不输入时间时,SQL Sever将加上缺省的时间“12:00:00:000AM”。当你输入一个时间,但不输入日期时,SQL Sever将加上缺省的日期“Jan 1 1900”。
要返回正确的记录,你需要适用日期和时间范围。有不止一种途径可以做到这一点。例如,下面的这个SELECT语句将能返回正确的记录:
SELECT *FROM weblog
WHEREentrydate>=”12/25/2000” AND entrydate<”12/26/2000”
这个语句可以完成任务,因为它选取的是表中的日期和时间大于等于12/25/2000 12:00:00:000AM并小于12/26/200012:00:00:000AM的记录。换句话说,它将正确地返回2000年圣诞节这一天输入的每一条记录。
另一种方法是,你可以使用LIKE来返回正确的记录。通过在日期表达式中包含通配符“%”,你可以匹配一个特定日期的所有时间。这里有一个例子:
SELECT *FROM weblog WHERE entrydate LIKE ‘Dec 25 2000%’
这个语句可以匹配正确的记录。因为通配符“%”代表了任何时间。
使用这两种匹配日期和时间范围的函数,你可以选择某个月,某一天,某一年,某个小时,某一分钟,某一秒,甚至某一毫秒内输入的记录。但是,如果你使用LIKE来匹配秒或毫秒,你首先需要使用函数CONVERT()把日期和时间转换为更精确的格式(参见前面“转换日期和时间”一节)。
比较日期和时间
最后,还有两个日期和时间函数对根据日期和时间取出记录是有用的。使用函数DATEADD()和DATEDIFF(),你可以比较日期的早晚。例如,下面的SELECT语句将显示表中的每一条记录已经输入了多少个小时:
SELECTentrydate ‘Time Entered’
DATEDIFF(hh,entrydate,GETDATE())‘Hours Ago’ FROM weblog
如果当前时间是2000年11月30号下午6点15分,则会返回如下的结果:
Time EnteredHours Ago
…………………………………………………..
Dec 30 20004:09PM 2
Dec 30 20004:13PM 2
Dec 1 20004:09PM 698
(3 row(s)affected)
函数DADEDIFF()的参数是三个变量。第个变量指定日期的某一部分。在这个例子中,是按小时对日期进行比较,(要了解日期各部分的详细内容,请参考表11.2)在日期2000年11月1日和2000年11月30日的指定时间之间有689个小时。另外两个参数是要进行比较的时间。为了返回一个正数,较早的时间应该先给。
函数DATEADD()把两个日期相加。当你需要计算截止日期这一类的数据时,这个函数是有用处的。例如,假设访问者必须先注册才能使用你的站点。注册以后,他们可以免费使用你的站点一个月。要确定什么时候他们的免费时间会用完,你可以使用如下的SELECT语句:
SELECTusername ‘User Name’,
DATEADD(mm,1,firstvisit_date)‘Registration Expires’
FROMregistration_table
函数DATEADD()的参数有三个变量。第一个变量代表日期的某一部分,这个例子用到了代表月份的mm。第二个变量指定了时间的间隔――在本例中是一个月。最后一个变量是一个日期,在这个例子中,日期是取自DATETIME型字段firstvisit_date.假设当前日期是June 30,2000,这个语句将返回如下的内容:
User NameRegistration Expires
……………………………………………………………………………
Bill GatesJul 30 2000 4:09PM
PresidentClinton Jul 30 2000 4:13PM
WilliamShakespeare Jul 1 2000 4:09PM
(3 row(s)affected)
注意:
与你预料的相反,使用函数DATEADD()把一个日期加上一个月,它并不加上30天。这个函数只简单地把月份值加1。这意味着在11月注册的人将比在2月注册的人多得到2天或3天的时间。要避免这个问题,你可以用函数DATEADD()直接增加天数,而不是月份。
发送邮件
你可以用SQL Sever发送简单的e_mail信息。要做到这一点,你需要在你的系统中安装邮件服务器,如Microsoft Exchange Sever(参见第四章“ExchangeActive Sever,Index Sever,和NetShow”)。你还需要配置SQL Sever以识别邮件服务器。
要让SQL Sever能识别邮件服务器,启动事务管理器并从菜单中选择Sever|SQLMail|Configue,这时会出现一个如图11.3所示的对话框。输入你在邮件服务器中注册的用户名和口令,然后单击OK。
注意:
如果你使用Microsoft Exchange Sever,配置SQL Sever的过程将会大大不同。你需要在同一个(域)用户帐号下运行Microsoft SQL Sever和Exchange Sever。你还需要在安装了SQL Sever的机器上安装Exchange Cliect并给这个帐号创建一个一个配置文件。完成这些之后,你就可以在SQL Mail Configuration对话框中输入该配置文件的名字。
图11。3
在发送邮件之前,你要先启动SQL Mail。从菜单中选择Sever|SQL Mail|Start。如果你的邮件服务器配置正确,并且你输入了正确的用户名和口令,则SQL Mail会成功启动。
注意:
你可以把SQL Sever配置为自动启动邮件服务。要做到这一点,在Set SeverOptons对话框(从菜单中选择Sever|SQL Sever|Configure)中选择Auto Start Mail Client即可。
要发送一个邮件,你可以使用名为xp_sendmail的扩展存储过程。这里有一个如何使用这个过程的例子:
master..xp_sendmail“president@whitehouse.gov”,”Hello Mr. President”
这个过程调用向e_mail地址president@whitehouse.gov发送一个简单的email信息:“Hello Mr. President”。你可以用任何其它的email地址和信息取代上例中相应的内容,但是,你所发送的信息不能超过255个字符长度。
当你想随时了解你的站点数据库的状态时,存储过程xp_sendmail是有用处的。例如,你可以向一个页面管理程序发送信息。如果你的站点出了什么问题,你马上就可以知道。下一章将讲述更多有关存储过程的内容。
总结
这一章加深了你的SQL知识。你学会了如何建立索引,使你的查询速度更快。你还学会了如何插入,删除和更新一个表中的数据,如何使用集合函数得到一个表中数据的统计信息。最后,你学会了许多有价值的表达式,函数和过程,用来操作字符串,日期和时间及邮件。
下一章将进一步加深你对Microsoft SQL Sever的掌握。你将学习如何用SQL来进行程序设计,如何建立存储过程,触发器和执行计划。更另人兴奋的是,你将学会让SQL Sever自动创建网页的一个简单方法。
SQL功能与特性
其实,在前面的文章中,已经提及SQL命令的一些基本功能,然而,通过SQL命令,程序设计师或数据库管理员(DBA)可以:
(一)建立数据库的表格。(包括设置表格所可以使用之空间)
(二)改变数据库系统环境设置。
(三)针对某个数据库或表格,授予用户存取权限。
(四)对数据库表格建立索引值。
(五)修改数据库表格结构。(新建、删除或是修改表格字段)
(六)对数据库进行数据的新建。
(七)对数据库进行数据的删除。
(八)对数据库进行数据的修改。
(九)对数据库进行数据的查询。
这几项便是通过SQL命令可以完成的事情,看起来是不是比起“查询”两个字所代表的功能要多的多了呢?
SQL语法的分类
其实SQL命令并不是非常多,可是要把SQL用到出神入化,却也只需要短短几个命令便够,因为SQL命令是针对关系型数据库所建立出来的语法叙述,所以SQL在这类数据库中所发挥的功能非常的强,以下将针对在VB中常用的SQL语法基本命令加以分类介绍。在说明SQL的命令以及使用语法之前,以下将SQL做了的分类,在致上SQL语法所使用到的类型,可以说都已包含在这些类别当中。
第一类、属性词(Predicates)
在SQL命令中用来指明所要选择的记录的方式。如ALL、TOP与DISTINCT等等。
第二类、声明(Declaration)
针对SQL Parameter或Parameter Query的名称与数据类型做声明,如PARAMETERS的声明等等。
第三类、条件子句(Clause)
在SQL的查询中,利用一些表达式定义出查询的条件,以缩小寻找的范围,如WHERE。
第四类、运算符(Operator)与操作数(Operation)
在SQL的查询中,与Operation共同组成表达式(Expression),如BETWEEN....AND运算符与INNER JOIN操作数。
第五类、函数(Function)
一些SQL常见的函数,像是AVG()是求算数平均数的函数。
第六类、SQL语句(Statement)
SQL的语句,可以说是SQL语法的主体,用来对某一个特定的数据库发出指示,并返回相关的数据,而SQL的语法结构,基本上可以利用下面
的式子来表示:命令+条件子句
例如:
SELECT*FROMTAB WHERE TAB.NAME='A'
其中的“FROM....WHERE”便是一个条件子句,其实SQL的语法并不难,您只需记住这样的一个规则,相信可以很快的了解SQL用法。
SQL语法与命令
SELECT语句
SELECT[predicate]{*|table.*|[table.]field[,[table.]field2[,...]]} [AS alias1 [,alias2[,...]]]
FROMtableexpression [,...][IN externaldatabase]
[WHERE...]
[GROUPBY...]
[HAVING...]
[ORDERBY...]
[WITHOWNERACCESS OPTION]
SELECT语句包括下面几个部分
predicate
如前面所述,包括了ALL,DISTINCT,DISTINCTROW,与TOP我们可以利用这样的语句去限制查询后所得的结果。
*
从指定表格中指定所有的字段。
table
针对被选择出的记录的字段,所指定表格的名称。
field1,field2
想要读取数据的字段名称,如果包含了一个以上的字段,会依照列出的顺序来读取数据。
alias1,alias2
用来替代在表格实际字段名称的化名。
tableexpression
表格名称或包含我们所想要的数据的表格。
externaldatabase
若使用到不是目前的数据库则将其名字定义在externaldatabase当中。
ALL,DISTINCT,DISTINCTROW,TOP属性词用法
SELECT [ALL|DISTINCT|DISTINCTROW|[TOPn[PERCENT]]] FROM table
ALL
若是您不指定任何的字段数据,则Microsoft Jet数据库引擎(database engine)将会选择所有的字段,并依据所定的条件查询出需求数据集。
例如下面这两个例子将会具有相同的效果,都会从职员表格中返回所有字段的数据。
例如:
若是我们要查询出职员表格中的所有记录,可以通过下面的语句来完成。
SELECT ALL*FROM职员表格; DISTINCT
对某个表格所选择的字段数据,略过重复的情况,也就是说,针对某个字段查询出来的记录结果是唯一的。例如有许多存放在职员表格的职员
数据,也许会具有相同的姓名,所以若是我们用SQL语句中的SELECT DISTINCT,则查询出来的结果将会针对不一样的姓名加以筛选。若是您把
DISTINCT加以省略,则这样的查询会显示所有的记录。
DISTINCTROW
将整条记录重复的记录忽略掉,而不是只有针对某一个字段的数据。
table
指定查询记录所需要的表格。
例如:
SELECTDISTINCTROW公司名称
FROM顾客表格 INNER JOIN订单表格
ON顾客表格.顾客ID=订单表格.顾客ID
ORDER BY公司名称;
如果您忽略 DISTINCTROW则会对每个公司产生一行以下的订单数据。此外,若是DISTINCTROW只有用在一个表格当中,则会被省略掉。
TOP
从第一条或最后一条开始(利用ORDER BY条件子句),返回特定条数的数据。
例如:
当您想要知道在2000年,班上前25名的学生姓名数据时,您可以输入这样的语句:
SELECT TOP25学生姓名
FORM学生表格
WHERE毕业年份=1994
ORDER BY毕业成绩平均分数 DESC;
如果您没有加上ORDER BY这行条件的话,您所得到的数据,将会随机的数据。此外,在TOP语句之后,除了可以加上数字以外,还可以利用保留
字PERCENT来查询。
例如:
SELECT TOP10 PERCENT学生姓名
FROM学生表格
WHERE毕业年份=1994
ORDER BY毕业成绩平均DESC;
PARAMETERS(参数)声明的用法
对于参数型的查询语法中,对参数的名称以及数据类型作声明的操作。
PARAMETERSname datatype[,name datatype[,...]]
name
PARAMETERS的名称。您可以把参数名称当作字符串来使用,若是名称中包含了空字符串,可以利用中括号来处理,例如:“VBeden”。
datatype
输入参数的数据类型。
例如:
若是您在查询时,需要机动的输入姓名,可以利用下列的方式完成:
PARAMETERS “输入姓名” Text;
SELECT*
FROM职员表格
WHERE姓名=“输入姓名:”;
ORDER BY条件语句
此条件子句,通常与SELECT语句合并使用目的是将查询的结果,依照指定字段加以排序。
SELECT fieldlist
FROM table
WHERE selectcriteria
ORDER BY field[ASC|DESC][,field2[ASC|DESC][,...]]
fieldlist
欲查询的字段名称。其中可以与ALL,DISTINCT,DISINCTROW,或TOP一起来使用。
table
欲查询的表格名称。
selectcriteria
查询的标准设置。
field1
指定要依照那个字段作为排序的依据,若是你没有加上ORDER BY查询出的数据集将不会作排序的操作。
ASC
递增顺序类别。(默认值)
DESC
递减顺序类别。
例如:
或是我们要将输出数据依据出生的先后次序排列,可以利用下面的命令。
SELECT姓名,生日
FROM职员表格
ORDER BY生日
SELECT LastName,FirstName
FROM Employees
ORDER BY LastName ASC;
IN条件子句
指定要速胜哪一个外部数据库的表格。(必须是Microsoft Jet数据库引擎所可以连接的数据库,如dBase,Paradox等等)
SELECT|INSERT]INTOdestination IN
{path|["path""type"]|[""[type;DATABASE=path]]}
FROMtableexpression IN
{path|["path""type"]|[""[type;DATABASE=path]]}
destination
欲插入数据的外部表格名称。
tableexpression
表格名称或是被读取数据的表格名称。这个参数可以是一个单一的表格名称,或是一段已经被存储的SQL查询等。
path
包含该表格的完整路径名称。
type
数据库的类型名称, 通常是当数据库部属于Jet database时才会使用。(例如:dBASE III,dBASE IV,Paradox 3.x,Paradox 4.x,或Btrieve)
例如:下面这两段的意义相同
PartA....FROMTable
IN""[dBASE IV;DATABASE=C:\DBASE\DATA\SALES;];
PartB....FROMTable
IN"C:\DBASE\DATA\SALES" "dBASE IV;"
例如:Microsoft Jet database
SELECT顾客编号
FROM顾客表格
INCUSTOMER.MDB
WHERE顾客编号 Like "A*";
其中CUSTOMER.MDBO为Jet database的数据库名称,其中包含了顾客表格。
例如:dBASE III or IV
SELECT顾客编号
FROM顾客表格
IN"C:\DBASE\DATA\SALES" "dBASE IV;"
WHERE顾客编号 Like "A*";
所以当我们使用不同于ACCESS的数据库时,必须指明该数据库的类型名称。
HAVING条件子句
指定一特定的分组记录,并满足HAVING所指定的条件或状态,但条件是针对分组的条件设置。
SELECTfieldlist
FROM table
WHEREselectcriteria
GROUP BYgroupfieldlist
HAVINGgroupcriteria
fieldlist
显示被查询的字段名称。(可与ALL,DISTINCT,DISTINCTROW,或TOP相结合)
table
欲查询数据的表格名称。
selectcriteria
选取标准。
groupfieldlist
分组记录的字段名称,到多10个字段。而这些字段的顺序决定最高到最低的分组阶层。
groupcriteria
决定什么样的分组记录要被显示。
HAVING跟WHERE的用法相当类似,不同之处在于HAVING必须用于GROUP之后的分组数据上。
例如:
SELECT分类编,Sum(库存数量)
FROM产品表格
GROUP BY分类编号
HAVING Sum(库存数量)>100 AND产品名称 LIKE "*纸";
GROUP BY条件子句
依据指定的字段,将具有相同数值的记录合并成一条。
SELECTfieldlist
FROM table
WHEREcriteria
GROUP BYgroupfieldlist
fieldlist
欲读取的字段名称。(可与ALL,DISTINCT,DISTINCTROW,或TOP合并使用)
table
被查询的表格名称。
groupfieldlist
分组记录的字段名称,到多10个字段,而这些字段的顺序决定最高到最低的分组层次。
例如:
SELECT姓名,Count(姓名)AS职员姓名
FROM职员表格
WHERE部门名称='业务部'
GROUP BY姓名
FROM条件子句
指定表格名称或是查询,其中包含列在SELECT语句的字段数据。
SELECTfieldlist
FROMtableexpression[IN externaldatabase]
fieldlist
表格中的字段名称。(可与ALL,DISTINCT,DISTINCTROW,或TOP相结合)
tableexpression
表格名称,或多个表格的算式。
externaldatabase
若该表格参考到外部的数据库时,将其完整的路径名称记下。
例如:
从职员表格下,查询出所有姓名字段的数据(只有姓名字段被查询,其他则不显示)。
SELECT姓名 FROM职员表格;
WHERE条件子句
指定查询的条件与限制。
SELECTfieldlist
FROMtableexpression
WHEREcriteria
fieldlist
字段名称。(可与ALL,DISTINCT,DISTINCTROW,或TOP相结合)
tableexpression
表格名称,或多个表格的算式。
criteria
查询的结果,必须依照这一限制标准。
例如:
要查询出职员表格中,所有姓氏是李的数据,可以用下面的语句。
SELECT姓名
FROM职员表格
WHERE姓氏='李';
BETWEEN...AND运算符
决定某一人数值是否介于特定的范围之内,此运算符只可以用在SQL的语句中。
expr[Not]BETWEENvalue1 AND value2
expr
指定要加以计算的字段与表达式的组合。
value1,value2
所指明的数值范围。
例如:
若是要从职员表格查询出所有年龄介于25-30岁的员工,可以利用下面的程序来做。
SELECT姓名,年龄 BETWEEN 25 AND 30
FROM职员表格;
LIKE操作数
用来将一字符串与另一特定字符串样式(pattern)比较,并将符合该字符串样式的记录过滤出来。
expressionLIKE "pattern"
expression
使用在WHERE条件子句,SQL表达式。
pattern
用以比较的字符串样式。
例如:
若是你要查询出所有以“李”为首的姓氏,可以利用下面的式子。
Like "李*"
LIKE操作数的多种范例:
1、多个字符:
(1)"a*a"
可筛选:"aa","aBa","aBBBa",不能筛选:"aBC"
(2)"*ab*"
可筛选:"abc","AABB","Xab",不能筛选:"aZb","bac"
2、特殊字符:
"a“*”a"
可筛选:"a*a",不能筛选:"aaa"
3、单一字符:
"a?a"
可筛选:"aaa","a3a","aBa",不能筛选:"aBBBa"
4、单一数字:
"a#a"
可筛选:"a0a","a1a","a2a",不能筛选:"aaa","a10a"
5、字符范围:
"“a-z”"
可筛选:"f","p","j",不能筛选:"2","&"
6、指定字符以外部范围:
"“!a-z”"
7、指定非数字:
"“!0-9”"
可筛选:"A","a","&","~",不能筛选:"0","1","9"
8、组合式结构:
"a“!b-m”#"
可筛选:"An9","az0","a99",不能筛选:"abc","aj0"
SQL数字函数
1、AVG:算数平均数
AVG(expr)
expr
字段名称或表达式。
例如:
若要计算职员身高超过165厘米的职员平均身高,可以利用下面的SQL语句来完成。
SELECT Avg(身高)
AS平均身高
FROM职员表格 WHERE身高>165;
2、COUNT:计算记录条数
COUNT(expr)
expr
字段名称或表达式。
例如:
若是要统计出业务部门的职员人数,并查询出职员的姓名,可以利用下面的程序。
SELECTCount(姓名) AS职员姓名
FROM职员表格
WHERE部门名称='业务部';
3、FIRST与LAST:返回某字段的第一条数据与最后一条数据。
FIRST(expr)
LAST(expr)
expr
字段名称或表达式。
例如:
若是要找出货品数量字段的第一条数据与货品价格字段的最后一条数据时,可以利用下面的查询方式。
SELECTFIRST(货品数量),LAST(货品价格)
FROM订单表格
4、MAX,与MIN:返回某字段的最大值与最小值。
用法同FIRST与LAST。
5、SUM:返回某特定字段或是运算的总和数值。
SUM(expr)
expr
字段名称或表达式。
例如:
要计算出货品总价,可使用下面的程序。
SELECT
Sum(单位价格*货品数量)
AS货品总价 FROM订单表格
多层SQL查询
顾名思义,多层的SQL查询的便在于:“在一个SQL语句中可以包含另一个SQL查询语句,形成内部嵌套的查询类型。”
comparison[ANY|ALL|SOME](sqlstatement)
expression[NOT]IN(sqlstatement)
[NOT]EXISTS(sqlstatement)
comparison
将表达式与内层查询的结果比较的操作。
expression
对内层查询的结果作搜索的表达式。
sqlstatement
为SELECT语句构成的SQL查询,必须用()将该语句括起来。
例如:
我们先从订单表格当中,查询出所有的单位,再将产品表格中的单位与的一一对比,查询出所有高于订单表格的单位价格的记录。
SELECT *FROM产品表格
WHERE单位价格>ANY (SELECT单位价格 FROM订单表格 WHERE折扣>=.25);
SQL与数据库的维护
表格的建立
将SQL中的基本语法作了一番介绍以后,但大多是偏向于数据库数据的查询与过滤,但实际上,我们通过SQL命令所可以做的事还有很多,接下来要介绍的便是如何利用SQL的语法命令来建立一个数据库中的表格。
CREATE TABLE语句
我们可以利用这个命令,来建立一个全新的表格,但前提则是:数据库必须已经存在。
CREATE TABLEtable(field1 type[(size)][index1][,field2type[(size)][index2][,...]][,nultifieldindex[,...]])
table
欲建立的新的表格名称。
field1,field2
在新表格中的新的字段名称,到少要一个字段以上。
type
字段的数据类型。
size
字段的大小。
index1,index2
利用CONSTRAINT条件子句定义一个单一字段的索引名称。
multifieldindex
利用CONSTRAINT条件子句定义一个多重字段的索引名称。
例如:
建立一个拥有职员姓名与部门字段的表格。
CREATE TABLE职员表格 (姓名TEST,部门TEST,职员编号 INTEGER CONSTRAINT职员字段索引PRIMARY KEY)
在这一个范例中,我们建立了一个表格名称为“职员表格”的表格,并且定义了该表格的主键值,以限制数据不能重复输入。
表格索引的建立
CREATE INDEX语句
这个命令主要是对一个已存在的表格建立索引,其用法如下:
CREATE[UNIQUE]INDEXindex ON table(field[ASC|DESC][,field[ASC|DESC],...])
[WITH{PRIMARY|DISALLOWNULL|IGNORENULL}]
index
欲被建立的索引名称。
table
欲建立索引的表格名称。
field
欲被建立的索引的字段名称。并可通过DESC保留字,来决定索引的顺序。
例如:
在职员表格中建立一个索引。
CREATE INDEX新索引名称
ON职员表格(姓名部门);
表格的字段更新
CONSTRAINT条件子句
CONSTRAINT的功能是类似索引(INDEX)的,虽然CONSTRAINT也可以建立表格之间的关联性。
单一字段索引:
CONSTRAINTname{PRIMARY KEY|UNIQUE|REFERENCES foreigntable[(foreignfield1,foreignfield2)]}
多字段索引:
CONSTRAINTname
{PRIMARYKEY(primary1[,primary2[,...]])
|UNIQUE(unique1[,unique2[,...]])
|FOREIGN KEY(ref1[,ref2[,...]])
|REFERENCESforeigntable[(foreignfield1[,foreignfield2[,...]])]}
name
要被建立的CONSTRAINT名称。
primary1,primary2
被用来设计成主键值的字段名称(可一个以上)。
unique1,unique2
被用来设计成唯一键值的字段名称(可一个以上)。
foreign key
字段名称,或是参考到别的表格中字段的字段名称。
foreigntable
如前所述,被参考到的表格。
foreignfield1,foreignfield2
在参考到的表格当中,被ref1,ref2字段所指定的字段。如果被参考的字段是参考表格中的主键值,你也可以省略这个条件子句。
例如:
当我们要建立一个新的职员数据表格,表格包含姓名、部门名称与生日三个字段,且由这三个字段建立一个唯一的索引时,可以使用下面这段SQL的语句。
CREATE TABLE职员数据表格
(姓名 TEST,部门名称 TEST,生日DATETIME,CONSTRAINT职员数据表格限制UNIQUE(姓名,部门名称,生日));
以上是SQL中,与数据库表格建立相关的命令,你可以利用这些命令,通过SQL的语句,将数据库表格完整的建立出来,接下来的章节,将针对数据库建立之后的维护与增删所要使用的SQL语句作一介绍。
表的删除
DELETE语句
我们可以利用DELETE语句,将表格中的记录删除。(注意:记录被删除后,无法再复原,所以条件设置要正确)
DELETE[table.*]
FROMtableexpression
WHEREcriteria
table
欲删除记录的表格名称,也可以用*来取代。
tableexpression
一个或一个以上表格的名称。此一参数可以为单一的表格名称或是从INNER JOIN,LEFT JOIN,或RIGHTJOIN等运算所得到的结果。
criteria
决定表格中记录要被删除的标准。
例如:
若是我们要将职员表格中姓名姓名叫做'李名'的记录删除,我们可以利用下面的SQL语句来完成。
DELETE *FROM职员表格
WHERE姓名='李名';
数据库表格相关的操作命令
SQL除了可以作为查询与数据库表格的建立的工具外,对于数据库与表格的新建、删修、与维护,与具有相当不错的功能,若是读者使用SQL命令得宜,对于整个效率的提高有着很大的帮助,所以对于SQL语句所带来的优势,常常会遇到一个情况,就是:“当我们对多个表格作复杂与多步骤的处理时,或许SQL只要一个语句就可以完成所有的需求与目标”,乍看一下,或许觉得有些玄妙,但是接下来的章节,会让你了解其中的妙处。
SELECT...INTO语句
我们可以通过这个命令,利用既存表格查询,来建立一个新表格的查询语句。
SELECTfield1[,field2[,...]]INTO newtable[IN externaldatabase]
FROM source
field1,field2
欲拷贝到新表格的字段名称。
newtable
欲建立之新表格的名称,不可是已经存在的表格。
externaldatabase
若是该表格在另外的外部数据库时,该数据库的名称。
source
记录数据拷贝的来源表格名称,可以是单一的表格或是一段SQL查询之语句。
例如:
你可以通过下面的SQL语句,来建立一个新的“训练名册”表格。
SELECT职员表格.姓名,职员表格.部门
INTO训练名册 FROM职员表格
WHERE职称='新进人员';
INNER JOIN操作数
当某一个共同的字段数据相等时,将两个表格的记录加以组合。
SELECTfields
FROM table1INNER JOIN table2
ONtable1.field1 compopr table2.field2
table1,table2
欲进行记录组合的表格名称。
field1,field2
欲组合的字段名称。(必须具有相同的数据类型)
compopr
比较关系运算符如下:“=”,“<”,“>”,“<=”,“<>”等。
例如:
若是你要把分类表格与产品表格作组合,可参考下面的SQL语句。
SELECT分类名称,产品名称
FROM分类表格 INNER JOIN产品表格
ON分类表格.分类编号=产品表格.分类编号;
UNION操作数
我们可以通过UNION操作数来建立连接的查询条件,UNION操作数可以将两个以上的表格或是查询的结果组合起来。
[TABLE]query1UNION [ALL][TABLE]query2 [UNION [ALL]
[TABLE]queryn[...]]
query1,query2,queryn
为一个SELECT的语句,或是一个已存在的查询名称,或是一个已存在的表格名称。
例如:
你可以利用下面的SQL语句,将订单数量超过1000的顾客表格记录,与新客户表格作UNION的操作。
TABLE新客户表格 UNION ALL
SELECT *
FROM顾客表格
WHERE订单数量>1000;
ALTER语句
在一个表格被建立之后,利用ALTER语句,我们可以去修改表格的字段设计。
ALTER TABLEtable
{ADD {COLUMNfield type[(size)][CONSTRAINT index]
|CONSTRAINTmultifieldindex}
|DROP{COLUMN field|CONSTRAINT indexname}}
table
欲被ALTER的表格名称。
field
要被增加或删除的字段名称。
type
字段数据类型。
size
字段大小。
index
对此字段的索引。
例如:
在职员表格中新建一个“薪水”的字段。
ALTER TABLE职员表格
ADD COLUMN薪水 CURRENCY;
例如:
在职员表格中删除一个“薪水”的字段。
ALTER TABLE职员表格 DROP COLUMN薪水;
DROP语句
针对所指定的表格或字段加以删除,或是把索引删除。
DROP {TABLEtable|INDEX index ON table}
table
欲删除之表格或索引依附之表格名称。
index
欲从表格中删除的索引名称。
例如:
从职员表格中,删除编号索引。
DROP INDEXMyIndex ON Employees;
例如:
从数据库中,删除整个表格。
DROP TABLE职员表格;
INSERT INTO语句
新建一条数据到表格当中。
多条记录新建查询:
INSERT INTOtarget [IN externaldatabase][(field1[,field2[,...]])]
SELECT[source.]field1[,field2[,...]
FROMtableexpression
单条记录新建查询:
INSERT INTOtarget[(field1[,field2[,...]])]
VALUES(value1[,value2[,...])
target
新建记录的表格名称。
externaldatabase
外部数据库的路径,搭配IN条件子句使用。
source
若是记录从其它表格中拷贝时,指明该表格的名称。
field1,field2
欲增加数据的字段名称。
tableexpression
表格名称或描述记录是从哪一个表格中插入的表格名称。配合INNER JOIN,LEFT JOIN,或 RIGHT JOIN运算符一起使用。
value1,value2
欲插入表格中的数值。
例如:
在客户数据表格中,从新的表格插入数据。
INSERT INTO客户数据表格
SELECT新客户数据表格.*
FROM新客户数据表格;
例如:
在职员表格中插入数据。
INSERT INTO职员表格(姓名,生日,职称)
VALUES("王荣","57/12/11","经理");
例如:
从训练人员表格中,将职员雇用时间超过30天者,加入到正式职员表格中。
INSERT INTO职员表格
SELECT训练人员表格.*
FROM训练人员表格
WHERE雇用天数>30;
UPDATE语句
建立一个UPDATE的查询,通过条件的限制来修改特定的数据。
UPDATE table
SET newvalue
WHEREcriteria;
table
欲修改数据的表格名称。
newvalue
欲修改成的数值(将该项数值插入到特定的字段当中)。
criteria
查询条件,用来决定要修改哪些记录。
例如:
若是要把订单表格中的订单数量修改成1.1倍,运费为1.03倍,可利用下列之SQL语句来完成。
UPDATE订单表格
SET订单数量=订单数量 * 1.1,运费=运费 * 1.03倍
WHERE运达地点='美国';
当我们完成修改后,你可以利用 SELECT语句,配合同样的 WHERE条件子句,来察看修改的数据是否正确。
事实上,要利用SQL完成某些看似复杂的操作,并不需要繁琐的SQL命令组合,或是许许多多的步骤才能完成,其实最重要的还是要活用SQL命令,才会在最精简的SQL语句里获得最高的效率。
1. CREATE DATABASE database_name [WITH LOG IN “pathname”]
创建数据库。
database_name:数据库名称。
“pathname”:事务处理日志文件。
创建一database_name.dbs目录,存取权限由GRANT设定,无日志文件就不能使用
BEGINWORK等事务语句(可用START DATABASE语句来改变)。
可选定当前数据库的日志文件。
如:select dirpath form systables where tabtype = “L”;
例:create databse customerdb with log in “/usr/john/log/customer.log”;
1 DATABASEdatabse-name [EXCLUSIVE]
选择数据库。
database_name:数据库名称。
EXCLUSIVE:独占状态。
存取当前目录和DBPATH中指定的目录下的数据库,事务中处理过程中不要使用此语句。
例:dtabase customerdb;
3. CLOSE DATABASE
关闭当前数据库。
database_name:数据库名称。
此语句之后,只有下列语句合法:
CREATEDATABASE; DATABASE; DROPDATABSE; ROLLFORWARD DATABASE;
删除数据库前必须使用此语句。
例:close database;
4. DROP DATABASE database_name
删除指定数据库。
database_name:数据库名称。
用户是DBA或所有表的拥有者;删除所有文件,但不包括数据库目录;不允许删除当前数据库(须先关闭当前数据库);事务中处理过程中不能使用此语句,通过ROLLBACK WORK也不可将数据库恢复。
例:drop databse customerdb;
5. CREATE [TEMP] TABLE table-name(column_name datatype [NOTNULL], …)
[IN“pathname”]
创建表或临时表。
table-name:表名称。
column_name:字段名称。
data-type:字段数据类型。
path-name:指定表的存放位置
TEMP用于指定建立临时表;表名要唯一,字段要唯一;有CONNECT权限的用户可建立临时表;创建的表缺省允许CONNECT用户存取,但不可以ALTER。
例:create table user
(c0 serial not null, c1 char (10),
c2char(2),
c3smallint,
c4decimal(6,3),
c5date
)in “usr/john/customer.dbs/user;
6. ALTER TABLE
ALTERTABLE table-name
{ADD(newcol_name newcol_type [BEFORE oldcol_name],…) | DROP (oldcol_name,…)
|MODIFY (oldcol_name newcol_type [NOT NULL], … )}, …
修改表结构。
table-name:表名称。
newcol_name:新字段名称
newcol_type:新字段类型
oldcol_name:老字段名称
可以使用单个或多个ADD子句、DROP子句、MODIFY子句,但某个字句失败,操作即中止;原字段是NULL,不允许MODIFY为NOTNULL,除非所有NULL字段中均非空,反之可以;ALTER使用者是表的拥有者或拥有DBA权限,或被授权;事务中处理过程中不要使用此语句。
例:alter table user
add( c6 char(20) before c5);
7. RENAME TABLE oldname TO newname
修改表名。
oldname:原名称。
newname:新名称。
RENAME使用者是表的拥有者或拥有DBA权限,或被授权;事务中处理过程中不要使用此语句。
例:rename user to bbb;
8. DROP TABLE table-name
删除表。
table-name:表名称。
删除表意味着删除其中所有数据、各字段上的索引及对表的赋权、视图等;用户不能删除任何系统目录表;语句使用者是表拥有者或拥有DBA权限,事务中处理过程中不要使用此语句。
9. RENAME COLUMN table.oldcolumn, TO newcolumn
修改字段名。
table.oldcolumn:表名及原字段名称
newcolumn:新字段名称。
语句使用者是表的拥有者或拥有DBA权限或有ALTER权限的用户,事务中处理过程中不要使用此语句。
例:rename column user.c6 toc7;
10.CREATEVIEW view-name column-list
CREATEVIEW view-name column-listAS select_statement [WITH CHECK OPTION]
创建视图。
view-name:视图名称。
column-list:字段列表。
select_statement:SELECT语句。
以下语句不使用视图:ALTER TABLE,DROP INDEX,ALTER INDEX,LOCK TABLE,CREATE INDEX, RENAME TABLE;视图将延用基表的字段名,对表达式等虚字段和多表间字段重名必须指明标识其字段名;若对视图中某些字段命名,则所有字段都必须命名;视图中数据类型延用基表中的数据类型,虚字段起诀于表达式;不能使用ORDER BY和UNION子句;对视图中所有的字段要有SELECT权限;事务中处理过程中使用此语句,即使事务回滚,视图也将建立,不能恢复。
例:create view v_user as select * from user where c1 = “B1”;
11.DROPVIEW view-name
删除视图。
view-name:视图名称。
用户可删除自己建立的视图;视图的后代视图也被删除;事务中处理中不要使用此语句。
例:drop view v_user;
12.CREATEINDEX
CREATE[UNIQUE/DISTINCT] [CLUSTER] INDEX index_nameON table_name
([column_name ASC/DESC],…)
创建索引。
index_name:索引名称。
table_name:表名称。
column_name:字段名称。
UNIQUE/DISTINCT:唯一索引。
CLUSTER:使表的物理存放顺序按索引排列。
ASC/DESC:升序或降序,缺省升序。
语句执行时,将表的状态置为EXCLUSIVE;复合索引最多包含8个字段,所有字段长度和不得大于120字节;事务中处理过程中使用此语句,即使事务回滚,索引将建立,不能恢复。
例:create cluster index ix_user on user(c5);
13.ALTERINDEX index-name TO [NOT] CLUSTER
修改索引性质。
index-name:索引名称。
TO[NOT] CLUSTER:去掉或加上CLUSTER属性。
语句执行时,将表的状态置为EXCLUSIVE;事务中处理过程中使用此语句,即使事务回滚,索引性质将改变,不能恢复。
例:alter index ix_user to not cluster;
14.DROPINDEX index-name
删除索引。
index-name:索引名称。
语句使用者是索引的拥有者或拥有DBA权限,事务中处理过程中不要使用此语句,否则事务无法恢复。
例:drop index ix_user;
15.CREATESYNONYM synonym FOR table-name
创建同义名。
synonym:同义名
table-name:表名称
数据库的创建者可以使用同义名;没有赋予同义名权限的用户不能使用同义名;同义名不能和表名相同;事务中处理过程中不要使用此语句。
例:create synonym user_alias for user;
16.DROPSYNONYM synonym
删除同义名。
synonym:同义名
可以删除自己建立的同义名;事务中处理过程中不要使用此语句,否则无法恢复。
例:drop synonym user_alias;
17.UPDATESTATISTICS [FOR TABLE table-name]
更新数据库的统计数字。
table-name:表名称
此语句仅作用于当前数据库;可提高查询效率;只有执行此语句,才改变统计数据。
例:update statistics for table user;
1 GRANT{DBA|RESOURCE|CONNECT} TO {PUBLIC|user-list}
授权命令。
PUBLIC|user-list:全部或指定的用户。
三种权限居且仅居其一,事务处理过程中不要执行GRANT语句。
例:grant resource to pulbic;
GRANT tab-privilege ON table-name TO {PUBLIC|user-list}[WITH GRANT OPTION]
授表级权限。
tab-privilege:表级权限。
table-name:表名称。
PUBLIC|user-list:全部或指定的用户。
[WITH GRANTOPTION]:表示被授权用户有否权限进行二次授权。
用户可以在自己建立表达式或被[WITH GRANT OPTION]准许的表中进行赋权;限定越多的权限优先级越高。
例:grant update(c1,c6) on user to dick with grant option;
附(INFORMIX的权限)
(1)数据库的权限(控制对数据库的访问以及数据库中表的创建和删除)
DBA权限:全部权利,修改系统表,建立和删除表与索引、增加和恢复表数据,以及授予其他用户数据库权限等;
RESOURCE权限:允许对数据库表中的数据进行存取,建立永久性表以及索引。
CONNECT权限:只允许对数据库表中的数据进行存取,建立和删除视图与临时表。
(2)表级权限(对表的建立、修改、检索和更新等权限)
ALTER:更改权限
DELETE:删除权限
INDEX:索引权限
INSERT:插入权限
SELECT [(cols)]:指定字段或所有字段上的查询权限,不指明字段缺省为所有字段。
UPDATE [(cols)]:指定字段或所有字段上的更新权限,不指明字段缺省为所有字段。
ALL[PRIVILEGES]:以上所有表级权限
2 REVOKE{DBA|RESOURCE|CONNECT} FROM {PUBLIC|user-list}
收权命令。
PUBLIC|user-list:全部或指定的用户。
三种权限居且仅居其一,事务处理过程中不要执行GRANT语句。
例:revoke resource from john;
REVOKEtab-privilege ON table-name FROM {PUBLIC|user-list}
收表级权限。
tab-privilege:表级权限。
table-name:表名称。
PUBLIC|user-list:全部或指定的用户。
[WITHGRANT OPTION]:表示被授权用户有否权限进行二次授权。
用户只能取消由其本人赋予其他用户的表级存取权限;不能取消自己的权限,对SELECT和UPDATE作取消时,将取消所有表中字段的SELECT和UPDATE权限。
例;revoke update on user from dick;
3 LOCKTABLE table-name IN {SHARE|EXCLUSIVE}MODE
记录级加锁和表级加锁或文件加锁。
table-name:表名称。
SHARE:允许读表中数据,但不允许作任何修改
EXCLUSIVE:禁止其他任何形式访问表
每次只能对表琐定一次;事务处理过程中,BEGIN WORK后立即执行LOCK TABLE以取代记录级加锁,COMMIT WORK和ROLLBACK WORK语句取消所有对表的加锁;若没有事务处理,锁将保持到用户退出或执行UNLOCK为止。
例:lock table user in exclusive mode;
4 UNLOCKTABLE table-name
取消记录级加锁和表级加锁或文件加锁。
table-name:表名称。
例:unlock user;
5 SETLOCK MODE TO [NOT] WAIT
改变锁定状态。
TO[NOT]:等待解锁,有可能被死锁或不等待并提示错误信息,表示此记录被锁,缺省值。
访问一个EXCLUSIVE状态下的记录,将返回一个错误。
6 STARTDATABSE db_name [WITH LOG IN “pathname”]
启动事务处理。
“pathname”:事务处理日志文件。
执行该语句前,需要先关闭当前数据库。
例;clost database;
startdatabse customer with log in “/usr/john/log/customer.log”;
7 BEGINWORK
开始事务。例:begin work;
8 COMMITWORK
提交(正常结束)事务。例:commit work;
9 ROLLBACKWORK
回滚(非正常结束)事务。例:rollback work;
10 SELECT
SELECT select_list FROM tab_name|view_name
WHERE condition
GROUP BY column_name
HAVING condition
ORDER BY column_list
INTO TEMP table_name
查询语句。
select_list:选择表或*
tab_name:表名称
view_name:视图名称。
condition:查询条件,可使用BETWEEN、IN、LIKE、IS NULL、LIKE、MATCHES、NOT、
AND、OR、=、!=或<>、>、 >=、<=、<、ALL、ANY、SOME
column_name:分组字段名称
condition:群聚条件
column_list:排序字段列表,缺省ASC,可指定DSC;排序时,NULL值小于非零值。
table_name:临时表名称
例:略
附(常用函数)
(1)集合函数:
count(*)、
sum(数据项/表达式)、avg(数据项/表达式)、max(数据项/表达式)、min(数据项/表达式)
count(distinct数据项/表达式)、sum(distinct数据项/表达式)、avg(distinct数据项/表达式)
(2)代数函数和三角函数
HEX(数据项/表达式)、ROUND(数据项/表达式)、TRUNC(数据项/表达式)、
TAN(数据项/表达式)、ABS(数据项/表达式)、MOD(被除数,除数)
(3)统计函数
标准差,stdev()、方差,variance()、范围,rang()
(4)时间函数
DAY(日期/时间表达式):返回数字型
MONTH(日期/时间表达式):返回整数
WEEKDAY(日期/时间表达式):0��6,0星期天,1星期一;返回整数
YEAR(日期/时间表达式)、返回整数
DATE(非日期表达式):返回日期型
EXTEND(日期/时间表达式,[第一个至最后一个]):返回指定的整数
MDY(月,日,年):返回日期型
CURRENT:返回日期型
(5)时间函数
ROUND(),四舍五入。如:ROUND(10.95,position)position进行四舍五入的前一位置
TRUNC(),截取。如:TRUNC(10.95,0)position截取的位置
INFORMIX临时表在下列情况下自动取消:
A.退出数据库访问工具(如DBACCESS)
B.SQL通话结束(DISCONNECT)
C.发出取消表语句
D.退出程序时
11 INSERT
INSERT INTO view_name|table_name [(column_list)] VALUES (value_list)
或 INSERT INTO view_name|table_name[(column_list)] select_statement
插入数据
view_name|table_name:视图名或表名称
column_list:数据项列表。
value_list:值列表
select_statement:查询语句。
例:略
12 DELETEFROM view_name|table_nameWHERE search-conditions
删除语句。
view_name|table_name:视图名或表名称
search-conditions;删除条件
例:略
13 UPDATE
UPDATE view_name|table_nameSET column_1= value_1istWHERE search_conditions
或UPDATE view_name|table_nameSETcolumn_1|* = value_1istWHERE search_conditions
更新数据语句。
view_name|table_name:表名称或视图表名称
value_1ist:字段值
search_conditions:更新数据的条件
例:略
14 CHECKTABLE table-name
检查索引语句。
语句使用者是表的拥有者或拥有DBA权限;不能对systable使用此语句。
例:略
15 REPAIRTABLE table-name
修复索引。
语句使用者是表的拥有者或拥有DBA权限;不能对systable使用此语句。
例:略
16 LOADFROM “file-name” INSERT INTO table_name [(column_name[,…])]
将文本数据栽入表中。
例:load form “aa.txt”insert into user;
17 UNLOADTO “pathname”
将表中数据卸为文本。
例:unload to “aa.txt”select * from user;
18 INFO
系统信息查询。
INFO TABLES:得到当前数据库上表的名字。
INFO columnsFOR table_name:指定表上的字段信息。
INFO INDEXESFOR table_name:指定表上的索引信息。
INFO[ACCESS|PRIVILEGES] FOR table_name:指定表上的存取权限。
INFO STATUSFOR table_name:指定表的状态信息。
例: info tables;
DB2提供了关连式资料库的查询语言 SQL(Structured Query Language), 是一种非常口语化、既易学又易懂的语法。此一语言几乎是每个资料库系统都必须提供的,用以表示关连式的操作,包含了资料的定义(DDL)以及资料的处理(DML)。SQL原来拼成 SEQUEL,这语言的原型以“系统 R“的名 字在 IBM圣荷西实验室完成,经过 IBM内部及其他的许多使用性及效率测试,其结果相当令人满意,并决定在系统R的技术基础发展出来 IBM的产品。而且美国国家标准学会(ANSI)及国际标准化组织(ISO 在 1987遵循一个几乎 是以IBM SQL为基础的标准关连式资料语言定义。
资料定义 DDL(Data Definition Language)
资料定语言是指对资料的格式和形态下定义的语言,他是每个资料库要建立时候 时首先要面对的,举凡资料分哪些表格关系、表格内的有什麽栏位主键、表格和表格之间互相参考的关系等等,都是在开始的时候所必须规划好的。
建表格:
语法:
CREATE TABLE table_name(
column1 DATATYPE [NOT NULL] [NOT NULL PRIMARY KEY],
column2 DATATYPE [NOT NULL],
...)
说明
DATATYPE --是资料的格式,详见表。
NUT NULL --可不可以允许资料有空的(尚未有资料填入)。
PRIMARY KEY --是本表的主键。
更改表格
ALTER TABLE table_name
ADD COLUMN column_name DATATYPE
说明 增加一个栏位(没有删除某个栏位的语法)
ALTER TABLE table_name
ADD PRIMARY KEY (column_name)
说明 更改表得的定义把某个栏位设为主键
ALTER TABLE table_name
DROP PRIMARY KEY (column_name)
说明 把主键的定义删除。
建立索引
CREATE INDEX index_name ON table_name (column_name)
说明 对某个表格的栏位建立索引以增加查询时的速度
删除
DROP table_name
DROP index_name
资料形态 DATATYPE
SQL 的资料形态 DATATYPEs
smallint
16位元的整数
interger
32位元的整数
decimal(p,s)
p精确值和 s大小的十进位整数,精确值 p是指全部有几个数(digits)
大小值 s是指小数点後有几位数。
如果没有特别指定,则系 统会设为 p=5; s=0。
float
32位元的实数
double
64位元的实数
char(n)
n长度的字串,n不能超过 254
varchar(n)
长度不固定且其最大长度为 n的字串,n不能超过 4000
graphic(n)
和 char(n)一样,不过其单位是两个字元double-bytes, n不能超过 127。这个形态是为了支援两个字元长度的字体,例如中文字
vargraphic(n)
可变长度且其最大长度为 n的双字元字串,n不能超过 2000
date
包含了 年份、月份、日期.
time
包含了 小时、分钟、秒.
timestamp
包含了 年、月、日、时、分、秒、千分之一秒.
资料操作 DML (Data Manipulation Language)
资料定义好之後接下来的就是资料的操作。资料的操作不外乎增加资料(insert)、 查询资料(query)、更改资料(update)、删除资料(delete)四种模式,以下分别介绍他们的语法:
增加资料:
INSERT INTO table_name (column1,column2,...) VALUES ( value1,value2, ...)
说明:
1.若没有指定column系统则会按表格内的栏位顺序填入资料
2.栏位的资料形态和所填入的资料必须吻合
3.table_name也可以是景观 view_name
INSERT INTO table_name (column1,column2,...) SELECT columnx,columny,...FROM another_table
说明:也可以经过一个子查询(subquery)把别的表格的资料填入
查询资料:
基本查询
SELECT column1,columns2,... FROM table_name
说明:把table_name 的特定栏位资料全部列出来
SELECT *
FROM table_name
WHERE column1 = xxx
[AND column2 > yyy] [OR column3 <> zzz]
说明:
1.'*'表示全部的栏位都列出来
2.WHERE之後是接条件式,把符合条件的资料列出来
SELECT column1,column2
FROM table_name
ORDER BY column2 [DESC]
说明:
ORDER BY是指定以某个栏位做排序,[DESC]是指从大到小排列,若
没有指明,则是从小到大排列
组合查询
组合查询是指所查询得资料来源并不只有单一的表格,而是联合一个以上的表格才能够得到结果的。
SELECT *
FROM table1,table2
WHERE table1.colum1=table2.column1
说明:
1.查询两个表格中其中 column1值相同的资料
2.当然两个表格相互比较的栏位,其资料形态必须相同
3.一个复杂的查询其动用到的表格可能会很多个
整合性的查询:
SELECT COUNT (*)
FROM table_name
WHERE column_name = xxx
说明:
查询符合条件的资料共有几笔
SELECT SUM(column1)
FROM table_name
说明:
1.计算出总和,所选的栏位必须是可数的数字形态
2.除此以外还有 AVG()是计算平均、MAX()、MIN()
计算最大最小值的整合性查询
SELECT column1,AVG(column2)
FROM table_name
GROUP BY column1
HAVING AVG(column2) > xxx
说明:
1.GROUP BY:以column1为一组计算 column2的平均值
必须和 AVG、SUM等整合性查询的关键字一起使用
2.HAVING :必须和 GROUP BY一起使用作为整合性的限制
复合性的查询
SELECT *
FROM table_name1
WHERE EXISTS (
SELECT *
FROM table_name2
WHERE conditions )
说明:
1.WHERE的 conditions可以是另外一个的 query
2.EXISTS在此是指存在与否
SELECT *
FROM table_name1
WHERE column1 IN (
SELECT column1
FROM table_name2
WHERE conditions )
说明
1. IN後面接的是一个集合,表示column1存在集合里面
2. SELECT出来的资料形态必须符合 column1
其他查询
SELECT *
FROM table_name1
WHERE column1 LIKE 'x%'
说明:
1.LIKE必须和後面的'x%'相呼应表示以 x为开头的字串
SELECT *
FROM table_name1
WHERE column1 IN ('xxx','yyy',..)
说明
1. IN後面接的是一个集合,表示column1存在集合里面
SELECT *
FROM table_name1
WHERE column1 BETWEEN xx AND yy
说明
1. BETWEEN表示 column1的值介於 xx和 yy之间
更改资料:
UPDATE table_name
SET column1='xxx'
WHERE conditoins
说明:
1.更改某个栏位设定其值为'xxx'
2.conditions是所要符合的条件、若没有 WHERE则
整个 table的那个栏位都会全部被更改
删除资料:
DELETE FROM table_name
WHERE conditions
说明:删除符合条件的资料
1. NULL值的使用
RDSQL中字段缺省值为空;并且对数值型的0和空值,以及字符型的空白和空值区别对待。
数值表达式中某个变为空,则整个表达式值为空;
聚合函数中,对空值忽略不计,若全部为空值,除COUNT(*)返回0外,其余返回空值。
布尔表达式中,结果可能为“未知”(见下表)。如TRUE AND NULL结果为 “未知”,
对“未知”结果在RDSQL中看作不符合查询条件。
and | T | F | ? |
| or | T | F | ? |
| not |
|
T | T | F | ? |
| T | T | T | T |
| T | F |
F | F | F | ? |
| F | T | F | ? |
| F | T |
? | ? | F | ? |
| ? | T | ? | ? |
| ? | ? |
结合上表,分析下列子句 ,其中n1=20;n2为空;n3=30。结果如右。
where n1*n2< 1000 and n3 = 30;结果:不符合查询条件
where n1*n2< 1000 or n3 = 30;结果:符合查询条件
ORDER BY子句中的空值,每一个空值为一组。
INSERT或UPDATE时,可使用关键字NULL/null表示空值。
字段是否可以为空,由CREATETABLE语句中是否有NOT NULL指定或由ALTER修改。
Q:select count(*) from t1和selectcount(c1) from t1是否一样?
2. 字符查找,主要使用LIKE和MATCHES。
LIKE | MATCHES | 意义 |
% | * | 匹配0或多个字符 |
- | ? | 匹配一个字符 |
\ | \ | 转义字符 |
无 | [] | 选择匹配 |
例:matches ‘*Sp’;匹配以任何字符开始,以Sp结束的字段值
matches ‘?l*’; 匹配第一个字符任意,第二个字符为l,其余字符任意的字段值
matches ‘[A-N]*’; 匹配以A到N的字符开始,其余字符任意的字段值
matches ‘*[sS]*’; 匹配含有s或S的字段值,扩展以下可用于case insensitive查询
like ‘%\%%’; 匹配含有%的字段值
3. 用SQL语句求表一中的关于name有多少不同的num,结果如表二。
4. 表一: 表二:
5. id name num name count1
6. 1 AA 1 CC 2
7. 2 AA 2 BB 2
8. 3 AA 3 AA 3
9. 4 AA 1
10. 5 AA 2
11. 6 BB 4
12. 7 BB 5
13. 8 BB 4
14. 9 BB 5
15. 10 CC 6
16. 11 CC 6
17. 12 CC 7
SQL语句如下:
create table t1
(
id smallint,
name char(10),
num smallint
);
insert into t1values(1,'AA',1);
insert into t1values(2,'AA',2);
insert into t1values(3,'AA',3);
insert into t1values(4,'AA',1);
insert into t1values(5,'AA',2);
insert into t1values(6,'BB',4);
insert into t1values(7,'BB',5);
insert into t1values(8,'BB',4);
insert into t1values(9,'BB',5);
insert into t1values(10,'CC',6);
insert into t1values(11,'CC',6);
insert into t1values(12,'CC',7);
A:select name ,count(distinct num) from t1 group by name;
4)使用旋转矩阵,将表一中关于id在不同月份的费用,由纵向变为横向。
其中表一对一个id某个月份的记录数可能>1。表一:
id d1 fee费用(分)
1 2000-01-24100
1 2000-04-24100
2 2000-02-24200
2 2000-06-24200
3 2000-04-24400
4 2000-04-24400
5 2000-05-24500
6 2000-06-24600
7 2000-09-24900
8 2000-11-241100
表二:
id 1月份费用 2月份费用 ……… …12月份费用
1 100 0 0 100 0 0 0 0 0 0 0 0
2 0 200 0 0 0 200 0 0 0 0 0 0
3 0 0 0 400 0 0 0 0 0 0 0 0
4 0 0 0 400 0 0 0 0 0 0 0 0
5 0 0 0 0 500 0 0 0 0 0 0 0
6 0 0 0 0 0 600 0 0 0 0 0 0
7 0 0 0 0 0 0 0 0 900 0 0 0
8 0 0 0 0 0 0 0 0 0 0 1100 0
SQL语句:
create table t3
(
id smallint,
d1 datetimeyear to day,
fee int
);
insert into t3values(1,"2000-01-24", 100);
insert into t3values(1,"2000-04-24", 100);
insert into t3values(2,"2000-02-24", 200);
insert into t3values(2,"2000-06-24", 200);
insert into t3values(3,"2000-04-24", 400);
insert into t3values(4,"2000-04-24", 400);
insert into t3values(5,"2000-05-24", 500);
insert into t3values(6,"2000-06-24", 600);
insert into t3values(7,"2000-09-24", 900);
insert into t3values(8,"2000-11-24", 1100);
create table t4–旋转矩阵
(
m_code smallint,
y1 smallint,
y2 smallint,
y3 smallint,
y4 smallint,
y5 smallint,
y6 smallint,
y7 smallint,
y8 smallint,
y9 smallint,
y10 smallint,
y11 smallint,
y12 smallint
);
insert into t4values(1, 1,0,0,0,0,0,0,0,0,0,0,0);
insert into t4values(2, 0,1,0,0,0,0,0,0,0,0,0,0);
insert into t4values(3, 0,0,1,0,0,0,0,0,0,0,0,0);
insert into t4values(4, 0,0,0,1,0,0,0,0,0,0,0,0);
insert into t4values(5, 0,0,0,0,1,0,0,0,0,0,0,0);
insert into t4values(6, 0,0,0,0,0,1,0,0,0,0,0,0);
insert into t4values(7, 0,0,0,0,0,0,1,0,0,0,0,0);
insert into t4values(8, 0,0,0,0,0,0,0,1,0,0,0,0);
insert into t4values(9, 0,0,0,0,0,0,0,0,1,0,0,0);
insert into t4values(10,0,0,0,0,0,0,0,0,0,1,0,0);
insert into t4values(11,0,0,0,0,0,0,0,0,0,0,1,0);
insert into t4values(12,0,0,0,0,0,0,0,0,0,0,0,1);
--方法一
selectid,month(d1) month,sum(fee) fei from t3 group by 1,2 into temp aa;
select id,
sum(y1*fei)y1,sum(y2*fei) y2,sum(y3*fei) y3,sum(y4*fei) y4,
sum(y5*fei)y5,sum(y6*fei) y6,sum(y7*fei) y7,sum(y8*fei) y8,
sum(y9*fei)y9,sum(y10*fei) y10,sum(y11*fei) y11,sum(y12*fei) y12
from aa, t4where aa.month = t4.m_code
group by idorder by id
--方法二
select id,
sum(y1*fee)y1,sum(y2*fee) y2,sum(y3*fee) y3,sum(y4*fee) y4,
sum(y5*fee)y5,sum(y6*fee) y6,sum(y7*fee) y7,sum(y8*fee) y8,
sum(y9*fee)y9,sum(y10*fee) y10,sum(y11*fee) y11,sum(y12*fee) y12
from t3, t4where month(d1) = t4.m_code
group by idorder by id
方法一和方法二的结果一样,但有所区别:
方法一中是先对id某个月的钱进行累加,然后进行旋转;
方法二中在表一对一个id某个月份的记录数可能>1的情况时,先对每条记录进行旋转,然后在
累加求和。
19. 用SPL写自己的UPPER函数?
INFORMIX中没有UPPER函数,用存储过程实现如下
例:方法一:
=================== SPL: Upper()===================
--
-- Procedure: GetCharAt()
-- Get a character from a string at aspecified position.
--
DROP PROCEDURE GetCharAt;
CREATE PROCEDURE GetCharAt(strVARCHAR(255), pos INTEGER)
RETURNING VARCHAR(1);
DEFINE i INTEGER;
IF pos < 1 THEN
FOR i = 2 TO pos
LET str = str[2,255];
END FOR;
END IF
RETURN str[1,1];
END PROCEDURE;
--
-- Procedure: Upper()
-- Convert a string to uppercase.
--
DROP PROCEDURE Upper;
CREATE PROCEDURE Upper(strVARCHAR(255))
RETURNING VARCHAR(255, 0);
DEFINE i INTEGER;
DEFINE len INTEGER;
DEFINE retstr VARCHAR(255);
IF str IS NULL THEN
RETURN NULL;
ELSE
LET len = LENGTH(str);
LET retstr = '';
FOR i = 1 TO len
LET retstr =retstr||ToUpper(GetCharAt(str, i));
END FOR;
LET retstr = retstr[2,255]; -- BUG:req'd to strip off leading blank
RETURN retstr;
END IF;
END PROCEDURE;
--
-- Procedure: ToUpper()
-- Convert a single character to uppercase.
--
DROP PROCEDURE ToUpper;
CREATE PROCEDURE ToUpper(fromcharVARCHAR(1,1))
RETURNING VARCHAR(1,1);
IF fromchar = 'a' THEN
RETURN 'A';
ELIF fromchar = 'b' THEN
RETURN 'B';
ELIF fromchar = 'c' THEN
RETURN 'C';
ELIF fromchar = 'd' THEN
RETURN 'D';
ELIF fromchar = 'e' THEN
RETURN 'E';
ELIF fromchar = 'f' THEN
RETURN 'F';
ELIF fromchar = 'g' THEN
RETURN 'G';
ELIF fromchar = 'h' THEN
RETURN 'H';
ELIF fromchar = 'i' THEN
RETURN 'I';
ELIF fromchar = 'j' THEN
RETURN 'J';
ELIF fromchar = 'k' THEN
RETURN 'K';
ELIF fromchar = 'l' THEN
RETURN 'L';
ELIF fromchar = 'm' THEN
RETURN 'M';
ELIF fromchar = 'n' THEN
RETURN 'N';
ELIF fromchar = 'o' THEN
RETURN 'O';
ELIF fromchar = 'p' THEN
RETURN 'P';
ELIF fromchar = 'q' THEN
RETURN 'Q';
ELIF fromchar = 'r' THEN
RETURN 'R';
ELIF fromchar = 's' THEN
RETURN 'S';
ELIF fromchar = 't' THEN
RETURN 'T';
ELIF fromchar = 'u' THEN
RETURN 'U';
ELIF fromchar = 'v' THEN
RETURN 'V';
ELIF fromchar = 'w' THEN
RETURN 'W';
ELIF fromchar = 'x' THEN
RETURN 'X';
ELIF fromchar = 'y' THEN
RETURN 'Y';
ELIF fromchar = 'z' THEN
RETURN 'Z';
END IF;
RETURN fromchar;
END PROCEDURE;
方法二:
=================== SPL: Upper()===================
--
-- Procedure: Upper()
-- Convert a string to uppercase.
--
DROP PROCEDURE upper;
CREATE PROCEDURE upper (strVARCHAR(255)) RETURNING VARCHAR(255);
DEFINE i INTEGER;
DEFINE l INTEGER;
DEFINE retstr VARCHAR(255);
IF str IS NULL THEN
RETURN NULL;
ELSE
LET l = LENGTH(str);
LET retstr = '';
FOR i = 1 TO l
IF str[1,1] BETWEEN "a" AND"z" THEN
IF str[1,1] = 'a' THEN
LET retstr = retstr || 'A';
ELIF str[1,1] = 'b' THEN
LET retstr = retstr || 'B';
ELIF str[1,1] = 'c' THEN
LET retstr = retstr || 'C';
ELIF str[1,1] = 'd' THEN
LET retstr = retstr || 'D';
ELIF str[1,1] = 'e' THEN
LET retstr = retstr || 'E';
ELIF str[1,1] = 'f' THEN
LET retstr = retstr || 'F';
ELIF str[1,1] = 'g' THEN
LET retstr = retstr || 'G';
ELIF str[1,1] = 'h' THEN
LET retstr = retstr || 'H';
ELIF str[1,1] = 'i' THEN
LET retstr = retstr || 'I';
ELIF str[1,1] = 'j' THEN
LET retstr = retstr || 'J';
ELIF str[1,1] = 'k' THEN
LET retstr = retstr || 'K';
ELIF str[1,1] = 'l' THEN
LET retstr = retstr || 'L';
ELIF str[1,1] = 'm' THEN
LET retstr = retstr || 'M';
ELIF str[1,1] = 'n' THEN
LET retstr = retstr || 'N';
ELIF str[1,1] = 'o' THEN
LET retstr = retstr || 'O';
ELIF str[1,1] = 'p' THEN
LET retstr = retstr || 'P';
ELIF str[1,1] = 'q' THEN
LET retstr = retstr || 'Q';
ELIF str[1,1] = 'r' THEN
LET retstr = retstr || 'R';
ELIF str[1,1] = 's' THEN
LET retstr = retstr || 'S';
ELIF str[1,1] = 't' THEN
LET retstr = retstr || 'T';
ELIF str[1,1] = 'u' THEN
LET retstr = retstr || 'U';
ELIF str[1,1] = 'v' THEN
LET retstr = retstr || 'V';
ELIF str[1,1] = 'w' THEN
LET retstr = retstr || 'W';
ELIF str[1,1] = 'x' THEN
LET retstr = retstr || 'X';
ELIF str[1,1] = 'y' THEN
LET retstr = retstr || 'Y';
ELSE
LET retstr = retstr || 'Z';
END IF;
ELSE
LET retstr = retstr || str[1,1];
END IF;
LET str = str[2,255];
END FOR;
LET retstr = retstr[2,255]; -- BUG:req'd to strip off leading blank
RETURN retstr;
END IF;
END PROCEDURE;
说明:
某些INFORMIX版本中,语句
DEFINEretstr VARCHAR(255);
LET retstr ='';
LET retstr =retstr || 'A';
将返回' A'.两个字符,如果确定你的系统没有这个问题
将以上SPL中的语句 LET retstr = retstr[2,255];
-- BUG:req'd to strip off leading blank去掉
20. 将一个表中重复键值的记录删除?
假定"keycol"是唯一的(即关于某个键值若有>=2条以上的记录时,只保留一条),
且表未分区,没有别的用户正在删除这个表的记录,使用如下语句
delete fromsometable as a
where rowid<> (select min(rowid) from sometable where keycol = a.keycol)
如以上条件不满足或没有ROWID,可以使用如下语句
BEGIN WORK;
SELECTDISTINCT * FROM Table INTO TEMP Temp1;
DELETE FROMTable WHERE 1 = 1;
INSERT INTOTable SELECT * FROM Temp1;
COMMIT WORK;
说明:适用于记录数不多,且临时空间足够的情况.
21. 怎样在语句错误时停止使用SQL脚本?
例:
$ dbaccess<database> <sql-file>
<sql-file>:
BEGIN WORK;
INSERT INTOhistory
SELECT *
FROM current
WHERE momth= 11;
DELETE FROMcurrent
WHERE month= 11;
COMMIT WORK;
插入语句错误(字段momth没有找到),脚本还是会继续执行,这回带来严重的错误.
可以通过设置环境变量DBACCNOIGN=1,使错误发生后,立即停止继续运行.
22. 设置运算结果的返回位数?
例:未使用DBDLTMASK环境变量时,
CREATE TEMPTABLE t
(
col_aDECIMAL(8,4) NOT NULL,
col_bDECIMAL(8,4) NOT NULL,
col_cDECIMAL(8,4) NOT NULL
);
INSERT INTOt VALUES(1.2345, 3.4567, 5.6789);
SELECT(col_a + col_b) / col_c AS value FROM t;
设置环境变量DBDLTMASK前 value 0.82607547236261
设置环境变量DBDLTMASK=6后 value 0.826075
连接查询
通过连接运算符可以实现多个表查询。连接是关系数据库模型的主要特点,也是它区别于其它类型数据库管理系统的一个标志。
在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在一个表中。当检索数据时,通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带来很大的灵活性,他们可以在任何时候增加新的数据类型。为不同实体创建新的表,尔后通过连接进行查询。
连接可以在SELECT语句的FROM子句或WHERE子句中建立,似是而非在FROM子句中指出连接时有助于将连接操作与WHERE子句中的搜索条件区分开来。所以,在Transact-SQL中推荐使用这种方法。
SQL-92标准所定义的FROM子句的连接语法格式为:
FROMjoin_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数据类型列进行直接连接,但可以对这三种列进行间接连接。例如:
SELECTp1.pub_id,p2.pub_id,p1.pr_info
FROMpub_info AS p1 INNER JOIN pub_info AS p2
ONDATALENGTH(p1.pr_info)=DATALENGTH(p2.pr_info)
(一)内连接
内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的列值。内连接分三种:
1、等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询结果中列出被连接表中的所有列,包括其中的重复列。
2、不等连接: 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。这些运算符包括>、>=、<=、<、!>、!<和<>。
3、自然连接:在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选择列表指出查询结果集合中所包括的列,并删除连接表中的重复列。
例,下面使用等值连接列出authors和publishers表中位于同一城市的作者和出版社:
SELECT *
FROM authorsAS a INNER JOIN publishers AS p
ONa.city=p.city
又如使用自然连接,在选择列表中删除authors和publishers表中重复列(city和state):
SELECTa.*,p.pub_id,p.pub_name,p.country
FROM authorsAS a INNER JOIN publishers AS p
ONa.city=p.city
(二)外连接
内连接时,返回查询结果集合中的仅是符合查询条件( WHERE搜索条件或 HAVING条件)和连接条件的行。而采用外连接时,它返回到查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外连接时)、右表(右外连接时)或两个边接表(全外连接)中的所有数据行。
如下面使用左外连接将论坛内容和作者信息连接起来:
SELECTa.*,b.* FROM luntan LEFT JOIN usertable as b
ON a.username=b.username
下面使用全外连接将city表中的所有作者以及user表中的所有作者,以及他们所在的城市:
SELECTa.*,b.*
FROM city asa FULL OUTER JOIN user as b
ONa.username=b.username
(三)交叉连接
交叉连接不带WHERE子句,它返回被连接的两个表所有数据行的笛卡尔积,返回到结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
例,titles表中有6类图书,而publishers表中有8家出版社,则下列交叉连接检索到的记录数将等
于6*8=48行。
SELECTtype,pub_name
FROM titlesCROSS JOIN publishers
ORDER BYtype
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。