MySQL 数据操作与表管理

  • 插入数据(INSERT)
  • 更新和删除数据(UPDATE/DELETE)
  • 创建和操纵表(CREATE/ALTER/DROP)
  • 使用视图
  • 存储过程
  • 游标
  • 触发器

插入数据

使用 INSERT 插入数据,要求指定表名和插入的值。

插入完整的行

INSERT INTO customers
VALUES(NULL, 'Pep E. LaPew', '100 Main Street', 'Los Angeles', 'CA', '90046', 'USA', NULL, NULL);

更安全的写法:明确给出列名,不依赖列的顺序。

INSERT INTO customers(cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES('Pep E. LaPew', '100 Main Street', 'Los Angeles', 'CA', '90046', 'USA', NULL, NULL);

插入多个行

INSERT INTO customers(cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country)
VALUES('Pep E. LaPew', '100 Main Street', 'Los Angeles', 'CA', '90046', 'USA'),
      ('M. Martian', '42 Galaxy Way', 'New York', 'NY', '11213', 'USA');

插入检索出的数据INSERT SELECT 将 SELECT 的结果插入表中。

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;

更新和删除数据

更新数据:使用 UPDATE 语句,由三部分组成:要更新的表、列名和它们的新值、确定要更新行的过滤条件。

-- 更新单个列
UPDATE customers SET cust_email = 'elmer@fudd.com' WHERE cust_id = 10005;

-- 更新多个列
UPDATE customers SET cust_contact = 'Sam Roberts', cust_email = 'sam@toyland.com' WHERE cust_id = 10005;

不要省略 WHERE 子句,否则会更新表中所有行。

删除数据:使用 DELETE 语句。

DELETE FROM customers WHERE cust_id = 10006;

DELETE 删除整行而不是列,要删除特定的列用 UPDATE。不要省略 WHERE 子句。

删除表的所有行TRUNCATE TABLE 比 DELETE 更快(实际上是删除原来的表并重新建一个空表)。

TRUNCATE TABLE customers;

更新和删除的指导原则

  • 除非确实打算更新和删除每一行,否则绝对不要使用不带 WHERE 子句的 UPDATE 或 DELETE
  • 保证每个表都有主键
  • 在对 UPDATE 或 DELETE 使用 WHERE 子句前,应该先用 SELECT 进行测试
  • 使用强制实施引用完整性的数据库,这样 MySQL 将不允许删除具有与其他表相关联的数据的行

创建和操纵表

创建表:使用 CREATE TABLE,必须指定表名、列名和列的数据类型。

CREATE TABLE customers (
    cust_id int NOT NULL AUTO_INCREMENT,
    cust_name char(50) NOT NULL,
    cust_address char(50) NULL,
    cust_city char(50) NULL,
    cust_state char(5) NULL,
    cust_zip char(10) NULL,
    cust_country char(50) NULL DEFAULT 'USA',
    cust_contact char(50) NULL,
    cust_email char(255) NULL,
    PRIMARY KEY (cust_id)
) ENGINE=InnoDB;

NULL 值:每个列要么是 NULL 列,要么是 NOT NULL 列。NULL 为默认设置。不要把 NULL 与空串混淆,空串是有效的值。

主键:用 PRIMARY KEY 指定。多列主键以逗号分隔的列表给出各列名。主键中只能使用不允许 NULL 值的列。

AUTO_INCREMENT:每当增加一行时自动增量,每个表只允许一个 AUTO_INCREMENT 列,且必须被索引。使用 last_insert_id() 获得最后一个 AUTO_INCREMENT 值。

指定默认值:用 DEFAULT 关键字,MySQL 不允许使用函数作为默认值,只支持常量。

引擎类型

  • InnoDB:可靠的事务处理引擎,不支持全文本搜索
  • MyISAM:性能极高的引擎,支持全文本搜索,不支持事务处理
  • MEMORY:数据存储在内存中,速度很快,适合临时表

引擎类型可以混用,但外键不能跨引擎。

更新表:使用 ALTER TABLE。使用时要极为小心,应该先做完整备份。

-- 添加列
ALTER TABLE vendors ADD vend_phone CHAR(20);

-- 删除列
ALTER TABLE vendors DROP COLUMN vend_phone;

-- 定义外键
ALTER TABLE products ADD CONSTRAINT fk_products_vendors
FOREIGN KEY (vend_id) REFERENCES vendors (vend_id);

删除表DROP TABLE,永久删除,不能撤销。

DROP TABLE customers2;

重命名表

RENAME TABLE customers2 TO customers;
RENAME TABLE backup_customers TO customers,
             backup_vendors TO vendors;

使用视图

视图是虚拟的表,只包含使用时动态检索数据的查询。

视图的用途

  • 重用 SQL 语句
  • 简化复杂的 SQL 操作
  • 使用表的组成部分而不是整个表
  • 保护数据
  • 更改数据格式和表示

视图的规则和限制

  • 与表一样,视图必须唯一命名
  • 视图不能索引,也不能有关联的触发器或默认值
  • 视图可以和表一起使用(可以联结视图和表)

创建视图

-- 创建将客户与邮件列表绑定的视图
CREATE VIEW customeremaillist AS
SELECT cust_id, cust_name, cust_email FROM customers WHERE cust_email IS NOT NULL;

-- 使用视图
SELECT * FROM customeremaillist;

-- 用视图重新格式化检索出的数据
CREATE VIEW vendorlocations AS
SELECT Concat(RTrim(vend_name), ' (', RTrim(vend_country), ')') AS vend_title
FROM vendors ORDER BY vend_name;

-- 用视图过滤不想要的数据
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;

查看视图

SHOW CREATE VIEW productcustomers;

删除视图

DROP VIEW productcustomers;

更新视图CREATE OR REPLACE VIEW,如果视图存在则替换,不存在则创建。并非所有视图都可更新(使用了分组、联结、聚集函数、DISTINCT 等的视图通常不可更新)。

存储过程

存储过程就是为以后使用而保存的一条或多条 MySQL 语句的集合,可视为批文件。

创建存储过程

CREATE PROCEDURE productpricing()
BEGIN
    SELECT Avg(prod_price) AS priceaverage FROM products;
    SELECT Min(prod_price) AS pricemin FROM products;
    SELECT Max(prod_price) AS pricemax FROM products;
END;

MySQL 命令行使用分号作为语句分隔符,如果存储过程体内有分号会导致语法错误。解决方法是临时更改分隔符:

DELIMITER //
CREATE PROCEDURE productpricing()
BEGIN
    SELECT Avg(prod_price) AS priceaverage FROM products;
END //
DELIMITER ;

调用存储过程

CALL productpricing();

删除存储过程

DROP PROCEDURE productpricing;
DROP PROCEDURE IF EXISTS productpricing;

使用参数:存储过程可以接收参数、返回参数。变量以 @ 开头。

CREATE PROCEDURE productpricing(
    OUT pl DECIMAL(8,2),
    OUT ph DECIMAL(8,2),
    OUT pa DECIMAL(8,2)
)
BEGIN
    SELECT Min(prod_price) INTO pl FROM products;
    SELECT Max(prod_price) INTO ph FROM products;
    SELECT Avg(prod_price) INTO pa FROM products;
END;

CALL productpricing(@pricelow, @pricehigh, @priceaverage);
SELECT @pricehigh, @pricelow, @priceaverage;

智能存储过程:包含业务规则和智能处理的存储过程。

游标

游标是一个存储在 MySQL 服务器上的数据库查询,检索出结果集后,应用程序可以滚动或浏览其中的数据。游标只能用于存储过程和函数。

创建游标DECLARE 语句定义游标,OPEN 打开,FETCH 访问每一行,CLOSE 关闭。

CREATE PROCEDURE processorders()
BEGIN
    DECLARE done BOOLEAN DEFAULT 0;
    DECLARE o INT;
    DECLARE t DECIMAL(8,2);
    DECLARE ordernumbers CURSOR FOR SELECT order_num FROM orders;
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;

    CREATE TABLE IF NOT EXISTS ordertotals(order_num INT, total DECIMAL(8,2));

    OPEN ordernumbers;
    REPEAT
        FETCH ordernumbers INTO o;
        CALL ordertotal(o, 1, t);
        INSERT INTO ordertotals(order_num, total) VALUES(o, t);
    UNTIL done END REPEAT;
    CLOSE ordernumbers;
END;

DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' 是在游标到达末尾时设置 done 为 1 的条件处理。

触发器

触发器是 MySQL 响应以下任意语句而自动执行的一条 MySQL 语句(或位于 BEGIN 和 END 语句之间的一组语句):DELETE、INSERT、UPDATE。其他 SQL 语句不支持触发器。

创建触发器:需要唯一的触发器名、关联的表、响应的活动(DELETE/INSERT/UPDATE)、何时执行(BEFORE/AFTER)。

CREATE TRIGGER newproduct AFTER INSERT ON products
FOR EACH ROW SELECT 'Product added';

每个表每个事件只允许一个触发器,每表最多 6 个触发器(每条 INSERT/UPDATE/DELETE 的 BEFORE 和 AFTER)。

INSERT 触发器:可以引用名为 NEW 的虚拟表访问被插入的行。BEFORE INSERT 中 NEW 的值可以被更新。AUTO_INCREMENT 列,NEW 在 INSERT 执行后包含新的自动生成值。

CREATE TRIGGER neworder AFTER INSERT ON orders
FOR EACH ROW SELECT NEW.order_num;

DELETE 触发器:可以引用名为 OLD 的虚拟表访问被删除的行。OLD 的值全部是只读的。

CREATE TRIGGER deleteorder BEFORE DELETE ON orders
FOR EACH ROW
BEGIN
    INSERT INTO archive_orders(order_num, order_date, cust_id)
    VALUES(OLD.order_num, OLD.order_date, OLD.cust_id);
END;

UPDATE 触发器:可以引用 OLD 访问以前的值,引用 NEW 访问新更新的值。BEFORE UPDATE 中 NEW 的值可以被更新。

-- 确保州名缩写总是大写
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors
FOR EACH ROW SET NEW.vend_state = Upper(NEW.vend_state);

删除触发器

DROP TRIGGER newproduct;