数据库 -- SQL

背景

SQL最早版本是有IBM开发,最初叫做Sequel,是20世纪70年代早期作为System R项目的一部分实现的。现在已变成SQL(结构化查询语言)。SQL已经很明确地建立起自己作为标准关系数据库语言的地位。1986年美国国家标准化组织(ANSI)和国际标准化组织(ISO)发布了SQL标准:SQL-86,1989年,ANSI发布了一个SQL的扩充标准:SQL-89。该标准的下一个版本是SQL-92标准,接着是SQL:1999标准,还有近期的SQL:2003版本。

SQL语言有以下部分组成:
* 数据定义语言(data-definition language, DDL): SQL DDL提供定义关系模式,删除关系以及修改关系模式的命令。
* 交互式数据操纵语言(interactive data-manipulating language, DML): SQL DML包含基于关系代数和元组关系演算的查询语言,还包含在数据库中插入、删除、修改元组的命令。
* 完整性(integrity):SQL DDL包含定义保存在数据库中的数据必须满足的完整性约束条件的命令。破环完整性约束条件的更新将被禁止。
* 视图定义(view definition): SQL DDL包含定义视图的命令。
* 事务控制(transaction control): SQL提供定义事务的开始和结束的命令。
* 嵌入式SQL和动态SQL(embedded SQL and dynamic SQL): 嵌入式SQL和动态SQL定义SQL语言如何嵌入到通用编程语言,如C、C++、Java、PL/I、Codol、Pascal和Fortran中。
* 授权(authorization):SQL DDL中包含说明对关系和视图的访问权限的命令。

数据定义

1.基本域类型

* char(n): 固定长度的字符串,用户指定长度为n,也可以用全称 character。
* varchar(n): 可变长度的字符串,用户指定最大长度n,等价于全称 character varying。
* int: 整数类型(和机器相关的整数的有限子集),等价于全称 integer。
* smallint: 小整数类型(和机器相关的整数域类型的子集)。
* numberic(p, d): 定点数,精度由用户指定。这个数由p位数字(还有一个符号位),其中d位数字在小数点右边。
* real, double precision: 浮点数与双精度浮点数,精度与机器有关。
* float(n): 精度至少为 n 位的浮点数。

2. 基本模式定义

我们用create table命令定义SQL关系,create table r(A1D1, A2D2, ......, AnDn, <完整性约束>, ..., <完整性约束>)
如下是建立银行数据库的部分SQL数据定义:
create table customer
	(customer_name char(20),
	 customer_street char(30),
	 customer_city char(30),
	 primary key(customer_name))

create table branch
	(branch_name char(15),
	 branch_city char(30),
	 assets numeric(16, 2),
	 primary key(branch_name))

create table account
	(account_number char(10),
	 branch_name char(15),
	 balance numeric(12, 2),
	 primary key(account_number))

create table depositor
	(customer_name char(20),
	 account_number char(10),
	 primary key(customer_name, account_number)) 
一个新创建的关系最初是空的。我们可以用 insert 命令将数据加载到关系中,例如:
/*在Perryridge支行有一个账户A-9732,其余额为1200美元*/
insert into account
	values('A-9732', 'Perryridge', 1200)

可以使用delete命令从关系中删除元组,例如:
delete from account	/*从account关系中删除所有元组,保留了关系r*/

drop table account /*比delete from 更强烈,不仅删除了account的所有元组,还删除了r的模式*/

可以使用alter table 命令为已有的关系增加属性。关系中的所有元组在该新属性的取值将设为null,alter table命令的格式为:
alter table r add A D /* 向r表中添加属性A,添加属性的域为D */i

alter table r drop A /* 从关系r去掉属性A*/

SQL查询的基本结构

SQL表达式基本结构包括三个子句: select、from和where
* select子句对应关系代数中的投影运算,用来列出查询结果中所要的属性。
* from 子句对应关系代数中的笛卡儿积,列出表达式求值中需扫描的关系。
* where 子句对应关系代数中的选择谓词,它包含一个作用在from子句中关系的属性上的谓词
例如:

select 子句


select branch_name	/*找出所有支行的名字*/
from loan

select all branch_name	/*使用all关键字来显式指明不去除重复*/
from loan

select distinct branch_name /*使用distinct关键字来强行删除重复*/
from loan

select loan_number, branch_name, amount * 100	/*返回属性amount的值的100倍*/
from loan 	/*select子句还可以包含+、-、*、/的算术表达式,运算对象为常数或元祖的属性*/

where子句

select loan_number /*查询所有在Perryridge支行贷款并且贷款超过1200美元的贷款的贷款号*/
from loan
where branch_name = 'Perryridge' and amount > 1200

select loan_number	/*找出贷款额为90000美元和100000美元之间的贷款的贷款号*/
from loan
where amount between 90000 and 100000

from子句

select customer_name, borrower.loan_number, amount	/*找出在Perryridge银行中有贷款的所有客户的姓名,贷款号和贷款额*/
from borrower, loan
where borrower.loan_number = loan.loan_number and 
	  branch_name = 'Perryridge'

更名运算

