sql必知必会读书笔记

第三课: 排序检索数据

  1. distinct关键字:distinct 列名1,列名2,列名3
    1. DISTINCT 关键字会作用于每一列,然后取最大的那一个返回;
  2. order by使用方法
    1. order by子句只能出现在select语句中最后一条子句,否则报错;
    2. 多个列排序时:order by 列名1,列名2,仅在多行具有相同的列名1时,才会按照列名2进行排序,如果每个列名1都是唯一的,则不会按列名2进行排序;
    3. select 列名1,列名2,列名3 from table order by 2,3:先按照列名2进行排序,再按照列名3进行排序(列名2的相对位置是2,列名3的相对位置是3)
    4. descasc用法:desc/asc只能作用于一列,如果想在多个列上进行排序,,需要对每一列指定desc/asc关键字。

第四课:使用where子句

4.2 where子句操作符

操作符说明举例
=等于
<>不等于
!=不等于
<小于
<=小于等于
!<不小于
>大于
>=大于等于
!>不大于
BETWEEN…AND…在指定的两个值之间WHERE price BETWEEN 5 AND 10;
IS NULL为null值WHERE name IS NULL;//name为空的记录
IS NOT NULL不为null值WHERE name IS NOT NULL //name不为空的记录

第五课 高级数据过滤

  1. and
  2. or:当一个where子句中有多个andor时,一定要加括号,因为and的优先级比or高;
  3. in:指定操作范围,相当于多个orin的优点:
    1. in的语法更加清楚,直观;
    2. 在与其他andor操作符组合使用in时,求值顺序更容易管理;
    3. in的效率高于多个or的组合;
    4. 最大优点:可以包含其他select语句,可以动态建立where子句。
  4. not :
    1. 功能:否定其后所跟的任何条件;
    2. 语法:not从不单独使用,总是和其他操作符一起使用;not关键字可以用在要过滤的列前,而不仅是在其后;
    3. 如何使用:****先写出条件表达式,然后在条件表达式前面(where后面)加not****
      //这两条语句等价
      SELECT * FROM t_project WHERE NOT Project_ID  IN (1,2,3,4,5,6);
      
      SELECT * FROM t_project WHERE Project_ID NOT IN (1,2,3,4,5,6);
      
      
    4. not 比!=的功能更加强大,尤其是在复杂的子句中,如上述和in的联合使用中。

第6课 用通配符进行过滤

通配符搜索只能作用于文本字段,非文本字段不能用通配符进行搜索;

6.1 “%”通配符

  1. %表示任何字符(0个,1个,多个字符)出现任意次数(0次,1次,多次);
  2. %可以在任何地方使用任何次;
  3. %不能匹配NULL;

6.2 “_”通配符

  1. _ 只匹配一个字符,因此有几个字符需要匹配就需要多个_;

6.3 “[]”通配符

  1. []通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符,但是并不是所有的DBMS都支持此通配符;
//找出所有名字以J或M起头的联系人
WHERE cust_contact LIKE '[JM]%' ORDER BY cust_contact;
//找出所有名字不以J或M起头的联系人
WHERE cust_contact LIKE '[^JM]%' ORDER BY cust_contact;

6.4 通配符的缺点及使用注意事项

  1. 缺点:通配符搜索比较耗时,性能低下;
  2. 注意事项:
    1. 不要过度使用通配符:如果其他操作符能达到相同的目的,应该使用其他操作符;
    2. 把通配符置于开始处,搜索起来是最慢的,在确实需要使用通配符时,也尽量不要把它们用在搜索模式的开始处;
    3. 仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据;

第7课 创建计算字段

  1. 计算字段并不是数据库中真实存在的字段,而是运行时,在select语句内创建的,用来接收数据库中多个字段计算后的结果。

7.2 拼接字段

将多个字段拼接成一个字段

从数据库中查询返回的字段可以来源于多张表,也可以是自己创建的计算字段。

SELECT CONCAT(Project_Caption,'(',Project_ID,")") 
AS project_title 
FROM t_project 
ORDER BY t_project.Project_ID; 
  1. 不同的数据库采用不一样的方式来拼接多个字段

|数据库类型|拼接方式|举例|
|—|:—-:|—-|—|
|AccessSQL Server|+|SELECT vend_name + ' (' + vend_country + ')'+FROM VendorsORDER BY vend_name;|
|DB2OraclePostgreSQLSQLiteOpen Office Base||||SELECT RTRIM(vend_name) || ' (' || RTRIM(vend_country) || ')'AS vend_title FROM Vendors ORDER BY vend_name;|
|MySQLMariaDB|concat(str...)|SELECT Concat(vend_name, ' (', vend_country, ')') AS vend_title FROM Vendors ORDER BY vend_name;|

  1. 去掉空格
名称作用举例
RTRIM切割字符串右边的空格RTRIM(vend_country)
LTRIM切割字符串左边的空格LTRIM(vend_country)
TRIM切割字符串左右两边的空格TRIM(vend_country)
  1. 使用别名(AS)

给计算字段取一个名字,方便以后的操作;

7.3 执行算术计算

对字段做加减乘除运算

SELECT prod_id,
quantity,
item_price,
quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;

7.4 测试计算

select 去掉from子句后就可以用来测试

SELECT 3 * 2;将返回6,SELECT Trim(‘ abc ‘);将返回abc

第8课 使用数据处理函数

8.1 函数以及带来的问题

  • 函数的作用:高效,方便。
  • 问题:每一个DBMS都有特定的函数,而且名称和语法可能不同;

