实操展示SQL几个高级特性

高级SQL特性

  • 1约束
    • 1.1主键
    • 1.2外键
    • 1.3唯一约束
    • 1.4检查约束
  • 2索引
  • 3触发器
  • 4数据库安全

1约束

正确的进行关系数据设计,需要一种方法保证只在表中插入合法数据。虽然可以在插入新行的时候进行检查(在另一个表上执行SELECT,以保证所有值合法并存在),但最好不要这么做,原因如下:

  • 如果在客户端层面上实施数据库完整性规则,则每个客户端都要被迫实施这些规则,一定会有一些客户端不实施这些规则。
  • 在执行UPDATE和DELETE操作时,也必须实施这些规则。
  • 执行客户端检查是非常耗时的,而DBMS执行这些检查会相对高效。

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

DBMS通过在数据库表上实施约束来实施引用完整性。大多数约束是在表定义中定义的,比如CREATE TABLE语句或ALTER TABLE语句

1.1主键

主键是一种特殊的约束,用来保证一列(或者一组列)中的值是唯一的,而且永不改动。这方便了直接或交互的处理表中的行。

表中任意行满足以下条件都可以用于主键:

  • 任意两行的主键值都不相同
  • 每行都具有一个主键值(即列中不允许NULL值)
  • 包含主键值的列从不修改或更新
  • 主键值不能重用,如果从表中删除某一行,其主键值不分配给新行

一种定义主键的方法是创建它,如下所示:

CREATE TABLE Vendors  
( 
    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 
);

分析:在此示例中给vend_id列定义添加关键字PRIMARY KEY,使其成为主键

ALTER TABLE Vendors
ADD CONSTRAINT PRIMARY KEY (vend_id);

分析:这里定义相同的列为主键,但是使用的是CONSTRAINT(约束)语法。此语法也可以用于CREATE TABLE和ALTER TABLE语句

1.2外键

外键是表中的一列,其值在另一个表的主键中。外键是保证引用完整性积极重要的一部分。
下面是定义外键的方法:

原来是没有外键的
实操展示SQL几个高级特性_第1张图片
添加外键

ALTER TABLE Orders1
 ADD CONSTRAINT FK_Orders1_Customers 
 FOREIGN KEY (cust_id) REFERENCES Customers (cust_id);

实操展示SQL几个高级特性_第2张图片
实操展示SQL几个高级特性_第3张图片
分析:其中的表定义使用了REFERENCES关键字,它表示cust_id中的任何值都必须是Customers表的cust_id中的值。

1.3唯一约束

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

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

这几条与主键相对应区别

唯一约束的语法类似于其他约束的语法。唯一约束既可以用UNIQUE关键I在表定义中定义,也可以用单独的CONSTRAINT定义。

1.4检查约束

检查约束用来保证一列(或者一组列)中的数据满足一组指定的条件。检查约束的常见用途有以下几点。

  • 检查最小或最大值。例如,防止0个物品的订单(即使0是合法的数)。
  • 指定范围。例如,保证发货日期大于等于今天的日期,但不超过今天起一年后的日期。
  • 只允许特定的值。例如,在性别字段中只允许M或F。

换句话说,数据类型限制了列中可保存的数据的类型。检查约束在数据类型内又做了进一步的限制,这些限制及其重要,可以确保插入数据库的数据证实你想要的的数据。不需要依赖于客户端,应用程序或者用户来保证正确获取它,DBMS本身将会拒绝任何无效数据

下面的列子对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。

检查名为gender的列只包含M或F,可编写如下的ALTER TABLE语句

输入

ADD CONSTRAINT CHECK (gender LIKE '[MF]')

2索引

索引用来排序数据以加快搜索和排序操作的速度。

使用索引的原因是:恰当的排序。
主键数据是排序的,这是DBMS的工作。因此,按住键检索特定行总是一种快速有效的操作。但是搜索其他列中的值通常效率不高。

在创建索引前,应该记住以下内容。

  • 索引改善检索操作的性能,但降低了数据插入、修改和删除的性能。在执行这些操作时,DBMS必须动态地更新索引。
  • 索引数据可能要占用大量的存储空间。
  • 并非所有数据都适合做索引。取值不多的数据(如州)不如具有更多可能值得数据(如姓或名),能通过索引得到那么多的好处。
  • 索引用于数据过滤和数据排序。如果你经常以莫衷疼的顺序排序数据,则该数据可能适合做索引。
  • 可以在索引中定义多个类(例如,州加上城市)。这样的索引仅在以州加城市的顺序排序时有用。如果想按城市排序,则这种所以因没有用处。

没有严格的规则要求什么应该索引,何时索引。大多数DBMS提供了可用来确定索引效率的使用程序,应该经常使用这些使用程序。

索引用CREATE INDEX语句创建(不同DBMS创建索引的语句变化很大)