SQL提供了为关系和属性重新更名的机制,这是通过as子句来进行的.旧名 as 新名. as 子句既可以出现在from 子句中,又可以出出现在select子句中.
select customer_name, borrower.loan_number as loan_id, amount
from borrower, loan
where borrower.loan_number = loan.loan_number

元祖变量是通过在from子句中使用as 子句来定义的
select customer_name, T.loan_number, S.amount
from borrower as T, loan as S
where T.loan_number = S.loan_number

字符串运算

SQL使用一对单引号来标示字符串。如果单引号是字符串的一部分,可以用两个单引号来表示。如" It's right" ,可以用" It''s right"来标识。最常见的字符串运算是使用了like运算符的模式匹配。我们使用两个特殊的字符来描述模式: 百分号(%):匹配任意子串 下划线(_):匹配任意一个字符。 模式是对大小写敏感的。
select customer_name /*找出街道地址中包含子串'Main'的所有客户名
from customer 
where customer_street like '%Main%'
我们可以在like比较运算中使用escape关键词来定义转义字符。例如 like 'ab\%cd%' escape '\' 匹配所有ab%cd开头的字符串。SQL还允许在字符串上使用not like比较运算符来搜寻不匹配项。SQL还允许在字符串上有多种函数,例如串联(使用“ || ”,提取子串,计算字符串长度,大小写转换(用upper()将字符串转换为大写或用lower()将字符串转换为小写)在SQL1999中提供了similar to运算,它具备比like运算更强大的匹配能力,它的模式定义的语法类似于Unix中的正则表达式。

排列元组的显示次序

SQL为用户提供了一些对关系中元组显示次序的控制。order by子句可以将查询结果中元组按排序顺序显示。
select distinct cumstomer_name	/*按字母列出在Perryridge支行中有贷款的客户*/
from borrower, loan
where borrower.loan_number = loan.loan_number and
		branch_name = 'Perryridge'
order by customer_name

集合运算

SQL作用在关系上的union, intersect 和 except运算对应于关系代数中的运算" 并 ". " 交 ”, “ 差 ”,当然参加运算的关系必须相容。也就是说,它们必须要有相同的属性集。

union运算

例如找出在银行有账户,有贷款或两者都有的所有客户
(select customer_name
 from depositor)
union
(select customer_name
 from borrower)
union运算会自动去除重复,这与select子句不同。如果想保留所有重复,我们必须用union all代替union.

intersect运算

例如为了找出在银行同时有账户和贷款的客户

(select distinct customer_name
 from depositor)
 intersect
 (select distinct customer_name
  from borrower)

intersect运算自动去除重复,如果想保留所有重复,我们必须用intersect all 代替 intersect

except运算

为了找出银行中有账户但无存款的客户

(select distinct customer_name
 from depositor)
except 
(select customer_name
 from borrower)

except运算会自动去除重复,想保留重复必须用except all 代替 except

聚集函数

聚集函数是以值的一个集合为输入,返回单个值的函数。SQL提供了5个内建函数:
平均值: avg   最小值: min   最大值:max    总和:sum      计数: count     sum和avg输入必须是数字集,而其他运算还可作用在非数字类型,如字符串上。
例如:找出Perryridge支行账户余额平均值:
select avg(balance)
from account
where branch_name = 'Perryridge'

我们可以用as子句给结果关系的属性赋个名称。
我们可以将聚集函数作用在一组元组集上,在SQL中,我们可以用group by子句实现这个愿望。group by子句中的一个或多个属性是用来构造分组的,在group by子句中的所有属性上具有相同值的的元组被分在一个组中。
select branch_name, avg(balance)
from account
group by branch_name
可以在聚集表达式中使用distinct,count(distinct customer_name)。可以使用SQL的having子句,它在形成分组后才起作用,因此可以使用聚集函数。

select branch_name, avg(balance) 
from account
group by branch_name
having avg(balance) > 120 

SQL不允许在用count(*)是使用distinct, 在max, min可以使用。如果having子句和where子句出现在同一个查询时,那么SQL首先应用where子句中的谓词,满足where谓词的元组通过group by子句形成分组。having子句若存在,就将作用于每一个分组。不符合having子句谓词的分组将被抛弃。剩下的组被select子句用来产生查询结构元组。
/*找出住在Harrison且在银行中至少有三个账户的平均余额*/
select depositor.customer_name, avg(balance)
from depositor, account, customer
where depositor.account_number = account.account_number and
	  depositor.customer_name = customer.customer_name and
	  customer_city = 'Harrison'
group by depositor.customer_name
having count(distinct depositor.account_number) >= 3

空值

SQL允许使用null值表示某属性值的信息缺失。我们可以使用is not null 用来检测非空值; is null 来检测空值。如果算术运算的输入有一个是空的,则该算术表达式的结果是空。如果有空值进行了比较运算,SQL将比较运算的结果看成unknown。由于在where子句中使用and, or, not等布尔运算符,所以布尔运算符也被扩展到可以处理unknown值。

嵌套子查询