8.2 使用函数

  1. 大多数SQL实现支持以下类型的函数。
    1. 处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数
      2.数值数据上进行 算术操作(如返回绝对值,进行代数运算)的数值函数
    2. 日期和时间值并从这些值中提取特定成分(如返回两个日期之差,检查日期有效性)的日期和时间函数。
    3. 返回DBMS正使用的特殊信息(如返回用户登录信息)的系统函数。
  2. 常用的文本处理函数
    函 数说 明
    LEFT()(或使用子字符串函数)返回字符串左边的字符
    LENGTH()(也使用DATALENGTH()或LEN())返回字符串的长度
    LOWER()(Access使用LCASE())将字符串转换为小写
    LTRIM()去掉字符串左边的空格
    RIGHT()(或使用子字符串函数)返回字符串右边的字符
    LTRIM()去掉字符串右边的空格
    SOUNDEX()返回字符串的SOUNDEX值
    UPPER()(Access使用UCASE())将字符串转换为大写
  3. 日期和时间处理函数
  4. 数值处理函数
    函 数说 明
    ABS()返回一个数的绝对值
    COS()返回一个角度的余弦
    EXP()返回一个数的指数值
    PI()返回圆周率
    SIN()返回一个角度的正弦
    SQRT()返回一个数的平方根
    TAN()返回一个角度的正切

第9课 汇总数据

9.1 聚集函数

函 数作用说明
AVG()返回某列的平均值
COUNT()返回某列的行数
MAX()返回某列的最大值返回最大的日期,数值,排序后列的最后一行
MIN()返回某列的最小值返回最小的日期,数值,排序后列的第一行
SUM()返回某列值之和

1. AVG()

  1. 特别说明

    1. 只能用于单个列;
    2. 而且必须给出列名;
    3. 多个列就必须使用多次avg();
    4. AVG()函数忽略列值为NULL的行;
  2. 举例说明

    SELECT
        AVG(
            DISTINCT t_project.Project_code
        ) AS project_code
    FROM
        t_project; 
    
    SELECT
        AVG(
            DISTINCT t_project.Project_code
        ) AS project_code,
        AVG(
            DISTINCT t_project.Project_ID
        ) AS project_Id
    FROM
        t_project;
    

2. COUNT()

  1. 特别说明
    1. 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值,但是不能用DISTINCT修饰,即COUNT(DISTINCT *)是违法的。
    2. 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值,可以用DISTINCT修饰,即COUNT(DISTINCT column)是合法的。

3. MAX()

  1. 特别说明
    1. 必须指定列名;
    2. 忽略列值为null的行
    3. 虽然MAX()一般用来找出最大的数值或日期值,但许多(并非所有)DBMS允许将它用来返回任意列中的最大值,包括返回文本列中的最值。在用于文本数据时,MAX()返回按该列排序后的最后一行。
SELECT MAX(prod_price) AS max_price
FROM Products;

4. MIN()

  1. 特别说明
    1. 必须指定列名;
    2. 忽略列值为null的行
    3. 虽然MIN()一般用来找出最小的数值或日期值,但许多(并非所有)DBMS允许将它用来返回任意列中的最小值,包括返回文本列中的最值。在用于文本数据时,MAX()返回按该列排序后的第一行。

5. SUM()

  1. SUM()函数忽略列值为NULL的行
SELECT
    SUM(3 * quantity) AS total_price
FROM
    OrderItems
WHERE
    order_num = 20005;

9.2 组合聚集函数

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;

第10课 分组数据

10.1 创建分组(GROUP BY)

  1. GROUP BY子句可以包含多个列名,可以对分组进行嵌套,更细致的进行数据分组;
  2. 如果在GROUP BY子句中嵌套了分组,数据将在最后指定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算(所以
    不能从个别的列取回数据)。
  3. GROUP BY子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在GROUP BY子句中指定相同的表达式。不能使用别名。
  4. 大多数SQL实现不允许GROUP BY列带有长度可变的数据类型(如文本或备注型字段)。
  5. 除聚集计算语句外,SELECT语句中的每一列都必须在GROUP BY子句中给出。
  6. 如果分组列中包含具有NULL值的行,则NULL将作为一个分组返回。如果列中有多行NULL值,它们将分为一组。
  7. GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
-- 先按照EPS_ID进行分组,然后在按照Status进行分组
SELECT
    t_project.EPS_ID,
    t_project.`Status`,
    COUNT(*)
FROM
    t_project
GROUP BY
    t_project.EPS_ID,
    t_project.`Status`;

10.2 过滤分组(HAVING)

  1. GROUP BY 创建分组,HAVING过滤分组,包括哪些分组,排除哪些分组。
  2. GROUP BYWHERE唯一的差别:
    1. WHERE过滤行,而HAVING过滤分组。
    2. 学过的有关WHERE的所有技术和选项都适用于HAVING。它们的句法是相同的,只是关键字有差别。

HAVING和WHERE的差别另一种理解方式:

WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。这是一个重要的区别,WHERE排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。

SELECT
    cust_id,
    COUNT(*) AS orders
FROM
    Orders
GROUP BY
    cust_id
HAVING
    COUNT(*) >= 2;

10.3 分组和排序

名称ORDERBYGROUP BY
作用对产生的输出排序对行分组,但输出可能不是分组的顺序
使用范围任意列都可以使用(甚至非选择的列也可以使用)只可能使用选择列或表达式列,而且必须使用每个选择列表达式
是否需要不一定需要如果与聚集函数一起使用列(或表达式),则必须使用

一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。

SELECT
    order_num,
    COUNT(*) AS items
FROM
    OrderItems
GROUP BY
    order_num
HAVING
    COUNT(*) >= 3
ORDER BY
    items,
    order_num;

10.4 SELECT子句顺序

子 句说 明是否必须使用
SELECT要返回的列或表达式
FROM从中检索数据的表仅在从表选择数据时使用
WHERE行级过滤
GROUP BY分组说明仅在按组计算聚集时使用
HAVING组级过滤
ORDER BY输出排序顺序

