SQL语句不区分大小写,空格都被忽略
row 行
column 列
select prod_name
from Products;
select prod_id,prod_name,prod_price
from products;
select * from products;
select distinct prod_id,prod_price
from products;
distinct关键字作用于所有的列
mySQL中使用limit
select prod_name
from products
limit 5;
还可以指定起始行,使用offset
select prod_name
from products
limit 5 offset 4;
#返回从第4行起的5行数据,4是指行的标号,标号是从第0行开始的。(所以实际是表中的第5行开始的5行)
MySQL支持简写limit 4,5;
前一个数字表示offset,后一个表示limit
--
或者 #
(行首有效)或者/**/
select prod_name
from Products; -- 没有特定顺序
使用order by子句
select prod_id,prod_price,prod_name
from products
order by prod_price,prod_name;
#先按照prod_price排序,再按照prod_name排序
等价于
select prod_id,prod_price,prod_name
from products
order by 2,3;
注意:
1、order by 必须是select语句的最后一条子句。
2、order by后面可以是未选择的列,可以按照未选择的列进行排序
3、order by 2,3是按照相对列位置来进行排序的。相对位置是指select子句中选中列的相对位置。但是使用相抵位置就不能按未选中列来排序了。
使用desc关键字进行降序排序
select prod_id,prod_price,prod_name
from products
order by prod_price desc;
注意:desc关键字只应用到直接位于其前面的列名,如果对多个列进行降序排序,只指定一个的话其他仍按照升序排序。需要在每个列名前面都加上desc关键字。
where子句指定搜索条件(过滤条件)
#1
select prod_name,prod_price
from products
where prod_price = 3.49;#与数值比较不需要加引号
#2
select prod_name,prod_price
from products
where vend_id <> "DLL01";#与字符串类型的列进行比较需要加引号
注意:SQL中只有单引号,双引号是MySQL对SQL的扩展。
select prod_name,prod_price
from products
where prod_price between 5 AND 10;
select cust_name
from customers
where cust_email is null;
注意:匹配过滤或者非匹配过滤的时候都不会返回NULL行。
组合where子句从而建立功能更强更高级的搜索条件
可以使用多个where子句进行更强的过滤控制,有AND子句和OR子句两种使用方式。 (操作符/逻辑操作符)
AND
select prod_id,prod_price,prod_name
from products
where vend_id = 'DLL01' and prod_price<=4;
OR
select prod_id,prod_price,prod_name
from products
where vend_id = 'DLL01' OR vend_id ='BRS01'
AND和OR优先级
select prod_name,prod_price
from products
where (vend_id = 'DLL01' OR vend_id ='BRS01' )and prod_price>=10;
在where子句中,AND的优先级高于OR,所以在AND和OR都存在的时候,可以使用圆括号来改变优先级。
in是where子句中用来指定要匹配值清单的关键字,功能与OR相当。但是有很多优点:
可以包含其他select语句(嵌套查询)
select prod_name,prod_price
from products
where vend_id in('DLL01','BRS01' );
否定其后所跟的任何条件。
select prod_name
from products
where not vend_id='DLL01'
order by prod_name;
#匹配非DLL01之外的所有
等价于
select prod_name
from products
where vend_id<>’DLL01’
order by prod_name;
NOT的优点:在复杂子句或者与IN联合使用的时候,操作非常简单。
通配符搜索只能用于文本字段(字符串),不能够用于非文本数据类型。
表示任何字符出现任意次数。不管所在位置是什么字符。
select prod_id,prod_name
from products
where prod_name like 'Fish%';
可以使用多个通配符,%bean bag%表示匹配任何位置上包含文本bean bag的值,不论之前或者之后出现什么值。
select prod_id,prod_name
from products
where prod_name like '%bean bag%';
可以放在中间:
select prod_id,prod_name
from products
where prod_name like 'F%y';
考虑到如果y后面有空格则不是以y结尾的,所以不会被匹配到,所以可以改进,写成: ‘F%y%’
单独使用’%’也不能匹配NULL
作用与%相同,但是只匹配单个字符,而不是多个字符。
select prod_id,prod_name
from products
where prod_name like '_ inch teddy bear';
上述语句只匹配一个字符,如果改成’__ inch teddy bear’则可以匹配两个字符。改成’%inch teddy bear’则都可以匹配。
指定一个字符集,必须匹配通配符位置的一个字符,可以使用 ^(脱字符)来否定'[^JM]%'
表示匹配J和M之外的任一字符开始的人名
如果需要直接从数据库中检索出转换、计算过或者格式化的数据而不是检查出数据然后再在客户端程序中重新格式化,这时候需要计算字段。计算字段并不实际存在于数据库表中,而是 运行在select语句内创建的。
列与字段:基本等价,但是通常说法:实际表列、计算字段
+或者||,MySQL中必须使用函数。
select vend_name+'(' + vend_country + ')'
from vendors
MySQL中使用concat函数
select concat (vend_name,'(' ,vend_country,')')
from vendors
有的数据库会在组成计算字段的两列之间用空格填充,去掉这些空格可以使用RTRIM()函数。select RTRIM (vend_name)+'(' +RTRIM(vend_country)+')'
RTRIM()函数去掉值右边的所有空格,MySQL中使用RTRIM:
select concat (RTRIM(vend_name),'(' ,RTRIM(vend_country),')')
from vendors
新计算的列的别名:AS关键字
MySQL中使用别名:
select concat (vend_name,’(’ ,vend_country,’)’) AS vend_title
from vendors
不使用别名的话就是未命名的列,不能被客户端引用。
select prod_id,quantity,item_price,
quantity*item_price AS expanded_price
from orderitems
where order_num=20008;
显示结果为4列
测试计算使用select,不接from子句
- select 3*2 返回6
- select Trim( ’ abc ‘) 返回abc
- select Now() 返回当前日期和时间
PS:
1、TRIM ( [ [位置] [要移除的字串] FROM ] 字串): [位置] 的可能值为 LEADING (起头), TRAILING (结尾), or BOTH (起头及结尾)。 这个函数将把 [要移除的字串] 从字串的起头、结尾,或是起头及结尾移除。如果我们没有列出 [要移除的字串] 是什么的话,那空白就会被移除。
2、LTRIM(字串): 将所有字串起头的空白移除。
3、RTRIM(字串): 将所有字串结尾的空白移除。
几乎所有DBMS都同等支持一些SQL语句,但是每一个DBMS都有特定的函数。只有少数几个函数被所有主要的DBMS同等支持。
下面三个函数在MySQL中的实现:
- 提取字符串的组成部分 SUBSTRING()
- 数据类型转换 CONVERT()
- 取当前日期 CURDATE()
upper()函数将文本转换为大写
select vend_name,upper(vend_name) as vend_name_upper
from vendors
soundex()函数匹配发音类似的值:
select cust_name,cust_contact
from customers
where soundex(cust_contact)=soundex('michael green')
MySQL使用year()函数从日期中提取出年份
select order_num
from orders
where year(order_date);
在各种DBMS中,通常较为统一。
利用SQL的聚集函数汇总表的数据
聚集函数在各种主要SQL实现中较一致
- AVG() 返回某列的平均值,忽略列值为NULL的行
select avg(prod_price) as avg_price
from products
也可以进行筛选,如计算特定供应商商品的平均价格:
select avg(prod_price) as avg_price
from products
where vend_id='DLL01';
COUNT() 返回某列的行数,可以利用count()确定表中行的数目或符合特定条件的行的数目
用法1:使用count(*)对表中行的数目进行技术,不管表列中包含的是NULL还是非空值。
#对所有行计数得到5
select count(*) as num_cust
from customers;
用法2:使用count(column)对特定列中具有值的行进行计数,忽略NULL值。
#只对具有邮件地址的客户计数
select count(cust_email) as num_cust
from customers;
MAX() 返回某列的最大值,要求指定列名:
select max(prod_price) as max_price
from products;
注意:用于文本数据时,max()返回按该列排序后的最后一行。
SUM() 返回某列值之和
select sum(quantity) as items_ordered
from orderitems
where order_num = 20005;
sum()也可以用来合计计算值:
select sum(item_price*quantity) as total_price
from orderitems
where order_num = 20005;
聚集函数的两种用法:
- 指定all参数或者不指定all参数,都是对所有行执行计算(因为all是默认行为)不指定distinct即假定为all。
- 指定distinct参数,只包含不同的值。
只考虑对不同价格进行求平均值:
select avg(distinct prod_price) as avg_price
from products
where vend_id='DLL01';
distinct必须使用列名,不能用于count(*)
select语句可包含多个聚集函数:
select count(*) as num_items,
min(prod_price) as price_min,
max(prod_price) as price_max,
avg(prod_price) as price_avg
from products;
#单条select语句执行4个聚集运算,返回4个值。
分组数据以便汇总表内容的子集。
涉及两个select语句子集:
返回供应商DLL01提供的产品数目:
select
count(*) as num_prods
from
products
where
vend_id = 'DLL01';
按照供应商分组,分别计算每个供应商的产品数目:
select
vend_id, count(*) as num_prods
from
products
group by vend_id;
**1、**group by子句可以包含任意数目的列,所以可以进行嵌套。
**2、**group by子句中的每一列都必须是检索列或者有效的表达式(但不能是聚集函数)。如果在select中使用表达式,则必须在group by子句中指定相同的表达式。不能使用别名。
3、除聚集计算语句之外,select的所有列都必须在group by子句中给出。
4、如果分组列中包含有NULL值的行,则NULL将作为一个分组返回。如果有多个包含NULL值的行将被分为一组。
group by分组数据,having分组过滤。
where和having的差别:
where在数据分组前进行过滤,而having在数据分组后进行过滤。where排除的行不包括在分组中,所以会影响having子句中基于这些值过滤掉的分组。
select
cust_id, count(*) as orders
from
orders
group by cust_id
having count(*) >= 2; -- 过滤count(*)>=2的那些分组,该过滤是基于分组聚集值而不是特定行的值,所以此处使用where子句不起作用。
同时使用where和group by:
select
vend_id, count(*) as num_prods
from
products
where
prod_price >= 4
group by vend_id
having count(*) >= 2;
order by和group by
- oder by对产生的输出排序,group by对行进行分组,但是输出的顺序可能不是分组的顺序。
- order by任意列都可以使用(甚至没有被选择的列也可以使用)。group by只能使用选择的列或表达式列(必须是每个选择列表达式)。
- 要进行排序的话,order by不一定需要(默认升序),但是group by如果是与聚集函数一起使用,则必须使用。
- 一般在使用group by子句时,应该也给出order by子句。不能仅依赖group by排序数据。
select
order_num, count(*) as items -- count(*)检索出行数
from
orderitems
group by order_num
having count(*) >= 3;
如果需要按照订购物品的数目排序输出,需要添加order by子句:
select
order_num, count(*) as items
from
orderitems
group by order_num
having count(*) >= 3
order by items , order_num;
select -> from -> where -> group by -> having -> order by
简单查询:从单个数据库表中检索数据的单条语句。
子查询:嵌套在其他查询中的查询。
子查询长用于where子句的in操作符中,以及用来填充计算列。
示例:列出订购物品RGAN01的所有顾客,步骤如下:
1、检索出包含物品RGAN01的所有订单的编号;
2、检索具有前一步骤列出的订单编号的所有顾客的ID;
3、检索前一步返回的所有顾客ID的顾客信息。
方法1:每个步骤使用一个单独的查询:
step1:
select order_num
from orderitems
where prod_id='RGAN01' #输出20007,20008
step2:
select cust_id
from orders
where order_num in (20007,20008); #输出
结合以上两个步骤的查询,把第一个查询变为子查询。可以写成:
select cust_id
from orders
where order_num in (select order_num
from orderitems
where prod_id='RGAN01');
在select语句中,子查询总是从内向外处理。
step3:以上两个两种方法得到了RGAN01的所有顾客的ID,下一步是检索这些顾客的顾客信息:
select cust_name,cust_contact
from customers
where cust_id in('1000000004','1000000005');
将step3作为子查询,而不是硬编码顾客的ID,以上三条查询语句可以写作:
select cust_name,cust_contact
from customers
where cust_id in(select cust_id
from orders
where order_num in(select order_num
from orderitems
where prod_id='RGAN01'));
注意:作为子查询的select语句只能查询单个列,检索多个列将返回错误。并且由于性能限制,不能千套太多子查询。
示例:显示customers表中的每个顾客的订单总数。
首先,对顾客1000000001的订单进行计数:
select count(*) as orders
from orders
where cust_id='1000000001';
要对每个顾客执行count(*),需要将以上查询作为一个子查询:
select cust_name,cust_state,
(select count(*)
from orders
where orders.cust_id =customers.cust_id) as orders
from customers
order by cust_name;
以上语句对customers表中每一个顾客返回散列:cust_name、cust_state、orders。其中orders是一个计算字段,由圆括号中的子查询建立。该子查询对检索出的每一个顾客执行一次。
以上语句中orders.cust_id =customers.cust_id
使用完全限定列名(指定表名和列名)。
select vend_name,prod_name,prod_price
from vendors,products
where vendors.vend_id=products.vend_id;
#如果没有where子句,=等表关系返回的结果是笛卡尔积。检索出的行的数目是第一个表的行数和第二个表行数的乘积。如下:
select vend_name,prod_name,prod_price
from vendors,products
#返回笛卡尔积的联结也成叉联结
上例中使用的等值联结(基于两个表之间的相等测试)也成为内联结。
对等值联结(内联结)明确指定联结类型,如下:
select vend_name,prod_name,prod_price
from vendors INNER JOIN products
on vendors.vend_id=products.vend_id
#与上例语句等价
select prod_name,vend_name,prod_price,quantity
from orderitems,products,vendors
where products.vend_id=vendors.vend_id
and orderitems.prod_id=products.prod_id
and order_num=20007;
DBMS在运行时关联指定个的每个表,已处理联结。但是出于性能考虑,不该联结不必要的表,否则性能下降严重。
返回订购产品RGAN01的顾客:
方法一:子查询
select
cust_name, cust_contact
from
customers
where
cust_id in (select
cust_id
from
orders
where
order_num in (select
order_num
from
orderitems
where
prod_id = 'RGAN01'));
方法二:使用两个联结
select
cust_name, cust_contact
from
customers,
orders,
orderitems
where
customers.cust_id = orders.cust_id
and orderitems.order_num = orders.order_num
and prod_id = 'RGAN01';
#三个where子句,前两个关联联结的表,后一个过滤产品RGAN01的数据
MySQL中对列名和计算字段使用别名:
select concat(vend_name,'(',vend_country,')')as vend_title
from vendors
order by vend_name
此外,还可以给表名起别名:
select
cust_name, cust_contact
from
customers as c,
orders as o,
orderitems as oi
where
c.cust_id = o.cust_id
and oi.order_num = o.order_num
and prod_id = 'RGAN01'
注意:表别名只在查询执行中使用。列别名可以返回到客户端但是表别名不会返回。
表别名的优点:
方法一:使用子查询
select
cust_id, cust_name, cust_contact
from
customers
where
cust_name = (select
cust_name
from
customers
where
cust_contact = 'Jim Jones');
方法二:使用自联结
select
c1.cust_id, c1.cust_name, c1.cust_contact
from
customers as c1,
customers as c2
where
c1.cust_name = c2.cust_name
and c2.cust_contact = 'Jim Jones';
#where首先联结两个表,然后过滤数据
#该例中,在from子句中,同一个表出现了两次,虽然合法,但是为了消除歧义(DBMS不知道引用的是哪个customer表),必须使用表别名。
自联结的速度快于子查询,所以应选用自联结而不用子查询。
自联结通常作为外部语句来替代从相同表中检索数据使用的子查询语句。
标准的联结返回所有数据,相同的列甚至多次出现。
自然联结排除多次出现,使每一列只返回一次。这要由使用者自己完成:对一个表使用通配符,而对其他表的列使用明确的子集来完成。
通配符只对第一个表使用,所有其他列都明确列出,所以没有重复的列被检索出来。
select
c . *,
o.order_num,
o.order_date,
oi.prod_id,
oi.quantity,
oi.item_price
from
customers as c,
orders as o,
orderitems as oi
where
c.cust_id = o.cust_id
and oi.order_num = o.order_num
and prod_id = 'RGAN01';
- 外联结(outer join)
left outer join
right outer join
full outer join
内联结检索所有顾客及订单:
select
customers.cust_id, orders.order_num
from
customers
inner join
orders ON customers.cust_id = orders.cust_id
要检索包括没有订单的顾客在内的顾客,可以使用外联结:
select
customers.cust_id, orders.order_num
from
customers
left outer join
orders ON customers.cust_id = orders.cust_id
#会输出包括order_num为NULL值的行
left outer join 从左边的表中选择所有行,如果要从右边的表中选择所有行,应该使用right outer join。
左外联结和右外联结唯一的差别是关联的表的顺序。
MySQL不支持full outer join 语法,可以使用union:
select
customers.cust_id, orders.order_num
from
customers
left outer join
orders ON customers.cust_id = orders.cust_id
union select
customers.cust_id, orders.order_num
from
customers
right outer join
orders ON customers.cust_id = orders.cust_id
使用聚集函数(和内联结)检索所有顾客以及每个顾客的订单数:
select
customers.cust_id, count(orders.order_num) as num_ord
from
customers
inner join
orders ON customers.cust_id = orders.cust_id
group by customers.cust_id
聚集函数也可以与其他联结一起使用
总结:应该总是提供联结条件,否则会得出笛卡尔积。
利用union操作符将多条select语句组合成一个结果集。成为并/复合查询。
什么情况下需要组合查询:
- 在一个查询中从不同的表返回结构数据
- 对一个表进行多个查询,按照一个查询返回数据
查询Illinois、Indiana、Michigan三个州的所有顾客的报表,还要包括不管哪个州的Fun4all:
方法一:使用union
select
cust_name, cust_contact, cust_email
from
customers
where
cust_state in ('Il' , 'IN', 'mi')
union select
cust_name, cust_contact, cust_email
from
customers
where
cust_name = 'FUN4all'
方法二:使用where子句
select
cust_name, cust_contact, cust_email
from
customers
where
cust_state in ('Il' , 'IN', 'MI')
or cust_name = 'Fun4all'
在简单的例子中,使用union可能比使用where子句更为复杂。但是在较复杂的过滤条件,或者从多个表汇总检索数据的时候使用union会使处理更为简单。
注意:使用union的规则:
使用union时结果会自动删除重复行,若要保留或者返回所有匹配行(包括重复行)需要使用union all
union几乎总是完成与多个where条件相同的工作,但是当使用union all来显示所有的匹配行的时候,where做不到这一点。
对union查询结果排序,只能使用一条order by语句,且必须位于最后一条select语句后面。
select
cust_name, cust_contact, cust_email
from
customers
where
cust_state in ('Il' , 'IN', 'mi')
union select
cust_name, cust_contact, cust_email
from
customers
where
cust_name = 'FUN4all'
order by cust_name,cust_contact;
insert:将行插入(添加)数据库表。
方法一:不指定行名,但是对于输入的值的顺序要求较高,且可伸缩性差
insert into customers -- into关键字是可选的但是最好跟上。
values('1000000006','toy land','123 any street','new york','ny','11111','usa',NULL,NULL);
方法二:指定列名
insert into customers(cust_id,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email)
values('1000000006','toy land','123 any street','new york','ny','11111','usa',NULL,NULL);
因为表的结构和列的次序很可能会发生变化,所以加上列名总是能确保正确插入值。
不要使用没有明确给出列的insert语句。
使用指定列名的插入方法,可以省略列,表示只给某些列提供值,其他列不提供值。
delete from customers
where
cust_id = '1000000006';
insert into customers(cust_id,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
values('1000000006','toy land','123 any street','new york','ny','11111','usa');
省略某些列的条件:
- 该列定义允许为NULL值(无值或者空值)
- 在表定义中给出默认值。表示如果不给出值将使用默认值。
利用insert将select语句的结果插入到表中,即insert select。
示例:想把另一个表中的顾客合并到customers表中,不需要每次读取一行再insert,而是可以用insert select:
#从一个名为custNew的表中读取所有数据并插入到customers表。
insert into customers(cust_id,cust_contact,cust_email,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
select cust_id,cust_contact,cust_email,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country
from custNew;
insert和select 中的列名不要求匹配,DBMS不关心select语句返回的列名,它使用的是列的位置。按照select中列的位置顺序填充到customers中。
insert通常只插入一行,如果要插入多行,必须执行多个insert语句。insert select语句除外,它可以用一条insert插入多行,不管insert语句返回多少行都将被insert插入。
将一个表的内容复制到一个全新的表(运行中创建的表),可以使用select into语句。
select *
into custCopy
from customers;
MySQL中使用的语法:
create table custCopy as
select * from customers;
以上select语句创建一个名为custCopy的新表,并把customers表的全部内容复制到新表中。
如果只想赋值部分列,可以明确给出列名,而不是使用*通配符。
使用select into注意:
- 任何select选项和子句偶读可使用,包括where和group by
- 可利用联结从多个表中插入数据
- 不管从多少个表中检索数据,数据都只能插入到一个表中。
使用update和delete
基本update语句包含三部分:
- 要更新的表
- 列名和他们的新值
- 确定要更新哪些行的过滤条件
实例:客户100000005有了新的电子邮件地址,所以他的记录需要更新:
update customers
set cust_email='[email protected]'
where cust_id='100000005';
#尤其注意where子句,如果没有where子句,DBMS将会用这个邮件地址更新customers表中所有的行。
更新多个列的语法:
update customers
set cust_contact='Sam Robets',
cust_email='[email protected]'
where cust_id='100000005';
要删除某个列的值,可以设置它为NULL:
update customers
set cust_email=NULL
where cust_id='1000000006'
从customers表中删除一行数据:
delete from customers
where cust_id='1000000006'
友好的外键:使用外键确保引用完整性可以防止删除某个关系需要用到的行。
delete不需要行名或者通配符,delete删除的是整行而不是删除列。要删除指定的列需要使用update语句。
truncate table删除表中的所有行,比使用delete速度更快,因为它不记录数据的变动。
使用update或者delete的时候一定要注意where字句。除非要更新或者删除每一行,否则不能使用不带where的update或delete语句。
使用update或delete语句之前应该用select进行测试。
使用强制实施引用完整性的数据库,这样DBMS将不允许删除其数据与其他表相关联的行。
create table products_1
(
prod_id char(10) not null,
vend_id char(10) not null,
prod_name char(254) not null,
prod_price decimal(8,2) not null,
prod_desc varchar(1000) null
);
各列之间逗号分隔,每列以列名开始(列名在表中必须是唯一的),后跟列的数据类型。
对于不同的DBMS必须编写不同的表创建脚本。
允许NULL值得列也允许在插入行的时候不给出该行的值,但是不允许NULL值的列在插入或者更新行的时候,该列必须有值。NULL为默认设置,如果没有指定not NULL,就是NULL。
主键和NULL:只有不允许NULL的值可以作为主键,允许NULL值的列不能作为唯一标识。
create table orderitems_1
(
order_num integer not null,
order_item integer not null,
prod_id char(10) not null,
quantity integer not null default 1,
item_price decimal(8,2) not null
);
默认值经常用于日期或者时间戳列,例如通过指定引用系统日期的函数,将系统日期作为默认日期。MySQL中是指定current_date()
使用alter table更改表结构,必须给出的信息:
- 在alter table之后给出要更改的表名(必须是存在的表)
- 列出要做哪些更改
实例:给vendors表增加一个名为vend_phone的列,数据类型为char。
给已有的表增加列可能是所有DBMS都支持的唯一操作。
alter table vendors
add vend_phone char(20);
使用alter table需要小心,在改动之前要做好完整的备份(表结构和数据)。数据库表的更改不能撤销。
drop table orderitems_1
应该使用关系规则防止意外删除整表。
需要指定旧表名和新表名,但是各个DBMS相差很大。
MySQL使用rename.
视图是虚拟的表,包含的不是数据而是根据需要检索数据的查询。视图提供了一种封装select语句的层次,可以简化数据处理,重新格式化或保护基础数据。
实例:使用3个select语句检索订购了某种产品的顾客:
select cust_name,cust_contact
from customers,orders,orderitems
where customers.cust_id=orders.cust_id
and orderitems.order_num=orders.order_num
and prod_id='RGAN01'
使用视图:(如果已经将整个查询包装成一个名为productCustomers的虚拟表,则可以如下轻松检索相同数据):
select cust_name,cust_contact
from productcustomers
where prod_id='RGAN01'
productCustomers是一个视图,它不包含任何列或数据,包含的是一个查询。
所有DBMS对于视图创建语法的支持都比较一致。
利用视图简化复杂的联结:
上一节使用视图中,创建视图的步骤如下:
step1:
create view productcustomers as
select
cust_name, cust_contact, prod_id
from
customers,
orders,
orderitems
where
customers.cust_id = orders.cust_id
and orderitems.order_num = orders.order_num;
这条语句创建一个名为productcustomers的视图,联结三个表,返回已经订购了任意产品的所有顾客的列表。
step2:接下来可以使用视图来隐藏上述联结,简化查询:
select
cust_name, cust_contact
from
productcustomers
where
prod_id = 'RGAN01'
利用视图重新格式化检索出的数据:
SQL中很多DBMS使用+或者||表示拼接:
select RTRIM(vend_name)+'('+RTRIM(vend_country)+')'
as vend_title
from vendors
order by vend_name
MySQL中使用函数concat:
select concat(vend_name,'(',vend_country)
as vend_title
from vendors
order by vend_name
如果经常要使用这种格式的结果,不需要每次都执行这种拼接。创建一个视图就可做到:
create view vendorLocations as
select
concat(vend_name, '(', vend_country) as vend_title
from
vendors
order by vend_name
接下来查询可以写作:
select *
from vendorLocations
因为视图只包含一个select语句,而select语句的雨大必须遵循各个DBMS的所有规则和约束,所以创建视图的版本有很多。
用视图过滤不想要的数据:
视图对于普通where子句也有用,可以定义customerEmailList视图,过滤没有电子邮件地址的顾客:
step 1 创建视图:
create view customerEmailList as
select
cust_id, cust_name, cust_email
from
customers
where
cust_email is not null;
step2:
select *
from customerEmailList
检索数据时,在视图中的where子句和传递给视图的where子句将自动组合
使用视图与计算字段:
视图作用4:简化计算字段
实例:检索某个订单中的物品,并且计算每种物品的总价格:
select
prod_id,
quantity,
item_price,
quantity * item_price as expanded_price
from
orderitems
where
order_num = 20008;
step 1 转换为视图:
create view orderitemsExpanded as
select
order_num,
prod_id,
quantity,
item_price,
quantity * item_price as expanded_price
from
orderitems
step 2 使用视图检索:
select *
from orderitemsExpanded
where order_num=20008
以上两步骤中注意order_num(where子句)
Oracle和SQLserver语法中的存储过程:
执行一个名为addNewProduct的存储过程,将一个新产品添加到products表中:
excute addNewProduct('JTS01',Stuffed Effiel Tower',6.49,'Plush stuffed toy with the text La Tour Effiel in red white and blue');
除了传递给存储过程的四个参数以外,products表还有一个prod_id(主键)需要值。但是此ID要保证正确生成应该使生成此ID的过程自动化而不是依赖于用户的输入。
以上存储过程完成的工作基本形式:
- 验证传递的数据,保证所有4个参数都有值
- 生成用作主键的唯一ID
- 将新产品插入products表,在合适的列中存储生成的主键和传递的数据。
各个DBMS可能有各自不同的执行选择
各个DBMS不同
MySQL:
略
事务处理是一种机制,用来管理必须成批执行的SQL操作,保证数据库不包含不完整的操作任务。(确保成批的SQL操作要么完全执行要么完全不执行,维护数据库的完整性)。
事务处理术语:
- 事务(transaction):指一组SQL语句(要么完全执行要么完全不执行)
- 回退(rollback):指撤销指定SQL语句的过程
- 提交(commit):指将未存储的SQL语句结果写入数据库
- 保留点(savepoint):指事务处理中设置的临时占位符(placeholder),可以对它发布回退(与回退整个事务处理不同)
哪些语句可以回退:
事务处理用来管理insert、update和delete语句,不能回退select语句(也没必要),也不能回退create或者drop操作。事务处理过程中可以使用这些语句,但是回退过程中这些操作不能撤销。
MySQL中事务开始
start transaction
使用rollback:
delete from orders;
rollback;
delete语句可以回退,说明insert、update和delete不是最终的结果。
使用commit:
一般SQL语句是针对数据库表直接执行和编写的,这就是所谓的隐式提交,也就是说提交(写或保存)时自动进行的。
事务处理过程中,提交不会隐式进行。进行明确的提交,需要使用commit语句。
SQL server中一个例子:
begin transaction
delete orderitems where order_num=12345
delete orders where order_num=12345
commit transaction
上例设计两个数据库表,所以使用事务处理块来保证订单不会被部分删除。最后的commit语句仅在不出错时写出更改。
使用保留点:
简单事务:使用简单的rollback和commit语句可以写入或者撤销整个事务
复杂事务:需要部分提交或回退,所以需要在事务处理块中合适位置放置占位符。回退的时候可以回退到某个占位符。也就是保留点。
MySQL中使用保留点(savepoint)
SQL检索操作返回一组称为结果集的行,这组返回的行都是与SQL语句匹配的行(0行或者多行)。简单使用SQL语句没法得到第一行、下一行或者前10行。
当需要在检索出来的行中前进或后退一行或多行的时候就需要游标。
游标是一个存储在DBMS服务器上的数据库查询不是一条select语句,而是被该语句检索出来的结果集。在存储游标之后,应用程序可以根据需要滚动或者浏览其中的数据。
游标主要用于交互式应用。
MySQL中创建游标实例:创建一个游标来检索没有电子邮件地址的所有顾客,作为应用程序的组成部分,帮助操作人员找出空缺的电子邮件地址。
declare custcursor CURSOR #declare语句创建和命名游标custcursor
for
select * from customers #select语句定义一个包含没有电子邮件地址的所有顾客的游标
where cust_email is nll
open cursor custCursor
处理open cursor语句时,执行查询,存储减速哦出的数据以供浏览和滚动。
fetch语句访问游标数据。fetch指出要检索哪些行,从何处检索他们以及将他们放在何处。
实例:Oracle语法从游标中检索出第一行:
declare type custcursor is ref cursor
return customers%rowtype;
declare custrecord customers%rowtype
begin
open custcursor;
fetch custcursor into custrecord;
close custcursor;
end;
上例中,fetch用来检索当前行,(自动从第一行开始)放到声明的变量custrecord中,对于检索出来的数据不做任何处理。
游标在使用完毕时要进行关闭。MySQL等DBMS要求明确释放游标占用的资源
MySQL中关闭游标:
close custCursor
deallocate cursor custCursor
一旦游标关闭,如果不再次打开将不能使用。第二次使用时不需要声明,只要open打开即可。
表中任意列只要满足以下条件就可以作为主键:
- 任意两行的主键值都不相同
- 每行都有一个主键值(列中不允许NULL值)
- 包含主键值的列从不修改或者更新
- 主键值不能重用。如果从表中删除某一行,主键值不分配给新行。
定义主键的方法:
方法一、创建主键
create table vendors_1
(
vend_id char(10) not null primary key,
vend_name char(10) not null,
vend_address char(50) null,
vend_city char(50) null,
vend_state char(5) null,
vend_zip char(10) null,
vend_country char(50) null
);
方法二、使用约束(constraint)
alter table vendors_1
add constraint primary key(vend_id);
外键是表中的一列,其值必须列在另一表的主键中。例如在orders表的顾客ID列上定义了一个外键,则该列只能接受customers表的主键值。
定义外键的方法:
create table orders_1 (
order_num integer not null primary key,
order_date datetime not null,
cust_id char(10) not null references customers (cust_id)
);
关键字reference表示cust_id中的任何值都必须是customers表的cust_id中的值。
在alter table中使用constraint语法可以实现相同的功能。
alter table orders_1
add constraint
foreign key(cust_id) references customers(cust_id)
外键的作用:
保证一列或一组列中的数据是唯一的。类似于外键,但是唯一约束和外键的区别如下:
唯一约束既可以用unique关键字在表定义中定义,也可以用单独的constrain定义。
用来保证一列(或一组列)中的数据满足一组指定的条件。常见用途:
- 检查最小或最大值
- 指定范围
- 只允许特定的值
实例:对orderitems表施加检查约束,保证所有物品的数量大于0:
create table orderitems
(
order_num integer not null,
order_item integer not null,
prod_id char(10) not null,
quantity integer not null check(quantity>0),
item_price money not null
);
利用这个约束,任何插入(或更新)的行都会被检查,保证quantity大于0
又如:add constraint check(gender like'[MF]')
用来排序数据以加快搜索和排序操作的速度。
主键数据总是排序的,所以按照主键搜索特定行是一种快速有效的操作。
搜索其他列的值通常效率不高,解决办法是使用索引。可以在一个或多个列上定义索引,使DBMS保存其内容的一个排过序的列表。
create index prod_name_id
on products(prod_name); #在products表的产品名 列上创建一个简单索引
索引必须唯一命名。
是特殊的存储过程,在特定的数据库活动发生时自动执行。可以与特定表上的insert、update和delete操作相关联。
存储过程与触发器:
存储过程只是简单的存储SQL语句,触发器与单个的表关联。
触发器内的代码具有以下数据的访问权:
触发器的常见用途:
- 保证数据一致
- 基于某个表的变动在其他表上执行活动
- 进行额外的验证并根据需要回退数据
- 计算计算列的值或更新时间戳
实例:创建一个触发器,对所有insert和update操作,将customers表中的cust_state列都转换为大写:
Oracle版本:
create trigger customer_state
after insert or update
for each row
begin
update customers
set cust_state=upper(cust_state)
where customers.cust_id=:old.cust_id
end;
一般榆树的处理比触发器快。所以应尽可能使用约束。
任何安全系统的基础都是用户授权和身份确认
安全性使用SQL的grant和revoke语句管理。大多数DBMS使用交互式的管理应用程序,这些实用程序在内部使用grant和revoke语句。