下面的语句在Products表的产品列上创建一个简单的索引。

输入

CREATE INDEX prod_name_ind
ON Products (prod_name);

在这里插入图片描述
查看索引

mysql> show index from products;
+----------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table    | Non_unique | Key_name            | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+----------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| products |          0 | PRIMARY             |            1 | prod_id     | A         |           9 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| products |          1 | FK_Products_Vendors |            1 | vend_id     | A         |           3 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| products |          1 | prod_name_ind       |            1 | prod_name   | A         |           9 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+----------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+

实操展示SQL几个高级特性_第4张图片
分析:索引必须唯一命名。这里的索引名prod_name_ind在关键字CREATE INDEX之后定义。ON用来指定被索引的表。而索引中包含的列(此例中仅有一列)在表名后的圆括号给出。

3触发器

触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行。触发器可以与特定表上的INSERT、UPDATE和DELETE操作(或组合)相关联。
与存储过程不一样(存储过程只是简单的存储SQL语句),触发器与单个的表相关联。与Order表上的INSERT操作相关联的触发器只在Orders表汇总插入行时执行。类似地,Customers表上对的INSERT和UPDATE操作的触发器只在表上出现这些操作时执行。

触发器内的代码具有以下数据的访问权:
INSERT操作中的所有新数据
UPDATE操作中的所有新数据和旧数据
DELETE操作中删除的数据

根据所使用的DBMS的不同,触发器可在特定操作执行之前或之后执行。

下面是一些常见用途。

  • 保证数据一致。例如,在INSERT或UPDATE操作汇总将所有州名转换为大写。
  • 基于某个表的变动在其他表上执行活动。例如,每当更新或者删除一行时将审计跟踪记录写入某个日志表。
  • 进行额外的验证并根据需要回退数据。
  • 计算计算列的值或更新时间戳。

下面创建一个触发器,它对所有INSERT和UPDATE操作,将Customer表中的cust_name列转换为大写。

这是本例的MySQL版本

输入

CREATE TRIGGER customer_state BEFORE INSERT ON customers FOR EACH ROW
BEGIN
		UPDATE Customers 
		SET cust_state = Upper( cust_state ) 
	WHERE
		Customers.cust_id = INSERT.cust_id;

END;

实操展示SQL几个高级特性_第5张图片
实操展示SQL几个高级特性_第6张图片
查看触发器的信息 使用SHOW TRIGGERS;

mysql> SHOW TRIGGERS;
+----------------+--------+-----------+----------------------------------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
| Trigger        | Event  | Table     | Statement                                                                                                      | Timing | Created                | sql_mode                                                                                                              | Definer        | character_set_client | collation_connection | Database Collation |
+----------------+--------+-----------+----------------------------------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
| customer_state | INSERT | customers | BEGIN
UPDATE Customers
SET cust_state = Upper(cust_state)
WHERE Customers.cust_id = INSERT.cust_id;
END | BEFORE | 2020-05-11 12:35:50.24 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | root@localhost | utf8mb4              | utf8mb4_0900_ai_ci   | utf8mb4_0900_ai_ci |
+----------------+--------+-----------+----------------------------------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+

使用SELECT * FROM information_schema.triggers \G ;语句查看触发器的详细信息

mysql> SELECT * FROM information_schema.triggers \G ;
*************************** 1. row ***************************
           TRIGGER_CATALOG: def
            TRIGGER_SCHEMA: sys
              TRIGGER_NAME: sys_config_insert_set_user
        EVENT_MANIPULATION: INSERT
      EVENT_OBJECT_CATALOG: def
       EVENT_OBJECT_SCHEMA: sys
        EVENT_OBJECT_TABLE: sys_config
              ACTION_ORDER: 1
          ACTION_CONDITION: NULL
          ACTION_STATEMENT: BEGIN
    IF @sys.ignore_sys_config_triggers != true AND NEW.set_by IS NULL THEN
        SET NEW.set_by = USER();
    END IF;
END
        ACTION_ORIENTATION: ROW
             ACTION_TIMING: BEFORE
ACTION_REFERENCE_OLD_TABLE: NULL
ACTION_REFERENCE_NEW_TABLE: NULL
  ACTION_REFERENCE_OLD_ROW: OLD
  ACTION_REFERENCE_NEW_ROW: NEW
                   CREATED: 2020-05-03 08:57:27.57
                  SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
                   DEFINER: mysql.sys@localhost
      CHARACTER_SET_CLIENT: utf8mb4
      COLLATION_CONNECTION: utf8mb4_0900_ai_ci
        DATABASE_COLLATION: utf8mb4_0900_ai_ci