第11课 使用子查询

  1. 就是在select里面嵌套select,嵌套的select就是子查询;
  2. 查询执行的顺序:由内而外;
  3. 使用子查询效率低下;
  4. 使用场景:
    1. 子查询常用于WHERE子句的IN操作符中;
    2. 用来填充计算列。

11.1 利用子查询进行过滤

SELECT
    customers.cust_name,
    customers.cust_contact
FROM
    customers
WHERE
    cust_id IN (
        SELECT
            orders.cust_id
        FROM
            orders
        WHERE
            order_num IN (
                SELECT
                    orderitems.order_num
                FROM
                    orderitems
                WHERE
                    orderitems.prod_id = "RGAN01"
            );
);
  1. 作为子查询的SELECT语句只能查询单个列。企图检索多个列将返回错误。
  2. 使用子查询并不总是执行这类数据检索的最有效方法,效率低下。

11.2 作为计算字段使用子查询

SELECT
    customers.cust_name,
    customers.cust_state,
    (
        SELECT
            COUNT(*)
        FROM
            orders
        WHERE
            orders.cust_id = customers.cust_id
    ) AS orders
FROM
    customers
ORDER BY
    cust_name;

必须完全限定类名

第12课 联结表

12.1 联结

  1. 关系式数据库设计,关系表
    1. 方便维护;
    2. 防止冗余数据;

12.2 创建联结

  1. 如何创建链接(首先列出表,然后定义表之间的关系)
    1. 指定要链接的表;
    2. 指定这些表之间的关联方式;
SELECT
    vend_name,
    prod_name,
    prod_price
FROM
    vendors,
    products
WHERE
    vendors.vend_id = products.vend_id;
WHERE子句的重要性
  1. 在一条SELECT语句中联结几个表时,相应的关系是在运行中构造的;
  2. 在数据库表的定义中没有指示DBMS如何对表进行联结的内容,你必须自己做这件事情;
  3. 在联结两个表时,实际要做的是将第一个表中的每一行与第二个表中的每一行配对;
  4. WHERE子句作为过滤条件,只包含那些匹配给定条件(这里是联结条件)的行。没有WHERE子句,第一个表中的每一行将与第二个表中的每一行配对,而不管它们逻辑上是否能配在一起。

笛卡儿积(cartesian product)

由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数

提示:叉联结

有时,返回笛卡儿积的联结,也称叉联结(cross join)

12.2.2 内联结

--上述where的等价SQL语句
SELECT
    vend_name,
    prod_name,
    prod_price
FROM
    vendors
INNER JOIN products ON vendors.vend_id = products.vend_id;

12.2.3 联结多个表

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在运行时关联指定的每个表,以处理联结。这种处理可能非常耗费资源,因此应该注意,不要联结不必要的表。联结的表越多,性能下降越厉害。

第13课 创建高级联结

13.1 使用表别名

  1. SQL可以给列名计算字段表名起别名;
  2. 使用别名的好处:
    1. 缩短SQL语句;
    2. 允许在一条SELECT语句中多次使用相同的表。
SELECT
    cust_name,
    cust_contact
FROM
    customers AS C,
    orders AS O,
    orderitems AS OI
WHERE
    O.cust_id = C.cust_id
AND O.order_num = OI.order_num AND prod_id = 'RGAN01';
  1. 表别名可以在任何子句中使用(如WHERE子句SELECT的列表ORDER BY子句等);
  2. 表别名只在查询执行中使用。与列别名不一样,表别名不返回到客户端;

13.2 使用不同类型的联结

联接的类型

  • 等值联接/内联接(inner join)
  • 自联接(self-join)
  • 自然联接(natural join)
  • 外联接(outer join)

13.2.1 自联结

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';
  • 分析
  1. 此查询中需要的两个表实际上是相同的表,因此Customers表在FROM子句中出现了两次。虽然这是完全合法的,但对Customers的引用具有歧义性,因为DBMS不知道你引用的是哪个Customers表。

  2. 解决此问题,需要使用表别名。Customers第一次出现用了别名C1,第二次出现用了别名C2。现在可以将这些别名用作表名。例如,SELECT语句使用C1前缀明确给出所需列的全名。如果不这样,DBMS将返回错误,因为名为cust_id、cust_name、cust_contact的列各有两个。DBMS不知道想要的是哪一列(即使它们其实是同一列)。WHERE首先联结两个表,然后按第二个表中的cust_contact过滤数据,返回所需的数据。

提示:用自联结而不用子查询

自联结通常作为外部语句,用来替代从相同表中检索数据的使用子查询语句。虽然最终的结果是相同的,但许多DBMS处理联结远比处理子查询快得多。应该试一下两种方法,以确定哪一种的性能更好。

13.2.2 自然联结

  1. 自然联接的作用:
    • 标准的联结(前一课中介绍的内联结)返回所有数据,相同的列甚至多次出现。自然联结排除多次出现,使每一列只返回一次。
  2. 如何实现自然联接:
    • 自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符(SELECT *),而对其他表的列使用明确的子集来完成。
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';

实际上,我们建立的大多数内联接都是自然联接,可能用不到不是自然联接的内联接。

13.2.3 外联结(使用OUTER JOIN来指定联结类型)

  1. 定义:联结包含了那些在相关表中没有关联行的行。
  2. 分类
    1. 左外联接(LEFT OUTER JOIN)
    2. 右外联接(RIGHT OUTER JOIN)
    3. 全外联接(FULL OUTER JOIN)
-- 输出Customers表的所有行
SELECT
    Customers.cust_id,
    Orders.order_num
FROM
    Customers
LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;
  1. 在使用OUTER JOIN语法时,必须使用RIGHTLEFT关键字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT指出的是OUTER JOIN左边的表)。
  2. 左外联结和右外联结的区别:唯一的区别是所关联的表的顺序。
  3. 全外联接:
