SQL必知必会笔记手册

SQL语句不区分大小写,空格都被忽略

行与列

row 行
column 列

检索数据

检索单个列

select prod_name
from Products;

检索多个列

select prod_id,prod_name,prod_price
from products;

检索所有列

select * from products;

检索不同值(只显示不同的):使用distinct关键字

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的扩展。

范围值检查 between关键字 between * and *

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和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运算符

in是where子句中用来指定要匹配值清单的关键字,功能与OR相当。但是有很多优点:

  • 更清楚直观
  • 与AND和OR运算符组合使用时,求值顺序更易管理。
  • 比一组OR执行地快
  • 可以包含其他select语句(嵌套查询)

    select prod_name,prod_price
    from products
    where vend_id in('DLL01','BRS01' );
    

NOT操作符

否定其后所跟的任何条件。

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联合使用的时候,操作非常简单。

用通配符进行过滤

LIKE操作符

  • 通配符:用来匹配值的一部分的特殊字符
  • 搜索模式:由字面值、通配符或者两者组合构成的搜索条件
  • 谓词:取值为TRUE、FALSE或者UNKNOWN的表达式,包括IN、BETWEEN以及LIKE等。

通配符搜索只能用于文本字段(字符串),不能够用于非文本数据类型。

%通配符

表示任何字符出现任意次数。不管所在位置是什么字符。

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()返回按该列排序后的最后一行。

  • MIN()返回某列的最小值,功能与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语句子集:

  • group by子句
  • having 子句

返回供应商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过滤分组。使用having的时候应该结合group by子句。
  • 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子句顺序:

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'

注意:表别名只在查询执行中使用。列别名可以返回到客户端但是表别名不会返回。

表别名的优点:

  • 缩短SQL语句
  • 允许在一条select语句中多次使用相同的表

使用不同类型的联结

  • 等值连接/内联结(inner join)
  • 自联结(self_join)
    实例1:找出Jim Jones工作的公司,然后找出在该公司工作的顾客。

方法一:使用子查询

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表),必须使用表别名。

自联结的速度快于子查询,所以应选用自联结而不用子查询。

自联结通常作为外部语句来替代从相同表中检索数据使用的子查询语句。

  • 自然联结(natural join)

标准的联结返回所有数据,相同的列甚至多次出现。

自然联结排除多次出现,使每一列只返回一次。这要由使用者自己完成:对一个表使用通配符,而对其他表的列使用明确的子集来完成。

通配符只对第一个表使用,所有其他列都明确列出,所以没有重复的列被检索出来。

    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的规则:

  • 必须由两条或者两条以上的select语句组成。语句之间用union
  • union组合的每个查询必须包含相同的列、表达式或者聚集函数。(但是不需要以相同的次序列出)。
  • 列数据类型必须兼容。类型不必完全相同但必须是DBMS可以隐含转换的类型。(例如不同的数值类型或者不同的日期类型)

使用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='kim@thetoystore.com'
where cust_id='100000005';
#尤其注意where子句,如果没有where子句,DBMS将会用这个邮件地址更新customers表中所有的行。

更新多个列的语法:

update customers
set cust_contact='Sam Robets',
    cust_email='kim@thetoystore.com'
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值的列在插入或者更新行的时候,该列必须有值。NULL为默认设置,如果没有指定not NULL,就是NULL。

主键和NULL:只有不允许NULL的值可以作为主键,允许NULL值的列不能作为唯一标识。

default指定默认值

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更改表结构,必须给出的信息:
- 在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对于视图创建语法的支持都比较一致。

创建视图:create view

利用视图简化复杂的联结:

上一节使用视图中,创建视图的步骤如下:

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语法中的存储过程:

执行存储过程 execute

执行一个名为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:

管理事务处理:commit 、rollback

事务处理是一种机制,用来管理必须成批执行的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打开即可。

高级SQL特性

约束(constraint)

主键(primary key)

表中任意列只要满足以下条件就可以作为主键:
- 任意两行的主键值都不相同
- 每行都有一个主键值(列中不允许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)

外键的作用:

  • 保证引用完整性
  • 防止以外删除

唯一约束

保证一列或一组列中的数据是唯一的。类似于外键,但是唯一约束和外键的区别如下:

  • 表可以有多个唯一约束,但每个表只允许一个外键
  • 唯一约束列可包含NULL值
  • 唯一约束列可修改或更新
  • 唯一约束列的值可重复使用
  • 主键可以用来定义外键,但是唯一约束列不可以。

唯一约束既可以用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操作中的所有新数据和旧数据
  • delete操作中删除的数据

触发器的常见用途:
- 保证数据一致
- 基于某个表的变动在其他表上执行活动
- 进行额外的验证并根据需要回退数据
- 计算计算列的值或更新时间戳

实例:创建一个触发器,对所有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语句。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值