*************************** 2. row ***************************
           TRIGGER_CATALOG: def
            TRIGGER_SCHEMA: sys
              TRIGGER_NAME: sys_config_update_set_user
        EVENT_MANIPULATION: UPDATE
      EVENT_OBJECT_CATALOG: def
       EVENT_OBJECT_SCHEMA: sys
        EVENT_OBJECT_TABLE: sys_config
              ACTION_ORDER: 1
          ACTION_CONDITION: NULL
          ACTION_STATEMENT: BEGIN
    IF @sys.ignore_sys_config_triggers != true AND NEW.set_by IS NULL THEN
        SET NEW.set_by = USER();
    END IF;
END
        ACTION_ORIENTATION: ROW
             ACTION_TIMING: BEFORE
ACTION_REFERENCE_OLD_TABLE: NULL
ACTION_REFERENCE_NEW_TABLE: NULL
  ACTION_REFERENCE_OLD_ROW: OLD
  ACTION_REFERENCE_NEW_ROW: NEW
                   CREATED: 2020-05-03 08:57:27.75
                  SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
                   DEFINER: mysql.sys@localhost
      CHARACTER_SET_CLIENT: utf8mb4
      COLLATION_CONNECTION: utf8mb4_0900_ai_ci
        DATABASE_COLLATION: utf8mb4_0900_ai_ci
*************************** 3. row ***************************
           TRIGGER_CATALOG: def
            TRIGGER_SCHEMA: data
              TRIGGER_NAME: customer_state
        EVENT_MANIPULATION: INSERT
      EVENT_OBJECT_CATALOG: def
       EVENT_OBJECT_SCHEMA: data
        EVENT_OBJECT_TABLE: customers
              ACTION_ORDER: 1
          ACTION_CONDITION: NULL
          ACTION_STATEMENT: BEGIN
UPDATE Customers
SET cust_state = Upper(cust_state)
WHERE Customers.cust_id = INSERT.cust_id;
END
        ACTION_ORIENTATION: ROW
             ACTION_TIMING: BEFORE
ACTION_REFERENCE_OLD_TABLE: NULL
ACTION_REFERENCE_NEW_TABLE: NULL
  ACTION_REFERENCE_OLD_ROW: OLD
  ACTION_REFERENCE_NEW_ROW: NEW
                   CREATED: 2020-05-11 12:35:50.24
                  SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
                   DEFINER: root@localhost
      CHARACTER_SET_CLIENT: utf8mb4
      COLLATION_CONNECTION: utf8mb4_0900_ai_ci
        DATABASE_COLLATION: utf8mb4_0900_ai_ci
3 rows in set (0.00 sec)

查看指定的某一个触发器的信息,比如查询我的触发器customer_state,使用SELECT * FROM information_schema.triggers WHERE TRIGGER_NAME=‘customer_state’ \G;语句进行查询

mysql> SELECT * FROM information_schema.triggers WHERE TRIGGER_NAME='customer_state' \G;
*************************** 1. row ***************************
           TRIGGER_CATALOG: def
            TRIGGER_SCHEMA: data
              TRIGGER_NAME: customer_state
        EVENT_MANIPULATION: INSERT
      EVENT_OBJECT_CATALOG: def
       EVENT_OBJECT_SCHEMA: data
        EVENT_OBJECT_TABLE: customers
              ACTION_ORDER: 1
          ACTION_CONDITION: NULL
          ACTION_STATEMENT: BEGIN
UPDATE Customers
SET cust_state = Upper(cust_state)
WHERE Customers.cust_id = INSERT.cust_id;
END
        ACTION_ORIENTATION: ROW
             ACTION_TIMING: BEFORE
ACTION_REFERENCE_OLD_TABLE: NULL
ACTION_REFERENCE_NEW_TABLE: NULL
  ACTION_REFERENCE_OLD_ROW: OLD
  ACTION_REFERENCE_NEW_ROW: NEW
                   CREATED: 2020-05-11 12:35:50.24
                  SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
                   DEFINER: root@localhost
      CHARACTER_SET_CLIENT: utf8mb4
      COLLATION_CONNECTION: utf8mb4_0900_ai_ci
        DATABASE_COLLATION: utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

4数据库安全

对于组织来说,没有什么比它的数据更重要了,因此应该保护这些数据,使其不被偷到或任意浏览。

需要保护的操作有:

  • 对数据库管理功能(创建表、更改或删除已存在的表等)的访问;
  • 对特定数据库或表的访问
  • 访问的类型(只读、对特定列的访问等);
  • 仅通过视图或存储过程对表进行访问
  • 创建多层次的安全措施,从而允许多种基于登录的反问和控制
  • 限制管理用户账号的能力

安全性使用SQL的GRANT和REVOKE语句来管理,不过大多数DBMS提供了交互式的管理使用程序,这些使用程序在内部使用GRANT和REVOKE语句。

你可能感兴趣的:(数据库(SQL))