SELECT
    Customers.cust_id,
    Orders.order_num
FROM
    Orders
FULL OUTER JOIN Customers ON Orders.cust_id = Customers.cust_id;

全外链接检索两个表中的所有行并关联那些可以关联的行;

13.3 使用带聚集函数的联结

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;

13.4 使用联结和联结条件的总结

  1. 注意所使用的联结类型
    • 一般我们使用内联结,但使用外联结也有效
  2. 关于确切的联结语法,应该查看具体的文档,看相应的DBMS支持何种语法
  3. 保证使用正确的联结条件(不管采用哪种语法),否则会返回不正确的数据。
  4. 应该总是提供联结条件,否则会得出笛卡儿积。
  5. 在一个联结中可以包含多个表,甚至可以对每个联结采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们前分别测试每个联结。这会使故障排除更为简单。

第14课 组合查询

14.1 组合查询/复合查询(compound query)/并(union)

  1. 定义:执行多条SELECT语句,并将结果作为一个查询结果返回;
  2. 使用场景:
    1. 在一个查询中从不同的表返回结构数据;
    2. 对一个表执行多个查询,按一个查询返回数据;
  3. 组合查询和多个where条件
    • 多数情况下,组合相同表的两个查询所完成的工作与具有多个WHERE子句条件的一个查询所完成的工作相同。换句话说,任何具有多个WHERE子句的SELECT语句都可以作为一个组合查询。
    • 利用UNION,可以把多条查询的结果作为一条组合查询返回,不管结果中有无重复。使用UNION可极大地简化复杂的WHERE子句,简化从多个表中检索数据的工作。

14.2 创建组合查询

只需要使用UNION操作符把多个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;       

等价的where语句

SELECT
    cust_name,
    cust_contact,
    cust_email
FROM
    customers
WHERE
    cust_state IN ('IL', 'IN', 'MI')
OR cust_name = 'Fun4All';
ORDER BY cust_name, cust_contact;

14.2.2 UNION使用规则

  1. 性能和多个where没有多大差别,具体需要测试;
  2. UNION从查询结果集中自动去除了重复的行,如果想返回所有匹配的行,使用UNION ALL
  3. 在用UNION组合查询时,只能使用一条ORDER BY子句,它必须位于最后一条SELECT语句之后;
    • 对于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条ORDER BY子句;
  4. 使用规则
    1. UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔;
    2. UNION中的每个查询必须包含相同的列、表达式或聚集函数(各个列次序可不同);
    3. 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含转换的类型;

第15课 插入数据

15.1.1插入部分行

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'
    );

说明:

  1. 不需要列出所有的列;
  2. 如果表的定义允许,则可以在INSERT操作中省略某些列。省略的列必须满足以下某个条件。
    1. 该列定义为允许NULL值(无值或空值);
    2. 在表定义中给出默认值。这表示如果不给出值,将使用默认值;
    3. 如果对表中不允许NULL值且没有默认值的列不给出值,DBMS将产生错误消息,并且相应的行插入不成功;

15.1.3 插入检索出的数据

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
WHERE XXX;

说明:

  1. INSERT INTO() SELECT() FROM table WHERE
  2. INSERT SELECT中的列名:
    • 为简单起见,这个例子在INSERT和SELECT语句中使用了相同的列名。但是,不一定要求列名匹配。事实上,DBMS一点儿也不关
      心SELECT返回的列名。它使用的是列的位置,因此SELECT中的第一列(不管其列名)将用来填充表列中指定的第一列,第二列将用来填充表列中指定的第二列;
  3. INSERT通常只插入一行。要插入多行,必须执行多个INSERT语句。INSERT SELECT是个例外,它可以用一条INSERT插入多行,不管SELECT语句返回多少行,都将被INSERT插入。