SQL提供嵌套子查询机制。子查询是其那套在另一个查询中的select-from-where表达式。一般子查询的使用是为了对集合的成员资格,集合的比较以及集合的基数进行检查。连接词in可以测试元组是否是集合中成员,not in 测试元组是否不是集合中的成员。

/*找出Perryridge支行同时有账户和贷款的客户*/
select distinct customer_name
from borrower, loan
where borrower.loan_number = loan.loan_number and
	  branch_name = 'Perryridge' and
	  (branch_name, customer_name)in
	  			(select branch_name, customer_name
	  			 from depositor, account
	  			 where depositor.account_number = account.account_number)


集合的比较

在SQL中用>some表示“至少比某一个要大”, 也允许使用<some, <= some, >=some,=some和<>some的比较。=some等价于in,在SQL中关键词any同义于some。>all对应于“比所有的都大”,也可以使用<all, <=all, >=all, =all和<>all的比较。
如:
/*找出那些总资产比Brooklyn任意一家支行都多的支行名*/
select branch_name
from branch
where assets > all(select assets
					from branch
					where branch_city = 'Brroklyn')

测试是否是空关系

exists结构在作为参数的子查询非空时返回true。使用not exists结构可以查询元组值是否为空。还可以用not exists结构模拟集合包含运算。“关系A包含关系B” 写成“not exists(B except A)",例如:
/*找出在Brooklyn所有支行都有账户的客户*/
select distinct S.customer_name
from depositor as S
where not exists((select branch_name
					from branch
					where branch_city = 'Brooklyn')
				except
				 (select R.branch_name
				  from depositor as T, account as R
				  where T.account_number = R.account_number and
				  		S.customer_name = T.customer_name))

测试是否存在重复元组

作为参数的子查询的结果中没有重复的元组,unique结构将返回true。我们可以用unique结构书写查询语句“找出所有Perryridge支行中只有一个账户的客户”

select T.customer_name
from depositor as T
where unique(select R.customer_name
			 from account, depositor as R
			 where T.custoemr_name = R.custoemr_name and
			 		R.account_number = account.account_number and
			 		account.branch_name = 'Perryridge')

同时,可以使用not unique结构测试一个子查询的结构是否存在重复的元组。

复杂查询

复杂查询通常很难或根本不可能在一个SQL查询块或者几个SQL查询块的并,交,差来解决。我们可以用多个SQL语句快表达一个复杂查询: 派生关系和with子句

1.派生关系

在from子句中使用子查询表达式,我们必须给表达式产生的结果关系命名,其属性也可以重新命名。
/*找出平均账户余额大于1200美元的支行的平均账户余额*/
select branch_name, avg_balance
from(select branch_name, avg(balance)
	 from account
	 group by branch_name)
	 as branch_avg(branch_name, avg_balance)
where avg_balance > 1200

/*找出在所有支行中总余额最多的支行*/
select max(tot_balance)
from (select branch_name, sum(balance)
	   from account
	   group by branch_name) as branch_total(branch_name, tot_balance)

2.with子句

with子句提供定义临时视图的方法,这个定义只对with子句出现在那条查询有效.
with max_balance(value) as
	select max(balance)
	from account
select account_number
from account,,max_balance
where account.balance = max_balance.value

with子句是SQL:1999中引入的,目前只有一部分数据库对此提供支持.

查询所有存款总额大于全部支行平均存款的支行,利用with子句:
with branch_total(branch_name, value) as
	select branch_name, sum(balance)
	from account
	group by branch_name
with branch_total_avg(value) as
	select avg(value)
	from branch_total
select branch_name
from branch_total, branch_total_avg
where branch_total.value >= branch_total_avg.value

视图

任何不是逻辑模型的一部分但作为虚关系对用户可见的关系称为视图.在任何给定的实际关系的集合上都能够支持大量视图.

视图定义

在SQL中用create view命令定义视图.要定义视图,我们要给视图一个名称,并提供计算视图的查询.create view v as <查询表达式>,v表示视图名.
/*定义一个视图all_customer,它包含各支行的名字以及它们的客户*/
create view all_customer as
	(select branch_name, customer_name
	 from depositor, account
	 where depositor.account_number = account.account_number)
	union
	(select branch_name, customer_name
	 from borrower, loan
	 where borrower.loan_number = loan.loan_number)
定义完,我们就可以用视图名指代该视图生成的虚关系.使用视图all_customer,我们可以用下面的查询找到Perryridge支行的所有客户:
select customer_name
from all_customer
where branch_name = 'Perryridge'

只要没有更新操作在视图上执行,视图名可以出现在关系名可以出现的任何地方.在任何给定时刻,视图关系的元祖集是该时刻视图定义中的查询表达式的计算结果.视图一般是在定义时,数据库系统存储视图定义的本身.无论何时,我们执行这个查询,视图关系都被重新计算.

用其他视图定义视图

视图展开是一种定义视图含义的方法,其中的视图是用其他视图来定义的.视图关系代表定义该视图的表达式,因此视图关系可以有定义它的表达式代替,生成的表达式还可能含有其他的视图关系.只要视图不是递归的,就会循环对视图展开





猜你喜欢

转载自blog.csdn.net/WilliamChancwl/article/details/78265468