主流数据库管理系统:
目前有许多DBMS产品,如DB2、Oracle、Microsoft SQL Server、Sybase SQLServer、
Informix、MySQL 等。
DB2 第一种使用使用SQL的数据库产品。应用程序可以通过使用微软的ODBC接口、Java的JDBC接口或者CORBA接口代理来访问DB2数据库。以DB2 的功能和性能都是非常优秀的,不过对开发人员的要求也比其他数据库系统更高,使用不当很容易造成宕机、死锁等问题;DB2 在SQL的扩展方面比较保守,很多其他数据库系统支持的SQL 扩展特性在DB2 上都无法使用;同时DB2 对数据的类型要求也非常严格,在数据类型不匹配的时候会报错而不是进行类型转换,而且如果发生精度溢出、数据超长等问题的时候也会直接报错,这虽然保证了数据的正确性,但是也使得基于DB2的开发更加麻烦。因此,很多开发人员称DB2 为“最难用的数据库系统”。
Oracle 从DB2等产品中吸取到了很多优点,同时又避免了IBM的官僚体制与过度学术化,大胆的引进了许多新的理论与特性,所以Oracle 无论是功能、性能还是可用性都是非常好的。
Microsoft SQL Server 可用性做的非常好,提供了很多了外围工具来帮助用户对数据库进行管理,用户甚至无需直接执行任何SQL 语句就可以完成数据库的创建、数据表的创建、数据的备份/恢复等工作;Microsoft SQL Server 的开发者社区也是非常庞大的,因此有众多可以参考的学习资料,学习成本非常低,这是其他数据库产品做不具有的优势;同时从Microsoft SQL Server 2005开始开发人员可以使用任何支持.Net的语言来编写存储过程,这进一步降低了Microsoft SQL Server 的使用门槛。
MySQL 资源占用非常小,更加易于安装、使用和管理,但目前还很难用于支撑大业务量的系统。
DBMS 采用了多数据库的方式来保存不同类别的数据,一个DBMS 可以管理多个数据库,我们将人力资源数
据保存在HR数据库中,而将核心业务数据保存在BIZ数据库中,我们将这些不同数据库叫做Catalog(在有的DBMS中也称为Database,即数据库。)
优点:便于对各个catalog进行个性化管理;避免了命名冲突;安全性更高。
将不同类型的资料放到不同的“区域”中,我们将这种区域叫做“表”(Table)
通常,以这种不标准的格式保存造成数据十分混乱,想要从数据库中取出合适的数据仍
然非常麻烦。为了解决这个问题,我们规定下面这种标准的格式:
这里的“姓名”、“部门”和“入职时间”就被称为员工表的列(Column),有时候也叫
做字段(Field),每个列描述了数据的一个特性。
规定各个列的数据类型(DataType),数据类型规定了一个列中能填写什么类型的数据,减少了不规范数据出现的几率。
优点:提高效率;对数据进行操作所需要的正确处理方式。
这里每一行数据就代表一个员工的资料,这样的一行数据就叫做一条记录。表是由行和
列组成的一张二维表,这就是关系数据库中最基本的数据模型。
能唯一标识一行记录的字段就是此表的主键。
复合主键:任何一个字段都不能唯一标识一行数据,只有构成“复合主键”的所有字段组合起来才能
唯一标识这一行数据。
索引 如果查找一个值,它在索引表中某个中间点以前不会出现,那么也有找到其第一个匹配索引项的定位算法,而不用进行表的顺序扫描(如二分查找法)。这样,可以快速定位到第一个匹配的值,以节省大量搜索时间。 缺点:索引占据一定磁盘空间,就像有按笔画查找的目录的书会比没有这种目录的书页数要多一些。索引减慢了数据插入和删除的速度。因为每次插入和删除的时候都需要更新索引,一个表拥有的索引越多,则写操作的平均性能下降就越大。
这种将两张表通过字段关联起来的方式就被称为“表关联”,关联到其他表主键的字段被称为“外键”。表关联也是关系数据库的核心理念,它使得数据库中的数据不再互相孤立,通过表关联我们可以表达非常复杂的数据关系。
SQL 具有下面4 个主要的功能:创建数据库并定义表的结构;查询需要的数据;更新或者删除指定的数据;控制数据库的安全。
三种方式执行SQL:
1,在工具中执行。各个DBMS几乎都提供了工具用于执行SQL语句,比如Microsoft SQL Server 的Management Studio、DB2 的命令中心、Oracle的SqlPlus 或者MySQL的Query Browser。在这些工具中我们只要输入要执行的SQL然后点击【执行】按钮就可以得到执行结果。
2,以编译的方式嵌入到语言中。在这种方式中我们可以把SQL 直接写到代码中,在编译的时候由编译器来决定和数据库的交互方式。比如PowerBuild、C等就采用这种方式。
3,以字符串的形式嵌入到语言中。在这种方式中SQL 语句只是以字符串的形式写到代码中,然后由代码将其提交到DBMS,并且分析返回的结果。目前这是大部分支持数据库操作的语言采用的方式,比如C#、Java, Python、Delphi 和VB等。
数据库系统管理员(Database Administrator,DBA)
1, 安装和配置数据库,创建数据库以及帐户;
2, 监视数据库系统,保证数据库不宕机;
3, 收集系统统计和性能信息以便进行调整;
4, 发现性能糟糕的SQL,并给开发人员提出调优建议;
5, 管理数据库安全性;
6, 备份数据库,当发生故障时要及时恢复;
7, 升级DBMS并且在必要时为系统安装补丁;
8, 执行存储和物理设计,均衡设计问题以完成性能优化;
DBA大部分时间是在监视系统、备份/恢复系统、优化系统,而开发人员则无需精通这些技能;开发人员大部分时间是在用SQL实现业务逻辑。二者知识的重合点就是SQL,一个开发人员如果不熟悉SQL的话就无法很好的实现业务逻辑,而一个DBA如果不熟悉SQL的话就无法完成数据库的调优工作。
数据库系统中的数据类型大致可以分为五类:整数、数值、字符相关、日期时间以及二进制。
bit 0,1或空值 只能存储两种可能值
int 可以表示-2147483648 到2147483647 之间的整数
smallint 表示-32768到32767之间的整数
bigint 表示-2的63次方 到2的63次方-1之间的整数
tingint 存储0-255之间的整数
decimal 小数点左右能存储的数字位数为10的38次方
float 近似数值型 -1.79E+308 到 1.79E+308之间的任意数
real 像浮点数一样 -3.40E+38 到 3.40E+38 之间的浮点数
char 固定长度字符串,长度为m
verchar 可变长度字符串,最大长度为m
nverchar 可变长度国际化字符串,最大长度为m
text 可变长度字符串,最大长度231 - 1 字节
ntext 可变长度国际化大字符串,其最大长度为2^30 - 1(1,073,741,823) 个字符。
datetime 从 1753 年1 月1 日到9999 年12 月31 日的日期时间数据,精确到百分之三秒
smalldatetime 从 1900 年1 月1 日到2079 年6 月6 日的日期和时间数据,精确到分钟
timestamp 时间戳
image 并不意味着只能保存图片二进制数据,实际上它可以保存任何二进制数据。
创建数据表
SQL语句CREATE TABLE 用于创建数据表,其基本语法如下:
CREATE TABLE 表名
(
字段名1 字段类型,
字段名2 字段类型,
字段名3 字段类型,
………………
约束定义 1,
约束定义 2,
------------
)
这个表名不能与数据库中已有的表名重复。括号中是一条或者多条表定义,表定义包括字段定义和约束定义两种,一张表中至少要有一个字段定义,而约束定义则是可选的。约束定义包括主键定义、外键定义以及唯一约束
定义等。
定义非空约束
非空字段的定义在类型定义后增加了“NOT NULL”,其他定义方式与普通字段相同。
例如:
CREATE TABLE T_Student
(FNumber VARCHAR(20) NOT NULL ,
FName VARCHAR(20) NOT NULL ,
FAge INT NOT NULL ,
FFavorite VARCHAR(20),
FPhoneNumber VARCHAR(20))
定义默认值
我们在定义字段的时候为字段设置一个默认值,当向表中插入数据的时候如果没有为这个字段赋值则这个字段的值会取值为这个默认值。
与普通字段定义不同的地方是,非空字段的定义在类型定义后增加了“DEFAULT 默认值表达式”,其他定义方式与普通字段相同。
例如:
CREATE TABLE T_Teacher
(FNumber VARCHAR(20),
FName VARCHAR(20),
FAge INT,
FISMaster VARCHAR(5) DEFAULT 'NO')
设置教师信息表中的是否班主任字段FISMaster 的默认值为“NO”
定义主键
通过主键能够唯一定位一条数据记录,而且在进行外键关联的时候也需要被关联的数据表具有主键,所以为数据表定义主键是非常好的习惯。
主键定义是在所有字段后的“约束定义段”中定义的,格式为PRIMARY KEY(主键字段名),在有的数据库系统中主键字段名两侧的括号是可以省略的,也就是可以写成PRIMARY KEY FNumber,不过为了能够更好的跨数据库,建议不要采用这种不通用的写法。
例如:
CREATE TABLE T_Bus
(FNumber VARCHAR(20),
FDriverName VARCHAR(20),
FUsedYears INT,
PRIMARY KEY (FNumber))
我们可以让FCompanyName、FInternalNumber 两个字段联合起来做为主键,这样的主键被称为联合主键
(或者称为复合主键)。定义时,只要在PRIMARY KEY后的括号中列出做为联合主键的各个字段。
CREATE TABLE T_PersonalMember
(FCompanyName VARCHAR(20),
FInternalNumber VARCHAR(20),
FName VARCHAR(20),
PRIMARY KEY (FCompanyName,FInternalNumber))
注,我们应该只在兼容遗留系统等特殊场合才使用联合主键,而在其他场合则应该使用唯一主键。
定义外键
外键是非常重要的概念,也是体现关系数据库中“关系”二字的体现,通过使用外键,我们才能把互相独立的表关联起来。
外键是定义在源表中的,定义位置同样为所有字段定义的后面,使用FOREIGN KEY关键字来定义外键字段,并且使用REFERENCES关键字来定义目标表名以及目标表中被关联的字段,格式为:FOREIGN KEY 外键字段名称REFERENCES 目标表名(被关联的字段名称)
例如:
创建一张部门信息表,表中记录了部门主键FId、部门名称FName、部门级别FLevel等字段
CREATE TABLE T_Department
(FId VARCHAR(20),
FName VARCHAR(20),
FLevel INT,
PRIMARY KEY (FId))
接着创建员工信息表,表中记录工号、姓名以及所属部门等信息,为了能够建立同部门信息表之间的关联关系,我们在员工信息表中保存部门信息表中的主键,保存这个主键的字段就被称为员工信息表中指向部门信息表的外键。
CREATE TABLE T_Employee
(FNumber VARCHAR(20),
FName VARCHAR(20),
FDepartmentId VARCHAR(20),
FOREIGN KEY (FDepartmentId) REFERENCES T_Department(FId))
修改已有数据表
添加字段的语法:
ALTER TABLE 待修改的表名 ADD 字段名 字段类型
例如:ALTER TABLE T_PERSON ADD FFavorite VARCHAR(20) 为T_Person表增加个人爱好字段
删除字段的语法:
ALTER TABLE 待修改的表名 DROP 待删除的字段名
例如:ALTER TABLET_Person DROP FAge 删除T_Person 表中年龄字段
删除数据表
语法格式: DROP TABLE 要删除的表名
例如:DROP TABLE T_Person 将T_Person表删除
注:如果在表之间创建了外键关联关系,那么在删除被引用数据表的时候会删除失败,因为这样会导致关联关系被破坏,所以必须首先删除引用表,然后才能删除被引用表。
比如A表创建了指向B表的外键关联关系,那么必须首先删除A表后才能删除B表。
创建两个相关联的表格
CREATE TABLE T_Person 创建表
(FName VARCHAR(20),
FAge INT DEFAULT 20 , 设默认值
FRemark VARCHAR(20),
PRIMARY KEY (FName)); 设主键
T_Person为记录人员信息的数据表,
其中主键字段FName 为人员姓名,FAge 为年龄,而FRemark则为备注信息
CREATE TABLE T_Debt
(FNumber VARCHAR(20),
FAmount NUMERIC(10,2) NOT NULL, 设为非空
FPerson VARCHAR(20),
PRIMARY KEY (FNumber),
FOREIGN KEY (FPerson) REFERENCES T_Person(FName)) ; 设外键
T_Debt 记录了债务信息,其中主键字段FNumber 为债务编号,FAmount为欠债金额,FPerson字段为欠
债人姓名,FPerson字段与T_Person中的FName 字段建立了外键关联关系。
查询表格所有内容:SELECT * FROM T_Person
数据表中插入数据:INSERT INTO 语句
例如:INSERT INTO T_Person(FName,FAge,FRemark) VALUES('Tom',18,'USA')
INSERT语句中列的顺序可以是任意的,但不能打乱它们之间的一一对应关系,而且要保证两边的条数是一致的,
例如:INSERT INTO T_Person(FAge,FName,FRemark) VALUES(21,'Kimisushi','Korea')
也并不需要我们指定表中的所有列。
例如:INSERT INTO T_Person(FAge,FName) VALUES(22,'LXF')
注:字符串类型的字段值需要用单引号将值包围起来,而整数类型的字段的值则不需要用单引号包围起来。
INSERT语句还有另一种用法,可以不用指定要插入的表列,这种情况下将按照定义表。(通常不建议使用)
例如:INSERT INTO T_Person VALUES('luren1',23,'China')
非空值影响:若是设置了非空值,那么在插入数据时不能为空,必须要有值。
例如:INSERT INTO T_Debt (FNumber,FAmount, FPerson) VALUES ('1',200, 'Jim')
主键的影响:主键是在同一张表中必须是唯一的,如果在进行数据插入的时候指定的主键与表中已有的数据重复的话则会导致违反主键约束的异常。
例如:INSERT INTO T_Debt (FNumber,FAmount, FPerson) VALUES ('2',300, 'Jim')
外键的影响:外键是指向另一个表中已有数据的约束,因此外键值必须是在目标表中存在的。如果插入的数据在目标表中不存在的话则会导致违反外键约束异常。
例如:INSERT INTO T_Debt (FNumber,FAmount, FPerson) VALUES ('3',100, 'Tom')
数据的更新:UPDATE 语句
例如:UPDATE T_Person SET FRemark = 'SuperMan'
将表T_Person 中所有人员的FREMARK 字段值更新为“SuperMan”:
同时修改多个列,需要使用逗号分隔开:
例如:UPDATE T_Person SET FRemark = 'Sonic',FAge=25
将所有人员的FRemark字段更新为“Sonic”,并且将年龄更新为25
在WHERE 语句中我们设定适当的过滤条件,这样UPDATE 语句只会更新符合WHERE 子句中过滤条件的行,而其他行的数据则不被修改。
例如:UPDATE T_Person SET FAge = 12 WHERE FNAME='Tom'
将FNAME等于‘Tom’的行FAGE字段更新为12
例如:UPDATE T_Person SET FAge = 22 WHERE FName ='jim' OR FName='LXF'
将FNAME 等于’Jim’或者’LXF’的行的FAge字段更新为22
这里我们使用OR逻辑运算符来组合两个条件来实现复杂的过滤逻辑,我们还可以使用OR、NOT等运算符实现更加复杂的逻辑,甚至能够使用模糊查询、子查询等实现高级的数据过滤。
非空约束的影响:如果对一个字段添加了非空约束,那么我们是不能将这个字段中的值更新为NULL的。
例如:UPDATE T_Debt set FAmount =123WHERE FPerson='Tom'
主键的影响:主键是在同一张表中必须是唯一的,如果在进行数据更新的时候指定的主键与表中已有的数据重复的话则会导致违反主键约束的异常。
例如:UPDATE T_Debt set FNumber = '8' WHERE FPerson='Tom'
外键的影响:外键是指向另一个表中已有数据的约束,因此外键值必须是在目标表中存在的。如果更新后的数据在目标表中不存在的话则会导致违反外键约束异常。
例如:UPDATE T_Debt set FPerson = 'Lili' WHERE FNumber='1'
数据的删除:DELETE 语句
删除表中的数据:DELETE FROM T_Debt; DELETE FROM T_Person;
由于T_Debt 表中FPerson 字段是指向表T_Person 的FName 字段的外键,所以必须首
先删除T_Debt表中的数据后才能删除T_Person中的数据。
DELETE 语句仅仅是删除表中的数据行,而表的结构还存在,而DROP TABLE语句则不仅将表中的数据行全部删除,而且还将表的结构也删除。
例如:DELETE FROM T_Person WHERE FAge > 20 or FRemark = 'Mars'
删除年龄大于20 岁或者来自火星(Mars)的人员
数据库检索
创建表格
CREATE TABLE T_Employee
(FNumber VARCHAR(20),
FName VARCHAR(20),
FAge INT,
FSalary NUMERIC(10,2),
PRIMARY KEY (FNumber))
T_Employee为记录员工信息的数据表,其中主键字段FNumber 为员工工号,FName 为人员姓名,FAge 为年龄,FSalary 为员工月工资。
取出一张表中所有的数据:SELECT * FROM 表名
“*”就意味着“所有列”
例如:SELECT * FROM T_Employee
只检索出我们需要的列:将星号“*”替换成我们要检索的列名。
例如:SELECT FNumber FROM T_Employee
检索出多个我们需要的列:只要在SELECT 语句后列出各个列的列名就可以了,需要注意的就是各个列之间要用半角的逗号“,”分隔开。
例如:SELECT FName,FAge FROM T_Employee
SELECT FNumber,FName,FAge,FSalary FROM T_Employee
列别名
别名的定义格式为“列名 AS 别名”,且AS可省略。
例如:SELECT FNumber AS Number1,FName AS Name,FAge AS Age,FSalary AS Salary FROM T_Employee
定义别名的时候“AS”不是必须的,是可以省略的。
例如:SELECT FNumber Number1,FName Name,FAge Age,FSalary Salary FROM T_Employee
如果数据库系统支持中文列名,那么还可以用中文来为列设定别名。
例如:SELECT FNumber 工号,FName 姓名,FAge 年龄,FSalary 工资 FROM T_Employee
按条件过滤
数据检索是数据库系统的一个非常重要的任务,它内置了对按条件过滤数据的支持,只要为SELECT 语句指定WHERE 语句即可。
数据库会采用适当的优化算法进行查询,大大降低了CPU资源的占用。
例如:SELECT FName FROM T_Employee WHERE FSalary<5000
检索出所有工资少于5000 元的员工的姓名
SELECT * FROM T_Employee WHERE FSalary<5000 OR FAge>25
检索出所有工资少于5000 元或者年龄大于25 岁的员工的所有信息
数据汇总
这几个聚合函数都有一个参数,这个参数表示要统计的字段名,比如要统计工资总额,那么就需要把FSalary做为SUM函数的参数。
例如:SELECT MAX(FSalary) FROM T_Employee WHERE FAge>25
SELECT MAX(FSalary) as 最高工资 FROM T_Employee WHERE FAge>25
查询年龄大于25岁的员工的最高工资
SELECT AVG(FAge) FROM T_Employee WHERE FSalary>3800
来统计一下工资大于3800元的员工的平均年龄
SELECT SUM(FSalary) FROM T_Employee
统计一下公司每个月应支出工资总额
SELECT MIN(FSalary),MAX(FSalary) FROM T_Employee
统计公司的最低工资和最高工资
统计记录数量的COUNT,这个函数有一点特别,因为它的即可以像其他聚合函数一样使用字段名做参数,也可以使用星号“*”做为参数。
例如:SELECT COUNT(*),COUNT(FNumber) FROM T_Employee
COUNT(*)、COUNT(FNumber)两种方式都能统计出记录的条数
注:COUNT(*)统计的是结果集的总条数,
而COUNT(FName)统计的则是除了结果集中FName 不为空值(也就是不等于NULL)的记录的总条数。
排序
执行结果集的排序方式:使用ORDER BY子句
ORDER BY 子句位于SELECT 语句的末尾,它允许指定按照一个列或者多个列进行排序,还可以指定排序方式是升序ASC(从小到大排列)还是降序DESC(从大到小排列)。
例如:SELECT * FROM T_Employee ORDER BY FAge ASC
“ORDER BY FAge ASC”指定了按照FAge 字段的顺序进行升序排列,其中ASC代表升序。
对于ORDER BY子句来说,升序是默认的排序方式,所以如果要采用升序的话可以不指定排序方式,
如果需要按照降序排列,那么只要将ASC 替换为DESC 即可,其中DESC 代表降序。
ORDER BY语句允许指定多个排序列,各个列之间使用逗号隔开即可。
例如:SELECT * FROM T_Employee ORDER BY FAge DESC,FSalary DESC
首先按照FAge 字段的降序进行排列,如果按照个排序规则无法区分两条记录的顺序,则按照FSalary字段的降序进行排列。
对于多个排序规则
数据库系统会按照优先级进行处理。数据库系统首先按照第一个排序规则进行排序;
如果按照第一个排序规则无法区分两条记录的顺序,则按照第二个排序规则进行排序;
如果按照第二个排序规则无法区分两条记录的顺序,则按照第三个排序规则进行排序;
……
以此类推
ORDER BY子句完全可以与WHERE子句一起使用,唯一需要注意的就是ORDER BY子句要放到WHERE子句之后,不能颠倒它们的顺序。
例如:SELECT * FROM T_Employee WHERE FAge>23 ORDER BY FAge DESC,FSalary DESC
高级数据过滤
通配符过滤:SQL中的通配符过滤使用LIKE 关键字,可以像使用OR、AND 等操作符一样使用它,它是一个二元操作符,左表达式为待匹配的字段,而右表达式为待匹配的通配符表达式。通配符表达式由通配符和普通字符组成,主流数据库系统支持的通配符有单字符匹配和多字符匹配,有的数据库系统还支持集合匹配。
进行单字符匹配的通配符为半角下划线“_”,它匹配单个出现的字符。
例如:SELECT * FROM T_Employee WHERE FName LIKE '_erry'
SELECT * FROM T_Employee WHERE FName LIKE '__n_'
进行多字符匹配的通配符为半角百分号“%”,它匹配任意次数(零或多个)出现的任意字符。
例如:SELECT * FROM T_Employee WHERE FName LIKE 'T%'
SELECT * FROM T_Employee WHERE FName LIKE '%n%'
SELECT * FROM T_Employee WHERE FName LIKE '%n_'
集合匹配:进行集合匹配的通配符为“[]”,方括号中包含一个字符集,它匹配与字符集中任意一个字符相匹配的字符。
(集合匹配只在MSSQLServer 上提供支持。)
比如通配符表达式“[bt]%”匹配第一个字符为b 或者t、长度不限的字符串。
例如:SELECT * FROM T_Employee WHERE FName LIKE '[SJ]%'
亦是 SELECT * FROM T_Employee WHERE FName LIKE 'S%' OR FName LIKE 'J%'
以“S”或者“J“开头长度,长度任意。
还可以使用否定符“^”来对集合取反,它匹配不与字符集中任意一个字符相匹配的字符
例如:SELECT * FROM T_Employee WHERE FName LIKE '[^SJ]%'
亦是 SELECT * FROM T_Employee WHERE NOT(FName LIKE 'S%') AND NOT(FName LIKE 'J%')
不以“S”或者“J“开头长度,长度任意。
注:
通配符过滤一个非常强大的功能,不过在使用通配符过滤进行检索的时候,数据库系统
会对全表进行扫描,所以执行速度非常慢。因此不要过分使用通配符过滤,在使用其他方式
可以实现的效果的时候就应该避免使用通配符过滤。
空值检测:使用方法“待检测字段名 IS NULL”
例如:SELECT * FROM T_Employee WHERE FNAME IS NULL
查询所有姓名未知的员工信息
如果要检测“字段不为空”,则要使用IS NOT NULL,使用方法为“待检测字段名IS NOT NULL”
IS NULL/IS NOT NULL可以和其他的过滤条件一起使用。
例如:SELECT * FROM T_Employee WHERE FNAME IS NOT NULL AND FSalary <5000
反义运算符:“=”、“<”、“>”等运算符都是用来进行数值判断的
MSSQLServer、DB2提供了“!”运算符来对运算符求反义,也就是“!=”表示“不等于”、“!<”表示“不小于”,而“!>”表示“不大于”。
例如:SELECT * FROM T_Employee WHERE FAge!=22 AND FSALARY!<2000
检索所有年龄不等于22岁并且工资不小于2000元
最常用的变通实现方式有两种:使用同义运算符、使用NOT运算符。(也可在其他数据库使用)
“不等于”、“不大于”和“不小于”就分别可以表示成“<>”、“<=”和“>=”
上例亦可表示为:SELECT * FROM T_Employee WHERE FAge<>22 AND FSALARY>=2000
NOT运算符用来将一个表达式的值取反,也就是将值为“真”的表达式结果变为“假”、将值为“假”的表达式结果变为“真”,
使用方式也非常简单“NOT (表达式)”。
上例也可以写成:SELECT * FROM T_Employee WHERE NOT(FAge=22) AND NOT(FSALARY<2000)
多值检测:可以使用OR语句来连接多个等于判断
例:SELECT FAge,FNumber,FName FROM T_Employee WHERE FAge=23 OR FAge=25 OR FAge=28
行多个离散值的匹配问题,SQL提供了IN语句,使用IN我们只要指定要匹配的数据集合就可以了,
使用方法为“IN (值1,值2,值3……)”。
上例也可以写成:SELECT FAge,FNumber,FName FROM T_Employee WHERE FAge IN (23,25,28)
范围值检测:语句“BETTWEEN AND”,它可以用来检测一个值是否处于某个范围中(包括范围的边界值,也就是闭区间)。
如果需要进行开区间或者半开半闭区间的范围值检测的话就必须使用其他的解决方案了。
例:SELECT * FROM T_Employee WHERE FAGE BETWEEN 23 AND 27
SELECT * FROM T_Employee WHERE (FSalary BETWEEN 2000 AND 3000) OR (FSalary BETWEEN 5000 AND 8000)
低效的“WHERE 1=1”:
为SQL语句指定一个永远为真的条件语句(比如“1=1”),这样就不用考虑WHERE语句是否存在的问题了。
String sql = " SELECT * FROM T_Employee WHERE 1=1";
if(工号复选框选中)
{
sql.appendLine("AND FNumber BETWEEN '"+工号文本框1内容+"' AND '"+工号文本框2内容+"'");
}
if(姓名复选框选中)
{
sql.appendLine("AND FName LIKE '%"+姓名文本框内容+"%'");
}
if(年龄复选框选中)
{
sql.appendLine("AND FAge BETWEEN "+年龄文本框1内容+" AND "+年龄文本框2内容);
}
executeSQL(sql);
这样如果不选中姓名和年龄前的复选框的时候就会执行下面的SQL语句:
SELECT * FROM T_Employee WHERE 1=1 AND FNumber BETWEEN 'DEV001' AND 'DEV008' AND FSalary BETWEEN 3000 AND 6000
而如果将所有的复选框都不选中的时候就会执行下面的SQL语句:
SELECT * FROM T_Employee WHERE 1=1
但是,数据库系统将会被迫对每行数据进行扫描(也就是全表扫描)以比较此行是否满足过滤条件,当表中数据量比较大的时候查询速度会非常慢。因此如果数据检索对性能有比较高的要求就不要使用这种“简便”的方式。
参考实现:
private void doQuery() { Bool hasWhere = false; StringBuilder sql = new StringBuilder(" SELECT * FROM T_Employee"); if(工号复选框选中) { hasWhere = appendWhereIfNeed(sql, hasWhere); sql.appendLine("FNumber BETWEEN '"+工号文本框1内容+"' AND '"+工号文本框2内容+"'"); } if(姓名复选框选中) { hasWhere = appendWhereIfNeed(sql, hasWhere); sql.appendLine("FName LIKE '%"+姓名文本框内容+"%'"); } if(年龄复选框选中) { hasWhere = appendWhereIfNeed(sql, hasWhere); sql.appendLine("FAge BETWEEN "+年龄文本框1内容+" AND "+年龄文本框2内容); } executeSQL(sql); } private Bool appendWhereIfNeed(StringBuilder sql,Bool hasWhere) { if(hasWhere==false) { sql. appendLine("WHERE"); } else { sql. appendLine("AND"); } }
数据分组:使用方式为“GROUP BY 分组字段”。
分组语句必须和聚合函数一起使用,GROUP BY子句负责将数据分成逻辑组,而聚合函数则对每一个组进行统计计算。
例:SELECT FAge FROM T_Employee GROUP BY FAge
查看公司员工有哪些年龄段的
GROUP BY子句必须放到SELECT语句的之后,如果句中有WHERE子句,则GROUP BY子句必须放到WHERE语句的之后
例:SELECT FAge FROM T_Employee
WHERE FSubCompany = 'Beijing'
GROUP BY FAge
注:没有出现在GROUP BY子句中的列(聚合函数除外),不能放到SELECT语句后的列名列表中。
例:SELECT FAge,AVG(FSalary) FROM T_Employee GROUP BY FAge
SELECT FSubCompany,FDepartment FROM T_Employee GROUP BY FSubCompany,FDepartment
SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee GROUP BY FAge
统计每个年龄段的员工的人数。
年龄相同的员工被分到了一组,接着使用“COUNT(*)”来统计每一组中的条数,这样就得到了每个年龄段的员工的个数
SELECT FSubCompany,FAge,COUNT(*) AS CountOfThisSubCompAge FROM
T_Employee
GROUP BY FSubCompany,FAge
ORDER BY FSubCompany
GROUP BY子句将检索结果首先按照FSubCompany进行分组,然后在每一个分组内又按照FAge进行分组
“COUNT(*)”对每一个分组统计总数,这样就可以统计出每个公司每个年龄段的员工的人数了。SUM、AVG、MIN、MAX也可以在分组中使用。
例:SELECT FSubCompany,SUM(FSalary) AS FSalarySUM FROM T_Employee GROUP BY FSubCompany
Having 语句:聚合函数不能在WHERE语句中使用,必须使用HAVING子句来代替
例:SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee GROUP BY FAge HAVING COUNT(*)>1
HAVING语句中也可以像WHERE语句一样使用复杂的过滤条件
例:SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee
GROUP BY FAge
HAVING COUNT(*) =1 OR COUNT(*) =3
HAVING语句能够使用的语法和WHERE几乎是一样的,不过使用WHERE的时候GROUP BY子句要位于WHERE子句之后,而使用HAVING子句的时候GROUP BY子句要位于HAVING子句之前。
where….. group by ……order by ……
group by….. having……
注:HAVING语句中不能包含未分组的列名,需要用WHERE语句来代替HAVING
限制结果集行数:
MSSQLServer2000中提供了TOP关键字用来返回结果集中的前N条记录,其语法为“SELECT TOP 限制结果集数目 字段列表 SELECT语句其余部分”
例:select top 5 * from T_Employee order by FSalary Desc
检索工资水平排在前五位(按照工资从高到低)的员工信息
要实现检索按照工资从高到低排序检索从第六名开始一共三个人的信息,则:
SELECT top 3 * FROM T_Employee
WHERE FNumber NOT IN
(SELECT TOP 5 FNumber FROM T_Employee ORDER BY FSalary DESC)
ORDER BY FSalary DESC
首先将前五名的主键取出来,在检索的时候检索排除了这五名员工的前三个人,
MSSQLServer2005提供了新的特性来帮助更好的限制结果集行数的功能,这个新特性就是窗口函数ROW_NUMBER()
ROW_NUMBER()函数:可以计算每一行数据在结果集中的行号(从1开始计数),其使用语法
如下:ROW_NUMBER() OVER(排序规则)
例:SELECT ROW_NUMBER() OVER(ORDER BY FSalary),FNumber,FName,FSalary,FAge FROM T_Employee
注:ROW_NUMBER()不能用在WHERE语句中
返回第3行到第5行的数据
SELECT * FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY FSalary DESC) AS rownum,
FNumber,FName,FSalary,FAge FROM T_Employee
) AS a
WHERE a.rownum>=3 AND a.rownum<=5
数据库分页:
实现数据库分页的核心技术就是“限制结果集行数”。(数据库系统使用的MYSQL)
int CurrentIndex=0; PageSize=10; //按钮【首页】被点击 private void btnFirstButtonClick() { CurrentIndex=0; DoSearch(); } //按钮【尾页】被点击 private void btnLastButtonClick() { CurrentIndex=GetTotalCount()/PageSize; DoSearch(); } //按钮【下一页】被点击 private void btnNextButtonClick() { CurrentIndex= CurrentIndex+1; DoSearch(); } //按钮【上一页】被点击 private void btnNextButtonClick() { CurrentIndex= CurrentIndex-1; DoSearch(); } //计算表中的总数据条数 private int GetTotalCount() { ResultSet rs = ExecuteSQL("SELECT COUNT(*) AS TOTALCOUNT FROM T_Employee "); return rs.getInt("TOTALCOUNT"); } //查询当前页中的数据 private void DoSearch() { //计算当前页的起始行数 String startIndex = (CurrentIndex* PageSize).ToString(); String size = PageSize.ToString() ResultSet rs = ExecuteSQL("SELECT * FROM T_Employee LIMIT "+ startIndex +","+ size); //显示查询结果 DisplayResult(rs); }
抑制数据重复
DISTINCT关键字是用来进行重复数据抑制的最简单的功能,而且所有的数据库系统都支持DISTINCT,
DISTINCT的使用也非常简单,只要在SELECT之后增加DISTINCT即可。
例:SELECT DISTINCT FDepartment FROM T_Employee
DISTINCT是对整个结果集进行数据重复抑制的,而不是针对每一个列。即只有完全相同的检索结果才可被抑制
计算字段
需要的数据不是数据表中本来就有的,必须经过一定的计算、转换或者格式化,这种情况下我们可以在宿主语言中通过编写代码的方式来进行这些计算、转换或者格式化的工作。
常量字段
SELECT 'CowNew集团'AS CompanyName,918000000 AS RegAmount,FName,FAge,FSubCompany FROM T_Employee
这里的'CowNew集团'和918000000并不是一个实际的存在的列,但是在查询出来的数据中它们看起来是一个实际存在的字段,这样的字段被称为“常量字段”(也称为“常量值”),
它们完全可以被看成一个值确定的字段。
字段间计算
要计算将FAge和FSalary的乘积做为一个工资指数列体现到检索结果中:
SELECT FNumber,FName,FAge * FSalary FROM T_Employee
前面提到常量字段完全可以当作普通字段来看待,那么普通字段也可以和常量字段进行计算,甚至常量字段之间也可以进行计算
统计每个员工的工资幸福指数,工资幸福指数的计算公式为工资/(年龄-21),而且要求在每行数据前添加一列,这列的值等于125与521的和:
SELECT 125+521,FNumber,FName,FSalary/(FAge-21) AS FHappyIndex FROM T_Employee
计算字段也可以在WHERE语句等子句或者UPDATE、DELETE中使用
例:SELECT * FROM T_Employee WHERE FSalary/(FAge-21)>1000
数据处理函数
除了“聚合函数”,SQL中还有其他类型的函数,比如进行数值处理的数学函数、进行日期处理的日期函数、进行字符串处理的字符串函数等。
计算字符串长度的函数:LEN
例:SELECT FName, LEN(FName) AS namelength FROM T_Employee WHERE FName IS NOT NULL
取得字符串的子串的函数:SUBSTRING。
这个函数接受三个参数,第一个参数为要取的主字符串,第二个参数为字串的起始位置(从1开始计数),第三个参数为字串的长度。
例:SELECT FName, SUBSTRING(FName,2,3) FROM T_Employee WHERE FName IS NOT NULL
多个函数还可以嵌套使用。
计算正弦函数值的函数SIN和计算绝对值的函数ABS,它们都接受一个数值类型的参数。
取得每个员工的姓名、年龄、年龄的正弦函数值以及年龄的正弦函数值的绝对值:
SELECT FName,FAge, SIN(FAge) , ABS(SIN(FAge)) FROM T_Employee
字符串的拼接
MSSQLServer中可以直接使用加号“+”来拼接字符串。
SELECT '工号为'+FNumber+'的员工姓名为'+Fname FROM T_Employee WHERE FName IS NOT NULL
计算字段的其他用途
我们不仅能在SELECT语句中使用计算字段,我们同样可以在进行数据过滤、数据删除以及数据更新的时候使用计算字段
×计算处于合理工资范围内的员工
上限为年龄的1.8倍加上5000元,下限为年龄的1.5倍加上2000元,介于这两者之间的即为合理工资:
SELECT * FROM T_Employee
WHERE Fsalary BETWEEN Fage*1.5+2000 AND Fage*1.8+5000
×查询“工资年龄指数”
定义“工资年龄指数”为“工资除以年龄”。我们需要查询“工资年龄指数”的最高值和最低值
SELECT MAX(FSalary/FAge) AS MAXVALUE,MIN(FSalary/FAge) AS MINVALUE FROM T_Employee
×年龄全部加1
UPDATE T_Employee SET FAge=FAge+1
×不从实体表中取的数据
SELECT DISTINCT 1 FROM T_Employee
注:如果不加DISTINCT,那么出现的结果会有与总条数相同的1
MYSQL和MSSQLServer允许使用不带FROM子句的SELECT语句来查询这些不属于任何实体表的数据
例:SELECT 1
SELECT LEN('abc')
SELECT 1,2,3,'a','b','c'
联合结果集
在SQL中可以使用UNION运算符来将两个或者多个查询结果集联合为一个结果集中。
UNION运算符要放置在两个查询语句之间
只要用UNION操作符连接这两个查询语句就可以将两个查询结果集联合为一个结果集。
例:
SELECT FNumber,FName,FAge FROM T_Employee
UNION
SELECT FIdCardNumber,FName,FAge FROM T_TempEmployee
UNION可以连接多个结果集,就像“+”可以连接多个数字一样简单,只要在每个结果集之间加入UNION即可。
使用UNION仍然有两个基本的原则需要遵守:
一是每个结果集必须有相同的列数;二是每个结果集的列必须类型相容。
UNION ALL
这时因为默认情况下,UNION运算符合并了两个查询结果集,其中完全重复的数据行被合并为了一条。如果需要在联合结果集中返回所有的记录而不管它们是否唯一,
则需要在UNION运算符后使用ALL操作符,
例:
SELECT FName,FAge FROM T_Employee
UNION ALL
SELECT FName,FAge FROM T_TempEmployee
要求查询每位正式员工的信息,包括工号、工资,并且在最后一行加上所有员工工资额合计:
SELECT FNumber,FSalary FROM T_Employee
UNION
SELECT '工资合计',SUM(FSalary) FROM T_Employee
要求打印出打印5以内自然数以及它们的平方数:
SELECT 1,1 * 1
UNION
SELECT 2,2 * 2
UNION
SELECT 3,3 * 3
UNION
SELECT 4,4 * 4
UNION
SELECT 5,5 * 5
要求列出公司中所有员工(包括临时工)的姓名,将重复的姓名过滤掉:
SELECT FName FROM T_Employee
UNION
SELECT FName FROM T_TempEmployee
分别列出正式员工和临时工的姓名,要求分别列出正式员工和临时工的姓名,要保留重复的姓名:
SELECT '以下是正式员工的姓名'
UNION ALL
SELECT FName FROM T_Employee
UNION ALL
SELECT '以下是临时工的姓名'
UNION ALL
SELECT FName FROM T_TempEmployee