15.2 从一个表复制到另一个表(SELECT INTO

SELECT
    * INTO CustCopy
FROM
    Customers;

说明:

  1. INSERT SELECTSELECT INTO的区别
    • INSERT SELECT导出数据;
    • SELECT INTO导入数据;
  2. 要想只复制部分的列,可以明确给出列名,而不是使用*通配符;
  3. 任何SELECT选项和子句都可以使用,包括WHERE和GROUP BY;
  4. 可利用联结从多个表插入数据;
  5. 不管从多少个表中检索数据,数据都只能插入到一个表中。
-- 这个不懂
CREATE TABLE CustCopy AS SELECT * FROM Customers;

第16课 更新和删除数据

16.1 更新数据

  • 更新的语法
UPDATE table 
SET columnName1 ='xxxx',
    columnName2 ='XXX',
    columnName3 ='XXX'
WHERE columnName4 ='' AND columnName5 ='';
  • 举例
UPDATE Customers
SET cust_contact = 'Sam Roberts',
    cust_email = 'sam@toyland.com'
WHERE cust_id = '1000000006';
  • UPDATE语句中使用子查询
    • UPDATE语句中可以使用子查询,使得能用SELECT语句检索出的数据更新列数据。

16.2 删除数据

  • delete是删除一整行数据,而不是一行的某几列数据,即删除的是表的内容,而不是表本身,删除表本身使用drop;
  • 删除的语法
DELETE FROM table
WHERE columnName1 = '' AND columnName2 ='';
  • 举例
DELETE FROM Customers
WHERE cust_id = '1000000006';
  • 更快的删除行
    如果想从表中删除所有行,不要使用DELETE。可使用TRUNCATE TABLE语句,它完成相同的工作,而速度更快(因为不记录数据的变
    动)
  • 友好的外键(不懂)

第12课介绍了联结,简单联结两个表只需要这两个表中的常用字段。也可以让DBMS通过使用外键来严格实施关系。存在外键时,DBMS使用它们实施引用完整性。例如要向Products表中插入一个新产品,DBMS不允许通过未知的供应商id插入它,因为vend_id列是作为外键连接到Vendors表的。那么,这与DELETE有什么关系呢?使用外键确保引用完整性的一个好处是,DBMS通常可以防止删除某个关系需要用到的行。例如,要从Products表中删除一个产品,而这个产品用在OrderItems的已有订单中,那么DELETE语句将抛出错误并中止。这是总要定义外键的另一个理由。

16.3 更新和删除的指导原则

  1. 除非确实打算更新和删除每一行,否则绝对不要使用不带WHERE子句的UPDATE或DELETE语句。
  2. 保证每个表都有主键,尽可能像WHERE子句那样使用它(可以指定各主键、多个值或值的范围)。
  3. 在UPDATE或DELETE语句使用WHERE子句前,应该先用SELECT进行测试,保证它过滤的是正确的记录,以防编写的WHERE子句不正确。
  4. 使用强制实施引用完整性的数据库,这样DBMS将不允许删除其数据与其他表相关联的行。
  5. 有的DBMS允许数据库管理员施加约束,防止执行不带WHERE子句的UPDATE或DELETE语句。如果所采用的DBMS支持这个特性,应该使用它。

第17课 创建和操纵表

17.1 创建表

-- 使用default 给某一列指定默认值
-- primary key 指定主键
-- autoincrement 自增长
CREATE TABLE OrderItems IF NOT EXISTS
(
    order_num INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    order_item INTEGER NOT NULL,
    prod_id CHAR(10) NOT NULL,
    quantity INTEGER NOT NULL DEFAULT 1,
    item_price DECIMAL(8,2) NULL
);

说明:

  1. 不允许NULL值的列不接受没有列值的行,否则会报错。
  2. 在不指定NOT NULL时,多数DBMS认为指定的是NULL,但是最好加上NULL;
  3. 不要把NULL值与空字符串相混淆。NULL值是没有值,不是空字符串。如果指定”(两个单引号,其间没有字符),这在NOT NULL列中是允许的。空字符串是一个有效的值,它不是无值。NULL值用关键字NULL而不是空字符串指定;

17.2 更新表(ALERT TABLE)

  1. 使用alert table时要考虑的问题
    1. 理想情况下,不要在表中包含数据时对其进行更新。应该在表的设计过程中充分考虑未来可能的需求,避免今后对表的结构做大改动。
    2. 所有的DBMS都允许给现有的表增加列,不过对所增加列的数据类型(以及NULL和DEFAULT的使用)有所限制。
    3. 许多DBMS不允许删除或更改表中的列。
    4. 多数DBMS允许重新命名表中的列。
    5. 许多DBMS限制对已经填有数据的列进行更改,对未填有数据的列几乎没有限制。
  2. 使用ALTER TABLE更改表结构,必须给出下面的信息:
    1. ALTER TABLE之后给出要更改的表名(该表必须存在,否则将出错);
    2. 列出要做哪些更改。
  3. 增加、更改、删除列、增加约束、增加键的做法都是类似的
ALTER TABLE Vendors
ADD vend_phone CHAR(20);

ALTER TABLE Vendors
DROP COLUMN vend_phone;
  1. 复杂的表结构更改一般需要手动删除过程,它涉及以下步骤(不懂)
    1. 用新的列布局创建一个新表;
    2. 使用INSERT SELECT语句,从旧表复制数据到新表。有必要的话,可以使用转换函数和计算字段;
    3. 检验包含所需数据的新表;
    4. 重命名旧表(如果确定,可以删除它);
    5. 用旧表原来的名字重命名新表;
    6. 根据需要,重新创建触发器、存储过程、索引和外键。
  2. 使用ALTER TABLE要极为小心,应该在进行改动前做完整的备份(模式和数据的备份),数据库的操作大多数都不可逆。

17.3 删除表

-- 就是这么简单
DROP TABLE CustCopy;

提示:使用关系规则防止意外删除

许多DBMS允许强制实施有关规则,防止删除与其他表相关联的表。在实施这些规则时,如果对某个表发布一条DROP TABLE语句,且该表
是某个关系的组成部分,则DBMS将阻止这条语句执行,直到该关系被删除为止。如果允许,应该启用这些选项,它能防止意外删除有用的
表。

17.4 重命名表

不同的DBMS对重命名完全不一样。参照具体的文档。

第18课 使用视图

18.1 视图

  1. 定义:视图是虚拟的表,包含的不是数据而是根据需要检索数据的查询,即将select语句封装起来。
  2. 作用:
    1. 简化数据处理,使用表的一部分而不是整个表;
    2. 提高sql语句的复用性,简化复杂的sql操作;
    3. 重新格式化数据,视图可返回与底层表的表示和格式不同的数据;
    4. 保护基础数据,可以授予用户访问表的特定部分的权限,而不是整个表的访问权限;
  3. 试图创建好之后,可以像正常的表一样操作他们,可以对视图执行SELECT操作过滤排序数据,将视图联结到其他视图,甚至添加更新数据。
  4. 试图的规则和限制
    1. 视图的名字必须唯一(与其他视图和表名都不能重复);
    2. 可以创建无数多个视图;
    3. 创建视图,必须具有足够的访问权限。这些权限通常由数据库管理人员授予。
    4. 视图可以嵌套,即视图里面含有视图,但是性能下降会很厉害,使用时需要测试;
    5. 许多DBMS禁止在视图查询中使用ORDER BY子句;
    6. 有些DBMS要求对返回的所有列进行命名,如果列是计算字段,则需要使用别名;
    7. 视图不能索引,也不能有关联的触发器或默认值;
    8. 有些DBMS把视图作为只读的查询,这表示可以从视图检索数据,但不能将数据写回底层表;
    9. 有些DBMS允许创建这样的视图,它不能进行导致行不再属于视图的插入或更新;(例如有一个视图,只检索带有电子邮件地址的顾客。如果更新某个顾客,删除他的电子邮件地址,将使该顾客不再属于视图。这是默认行为,而且是允许的,但有的DBMS可能会防止这种情况发生。)

18.2 创建视图

-- 创建视图,只能用来创建不存在的视图
CREATE VIEW view_name AS SELECT
-- 删除视图
DROP VIEW view_name;
  1. 更新视图,必须先删除它,然后重新创建;
  2. 视图的创建与使用举例
    -- 创建视图
    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;
    
    --使用视图,当DBMS处理此查询时,
    -- 它将指定的WHERE子句添加到视图查询中
    -- 已有的WHERE子句中,以便正确过滤数据。
    SELECT cust_name, cust_contact
    FROM ProductCustomers
    WHERE prod_id = 'RGAN01';
    
  3. 如何创建视图
    1. 先把select语句创建写出来;
    2. 然后把创建视图的语句加上;
    3. select语句有什么功能,视图就有什么功能,比如在视图中过滤不想要的数据,在试图中创建计算字段;
    -- 用视图过滤不想要的数据
    CREATE VIEW CustomerEMailList AS
    SELECT cust_id, cust_name, cust_email
    FROM Customers  
    WHERE cust_email IS NOT NULL;
    
    -- 使用视图与计算字段
    CREATE VIEW OrderItemsExpanded AS
    SELECT order_num,
    prod_id,
    quantity,
    item_price,
    quantity*item_price AS expanded_price
    FROM OrderItems;
    

第19课 使用存储过程

存储过程就是为以后使用而保存的一条或多条SQL语句。可将其视为批文件,但它们的作用不仅限于批处理。

19.2 为什么要使用存储过程

1. 存储过程的好处:简单安全高性能

  1. 通过把处理封装在一个易用的单元中,可以简化复杂的操作;
  2. 由于不要求反复建立一系列处理步骤,因而保证了数据的一致性(防止错误);
  3. 简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,那么只需要更改存储过程的代码(保证安全性);
  4. 因为存储过程通常以编译过的形式存储,所以DBMS处理命令的工作较少,提高了性能;
  5. 存在一些只能用在单个请求中的SQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码;

2. 存储过程的不足

  1. 不同DBMS中的存储过程语法有所不同,代码不具有可可移植性;
  2. 编写存储过程比编写基本SQL语句复杂,需要更高的技能,更丰富的经验;

19.3 执行存储过程

-- orcale版本的存储过程
-- 语法:EXECUTE <存储过程名称> <参数>
-- 利用存储过程插入一条数据
EXECUTE AddNewProduct( 'JTS01',
    'Stuffed Eiffel Tower',
    6.49,
    'Plush stuffed toy with the text La
    tour Eiffel in red white and blue' );

19.4 创建存储过程

-- orcale 版本
-- 功能:对邮件发送清单中具有邮件地址的顾客进行计数
CREATE PROCEDURE MailingListCount (
    ListCount OUT INTEGER
) 
IS v_rows INTEGER;
BEGIN
    SELECT COUNT(*) INTO v_rows
    FROM Customers
    WHERE NOT cust_email IS NULL;
    ListCount := v_rows;
END;

分析:

  1. 这个存储过程有一个名为ListCount的参数。此参数从存储过程返回一个值而不是传递一个值给存储过程。关键字OUT用来指示这种行为。
  2. 存储过程的代码括在BEGINEND语句中,这里执行一条简单的SELECT语句,它检索具有邮件地址的顾客。然后用检索出的行数设置ListCount(要传递的输出参数)

Oracle支持

  1. IN(传递值给存储过程);
  2. OUT(从存储过程返回值);
  3. INOUT(既传递值给存储过程也从存储过程传回值)类型的参数;

调用存储过程

-- 这段代码声明了一个变量来保存存储过程返回的任何
-- 值,然后执行存储过程,再使用SELECT语句显示返回的值
var ReturnValue NUMBER
EXEC MailingListCount(:ReturnValue);
SELECT ReturnValue;

第20课 管理事务处理

20.1 事务处理

1. 使用事务的目的

使用事务处理(transaction processing),通过确保成批的SQL操作要么完全执行,要么完全不执行,来维护数据库的完整性。

2. 什么是事务处理

事务处理是一种机制,用来管理必须成批执行的SQL操作保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们要么完全执行,要么完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表;如果发生错误,则进行回退(撤销),将数据库恢复到某个已知且安全的状态。

3. 几个概念

  1. 事务(transaction):指一组SQL语句;
  2. 回退(rollback):指撤销指定SQL语句的过程;
  3. 提交(commit):指将未存储的SQL语句结果写入数据库表;
  4. 保留点(savepoint):指事务处理中设置的临时占位符(placeholder),可以对它发布回退(与回退整个事务处理不同)

4. 不能回退的语句

  1. 事务处理用来管理INSERTUPDATEDELETE语句;
  2. 不能回退SELECT,DROP,CREATE语句;

20.2 控制事务处理

管理事务的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退

-- BEGIN TRANSACTION和COMMIT TRANSACTION语句之间的SQL
-- 必须完全执行或者完全不执行
-- 不同的DBMS不一样
BEGIN TRANSACTION
...
COMMIT TRANSACTION

1.使用ROLLBACK

--执行DELETE操作,然后用ROLLBACK语句撤销
DELETE FROM Orders;
ROLLBACK;

2.使用COMMIT

一般的SQL语句都是针对数据库表直接执行和编写的。这就是所谓的隐式提交(implicit commit),即提交(写或保存、删除)操作是自动进行的。在事务处理块中,提交不会隐式进行。不过,不同DBMS的做法有所不同。有的DBMS按隐式提交处理事务端,有的则不这样。

-- SQL Server使用commit进行(明确)提交
BEGIN TRANSACTION
DELETE OrderItems WHERE order_num = 12345
DELETE Orders WHERE order_num = 12345
COMMIT TRANSACTION

-- Oracle使用commit
SET TRANSACTION
DELETE OrderItems WHERE order_num = 12345;
DELETE Orders WHERE order_num = 12345;
COMMIT;

3.使用保留点

1.使用保留点的原因

  • 使用简单的ROLLBACKCOMMIT语句,就可以写入或撤销整个事务。但是,只对简单的事务才能这样做,复杂的事务可能需要部分提交或回退;
  • 要支持回退部分事务,必须在事务处理块中的合适位置放置占位符。这样,如果需要回退,可以回退到某个占位符;在SQL中,这些占位符称为保留点
  1. 每个保留点都要取能够标识它的唯一名字,以便在回退时,DBMS知道回退到何处
-- MariaDB、MySQL和Oracle
SAVEPOINT delete1;

ROLLBACK TO delete1;

-- SQL Server
SAVE TRANSACTION delete1;

ROLLBACK TRANSACTION delete1;
-- 一个完整的SQL Server例子
BEGIN TRANSACTION
INSERT INTO Customers(cust_id, cust_name)
VALUES('1000000010', 'Toys Emporium');
-- 这里设置了一个保留点,如果后面的任何一个INSERT操作失败,
-- 事务处理最近回退到这里
SAVE TRANSACTION StartOrder;  
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20100,'2001/12/1','1000000010');
-- 在SQL Server中,可检查一个名为@@ERROR的变量,看操作是否成功
-- 如果@@ERROR返回一个非0的值,表示有错误发生,
-- 事务处理回退到保留点
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 1, 'BR01', 100, 5.49);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 2, 'BR03', 100, 10.99);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION

第21课 使用游标

21.1 游标

  1. 结果集:SQL查询所检索出的结果
  • SELECT操作返回一组称为结果集的行,这组返回的行都是与SQL语句相匹配的行(零行或多行)。
  1. 游标
    游标(cursor)是一个存储在DBMS服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集;可以在检索出来的行中前进或后退一行或多行。

21.2 使用游标

使用步骤

  1. 在使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的SELECT语句和游标选项。
  2. 一旦声明,就必须打开游标以供使用。这个过程用前面定义的SELECT语句把数据实际检索出来。
  3. 对于填有数据的游标,根据需要取出(检索)各行。
  4. 在结束游标使用时,必须关闭游标,可能的话,释放游标(有赖于具体的DBMS)。

1、创建游标

使用DECLARE语句创建游标,DECLARE命名游标,并定义相应的SELECT语句,根据需要带WHERE和其他子句。

-- DB2、MariaDB、MySQL和SQL Server
DECLARE CustCursor CURSOR
FOR
SELECT * FROM Customers
WHERE cust_email IS NULL

-- Oracle和PostgreSQL
DECLARE CURSOR CustCursor
IS
SELECT * FROM Customers
WHERE cust_email IS NULL

2、使用游标

-- 打开游标
OPEN CURSOR CustCursor
-- oracle
-- FETCH用来检索当前行(自动从第一行开始),
-- 放到声明的变量CustRecord中。
-- 对于检索出来的数据不做任何处理
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的变量中
DECLARE TYPE CustCursor IS REF CURSOR
    RETURN Customers%ROWTYPE;
DECLARE CustRecord Customers%ROWTYPE
BEGIN
    OPEN CustCursor;
    LOOP
    -- 这里的FETCH位于LOOP内,因此它反复执行。
    -- 代码EXIT WHEN  CustCursor%NOTFOUND
    -- 使在取不出更多的行
    -- 时终止处理(退出循环)
    FETCH CustCursor INTO CustRecord;
    EXIT WHEN CustCursor%NOTFOUND;
    ...
    END LOOP;
    -- 关闭游标
    CLOSE CustCursor;
END;

第22课 高级SQL特性

22.1 约束

约束(constraint): 管理如何插入或处理数据库数据的规则。

1.1 主键

  1. 主键是一种特殊的约束,用来保证一列(或一组列)中的值是唯一的,而且永不改动。
  2. 表中任意列只要满足以下条件,都可以用于主键:
    1. 任意两行的主键值都不相同。
    2. 每行都具有一个主键值(即列中不允许NULL值)。
    3. 包含主键值的列从不修改或更新。(大多数DBMS不允许这么做,但如果你使用的DBMS允许这样做,好吧,千万别!)
    4. 主键值不能重用。如果从表中删除某一行,其主键值不分配给新行
CREATE TABLE Vendors
( 
    --通过primary key来指定主键
    vend_id CHAR(10) NOT NULL PRIMARY KEY,  
    vend_name CHAR(50) 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
ADD CONSTRAINT PRIMARY KEY (vend_id);

1.2 外键

CREATE TABLE Orders
(
    order_num INTEGER NOT NULL PRIMARY KEY,
    order_date DATETIME NOT NULL,
    -- 添加外键
    -- 使用了REFERENCES关键字,它表示cust_id中的
    -- 任何值都必须是Customers表的cust_id中的值
    cust_id CHAR(10) NOT NULL REFERENCES Customers(cust_id)
);


ALTER TABLE Orders
ADD CONSTRAINT
FOREIGN KEY (cust_id) REFERENCES Customers (cust_id)

提示:外键有助防止意外删除

外键的作用

  1. 保证引用完整性;
  2. 在定义外键后,DBMS不允许删除在另一个表中具有关联行的行;

例如,不能删除关联订单的顾客。删除该顾客的唯一方法是首先删除相关的订单(这表示还要删除相关的订单项)。由于需要一系列的删除,
因而利用外键可以防止意外删除数据。

有的DBMS支持称为级联删除(cascading delete)的特性。如果启用,该特性在从一个表中删除行时删除所有相关的数据。例如,如果启用级联删除并且从Customers表中删除某个顾客,则任何关联的订单行也会被自动删除。

1.3 唯一约束

  1. 唯一约束用来保证一列(或一组列)中的数据是唯一的。它们类似于主键,但存在以下重要区别。

    • 表可包含多个唯一约束,但每个表只允许一个主键。
    • 唯一约束列可包含NULL值。
    • 唯一约束列可修改或更新。
    • 唯一约束列的值可重复使用。
    • 与主键不一样,唯一约束不能用来定义外键
  2. 语法:唯一约束既可以用UNIQUE关键字在表定义中定义,也可以用单独的CONSTRAINT定义

employees表中每个雇员都有唯一的社会安全号,但我们并不想用它作主键,因为它太长(而且我们也不想使该信息容易利用)。因此,每个雇员除了其社会安全号外还有唯一的雇员ID(主键)。

雇员ID是主键,可以确定它是唯一的。你可能还想使DBMS保证每个社会安全号也是唯一的(保证输入错误不会导致使用他人号码)。可以通过在社会安全号列上定义UNIQUE约束做到。

1.4 检查约束

  1. 检查约束用来保证一列(或一组列)中的数据满足一组指定的条件。检查约束的常见用途有以下几点。
    1. 检查最小或最大值。例如,防止0个物品的订单(即使0是合法的数)。
    2. 指定范围。例如,保证发货日期大于等于今天的日期,但不超过今天起一年后的日期。
    3. 只允许特定的值。例如,在性别字段中只允许M或F。
CREATE TABLE OrderItems
(
    order_num INTEGER NOT NULL,
    order_item INTEGER NOT NULL,
    prod_id CHAR(10) NOT NULL,
    -- 利用这个约束,任何插入(或更新)的行都会被检查,保证quantity大于0。
    quantity INTEGER NOT NULL CHECK (quantity > 0),
    item_price MONEY NOT NULL
);
-- 检查名为gender的列只包含M或F,可编写如下的ALTER TABLE语句
ADD CONSTRAINT CHECK (gender LIKE '[MF]');

22.2 索引

  1. 索引用来排序数据加快搜索排序操作的速度,类似于书的目录;
  2. 可以在一个或多个列上定义索引,使DBMS保存其内容的一个排过序的列表。
  3. 关于索引的几点认识
    1. 索引改善检索操作的性能,但降低了数据插入、修改和删除的性能。在执行这些操作时,DBMS必须动态地更新索引。
    2. 索引数据可能要占用大量的存储空间。
    3. 并非所有数据都适合做索引。取值不多的数据(如州)不如具有更多可能值的数据(如姓或名),能通过索引得到那么多的好处。
    4. 索引用于数据过滤和数据排序。如果你经常以某种特定的顺序排序数据,则该数据可能适合做索引。
    5. 可以在索引中定义多个列(例如,州加上城市)。这样的索引仅在以州加城市的顺序排序时有用。如果想按城市排序,则这种索引没有用处。
-- 在Products表的产品名列上创建一个简单的索引
-- 索引必须唯一命名
CREATE INDEX prod_name_ind
ON PRODUCTS (prod_name);
  1. 索引的效率随表数据的增加或改变而变化,定期检查索引,并根据需要对索引进行调整;

22.3 触发器

  1. 触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行;
  2. 触发器可以与特定表上的INSERT、UPDATE和DELETE操作(或组合)相关联。
  3. 触发器与单个的表相关联(存储过程可以关联多张表,存储过程只是简单的存储SQL语句)。与Orders表上的INSERT操作相关联的触发器只在Orders表中插入行时执行;
  4. 触发器内的代码具有的权限
    1. INSERT操作中的所有新数据;
    2. UPDATE操作中的所有新数据和旧数据;
    3. DELETE操作中删除的数据。
  5. 触发器的一些常见用途
    1. 保证数据一致。例如,在INSERT或UPDATE操作中将所有州名转换为大写。
    2. 基于某个表的变动在其他表上执行活动。例如,每当更新或删除一行时将审计跟踪记录写入某个日志表。
    3. 进行额外的验证并根据需要回退数据。例如,保证某个顾客的可用资金不超限定,如果已经超出,则阻塞插入。
    4. 计算计算列的值或更新时间戳
-- 对所有INSERT和UPDATE操作,将Customers表中的cust_state列转换为大写
-- SQL Server
CREATE TRIGGER customer_state
ON Customers
FOR INSERT, UPDATE
AS
UPDATE Customers
SET cust_state = Upper(cust_state)
WHERE Customers.cust_id = inserted.cust_id;


-- Oracle和PostgreSQL
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;
  1. 约束的处理比触发器快,因此在可能的时候,应该尽量使用约束。

22.4 数据库安全

  1. 使用用户名用户密码来保证系统安全。
  2. 一般说来,需要保护的操作有:
    1. 对数据库管理功能(创建表、更改或删除已存在的表等)的访问;
    2. 对特定数据库或表的访问;
    3. 访问的类型(只读、对特定列的访问等);
    4. 仅通过视图或存储过程对表进行访问;
    5. 创建多层次的安全措施,从而允许多种基于登录的访问和控制;
    6. 限制管理用户账号的能力。
  3. 安全性使用SQL的GRANTREVOKE语句来管理,交互式管理程序在内部使用GRANTREVOKE语句;
    原文作者:VictorBXv
    原文地址: https://www.jianshu.com/p/5daf71d09c25
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