使用MySQL

# 选择数据库
USE database_name;
# 显示数据库
SHOW DATABASES;
# 显示当前数据库的表
SHOW TABLES;
# 显示特定表有哪些列
SHOW COLUMNS FROM customers;

检索数据

# SELECT语句
SELECT prod_name FROM products;
SELECT prod_id,prod_name,prod_price FROM products;
SELECT * FROM products;
# 配合DISTINCT检索不同的行
SELECT DISTINCT vend_id FROM products;
# 配合LIMIT限制结果
SELECT prod_name FROM products LIMIT 5;
SELECT prod_name FROM products LIMIT 5,5;
# 完全限定的表名或列名
限定列名:SELECT products.prod_name FROM products;
限定表名:SELECT products.prod_name FROM crashcourse.products;

排序

# 单列排序
SELECT prod_name FROM products ORDER BY prod_name;
# 多列排序
SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price, prod_name;
# 降序
SELECT prod_id, prod_price, price_name FROM products ORDER BY prod_price DESC;
# 升序和降序组合
SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC, prod_name;
# ORDER BY和LIMIT组合(WHERE在ORDER BY之前,ORDER BY在LIMIT之前)
SELECT prod_price FROM products ORDER BY prod_price DESC LIMIT 1;

过滤数据1(WHERE)

SELECT prod_name, prod_price FROM products WHERE prod_price = 2.5;
SELECT prod_name, prod_price FROM prod_price BETWEEN 5 AND 10;
# 空值检索
SELECT prod_name FROM products WHERE prod_price IS NULL;
# AND操作符
SELECT prod_id, prod_price, prod_name FROM products WHERE vend_id = 1003 AND prod_price <= 10;
# OR操作符
SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR vend_id = 1003;
# AND优先级比OR要高
SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR vend_id = 1003 AND prod_price >= 10;
上句相当于:SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR (vend_id = 1003 AND prod_price >= 10);
# IN操作符
SELECT prod_name, prod_price FROM products WHERE vend_id IN (1002,1003) ORDER BY prod_name;
# NOT操作符
SELECT prod_name, prod_price FROM products WHERE vend_id NOT IN (1002,1003) ORDER BY prod_name;

过滤数据2(LIKE配合通配符)

# %通配符(表示任意字符出现任意次数)
SELECT prod_id, prod_name FROM products WHERE prod_name LIKE 'jet%';
SELECT prod_id, prod_name FROM products WHERE prod_name LIKE '%anvil%';
# _通配符(表示单个任意字符)
SELECT prod_id, prod_name FROM products WHERE prod_name LIKE '_ ton anvil';

过滤数据3(正则表达式)

SELECT prod_id, prod_name FROM products WHERE prod_name REGEXP '1000' ORDER BY prod_name;
# .表示任意一个字符
SELECT prod_id, prod_name FROM products WHERE prod_name REGEXP '.000' ORDER BY prod_name;
# |类似于OR
SELECT prod_id, prod_name FROM products WHERE prod_name REGEXP '1000|2000' ORDER BY prod_name;
# 匹配几个字符之一:[]
SELECT prod_id, prod_name FROM products WHERE prod_name REGEXP '[123] Ton' ORDER BY prod_name;
# 匹配范围
SELECT prod_id, prod_name FROM products WHERE prod_name REGEXP '[1-5] Ton' ORDER BY prod_name;
# 匹配特殊字符(用\\进行转义)
SELECT vend_name FROM vendors WHERE vend_name REGEXP '\\.' ORDER BY vend_name;
# 匹配重复字符
SELECT prod_name FROM products WHERE prod_name REGEXP '\\([0-9] sticks?\\)' ORDER BY prod_name;
SELECT prod_name FROM products WHERE prod_name REGEXP '[[:digit:]]{4}' ORDER BY prod_name;
# 定位元字符
SELECT prod_name FROM products WHERE prod_name REGEXP '^[0-9\\.]' ORDER BY prod_name;

使用计算字段

# 拼接字段
SELECT Concat(vend_name, '(', vend_country, ')') FROM vendors ORDER BY vend_name;
# (别名)
SELECT Concat(vend_name, '(', vend_country, ')') AS vend_title FROM vendors ORDER BY vend_name;
# 算术计算
SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price FROM orderitems WHERE order_num = 20005;

使用数据处理函数

# 字符处理函数
SELECT vend_name, Upper(vend_name) AS vend_name_upcase FROM vendors ORDER BY vend_name;
SELECT cust_name, cust_contact FROM customers WHERE customers WHERE Soundex(cust_contact) = Soundex('Y Lie');
# 日期处理函数
SELECT cust_id, order_num FROM orders WHERE Date(order_date) = '2005-09-01';
SELECT cust_id, order_num FROM orders WHERE Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30';
SELECT cust_id, order_num FROM orders WHERE Year(order_date) = 2005 AND Month(order_date) = 9;

汇总数据(通过函数)

# AVG()函数
SELECT AVG(prod_price) AS avg_price FROM products;
SELECT AVG(prod_price) AS avg_price FROM products WHERE vend_id = 1003;
# COUNT()函数
SELECT COUNT(*) AS num_cust FROM customers;
SELECT COUNT(cust_email) AS num_cust FROM customers;
# MAX()函数
SELECT MAX(prod_price) AS max_price FROM products;
# MIN()函数
SELECT MIN(prod_price) AS min_price FROM products;
# SUM()函数
SELECT SUM(quantity) AS items_ordered FROM orderitems WHERE order_num = 20005;
SELECT SUM(item_price*quantity) AS total_price FROM orderitems WHERE order_num = 20005;
# 配合DISTINCT使用
SELECT AVG(DISINCT prod_price) AS avg_price FROM products WHERE vend_id = 1003;

数据分组

# 创建分组:GROUP BY
SELECT  vend_id, COUNT(*) AS num_prods FROM products GROUP BY vend_id;
# 过滤分组
SELECT cust_id, COUNT(*) AS orders FROM orders GROUP BY cust_id HAVING COUNT(*) >= 2;
# 不要依赖分组的默认排序,直接用ORDER BY
SELECT order_num, SUM(QUANTITY*item_price) AS ordertotal FROM orderitems GROUP BY order_num HAVING SUM(quantity*item_price) >= 50 ORDER BY ordertotal;

使用自查询

# 使用自查询进行过滤
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 = 'TNT2'));
# 作为计算字段使用自查询
SELECT cust_name, cust_state, (SELECT COUNT(*) FROM orders WHERE orders.cust_id = customers.cust_id) AS orders FROM customers ORDER BY cust_name;

表联结(联结有点不好理解--这章和下章)

# 创建联结
SELECT vend_name, prod_name, prod_price FROM vendors, products WHERE vendors.vend_id = products.vend_id ORDER BY vend_name, prod_name;
# 内部联结(返回数据和上个语句相同,只不过表达形式不同)
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 = 20005;

高级联结

# 使用表别名
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 = 'TNT2';
# 自联结
SELECT p1.prod_id, p1.prod_name FROM products AS p1, products AS p2 WHERE p1.vend_id = p2.vend_id AND p2.prod_id = 'DTNTR';
# 外部联结
SELECT customers.cust_id, orders.order_num FROM customers LEFT OUTER JOIN orders ON customers.cust_id = orders.cust_id;
SELECT customers.cust_id, orders.order_num FROM customers RIGHT OUTER JOIN orders ON customers.cust_id = orders.cust_id;
# 使用了聚集函数的联结
SELECT customers.cust_name, 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 vend_id, prod_id, prod_price FROM products WHERE prod_price <= 5 
UNION 
SELECT vend_id, prod_id, prod_price FROM products WHERE vend_id IN (1001,1002);
# 返回所有行(UNION ALL)
SELECT vend_id, prod_id, prod_price FROM products WHERE prod_price <= 5 
UNION ALL 
SELECT vend_id, prod_id, prod_price FROM products WHERE vend_id IN (1001,1002);

全文本搜素(需要索引,MyISAM支持,InnoDB不支持)

# 启用全文本索引支持(创建表时,或之后也可以)
CREATE TABLE test 
    (note_id  int NOT NULL AUTO_INCREMENT,   
    prod_id  char(10)  NOT NULL,   
    note_date datetime  NOT NULL,   
    note_text text NULL, 
    PRIMARY KEY(note_id),   FULLTEXT(note_text) 
) ENGINE=MyISAM;
# 使用全文本搜索
SELECT note_text FROM productnotes WHERE Match(note_text) Against('effective');
SELECT note_text, Match(note_text) Against('rabbit') AS rank FROM productnotes;
# 使用扩展查询(会返回更多相关行)
SELECT note_text FROM productnotes WHERE Match(note_text) Against('anvils' WITH QUERY EXPANSION);
# 布尔文本搜索(和全文本搜索不同,不需要数据库支持,但比较慢)
SELECT note_text FROM productnotes WHERE Match(note_text) Against('heavy' IN BOOLEAN MODE);
SELECT note_text FROM productnotes WHERE Match(note_text) Against('heavy -rope*' IN BOOLEAN MODE);

插入语句

# 插入单行
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) 
VALUES
(NULL, 'Pep E. LaPew', '100 Main Street', 'Los Angeles', 'CA', '90046', '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
(NULL, 'Pep E. LaPew', '100 Main Street', 'Los Angeles', 'CA', '90046', 'USA', NULL, NULL),
('M. Martian', '42 Galaxy Way', 'New York', 'NY', '11213', 'USA');
# 插入索引出的数据(有点类似于复制)
INSERT INTO custormers(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 customers SET cust_email = 'elmer@fudd.com' WHERE cust_id = 10005;
# 更新多个列
UPDATE customers SET cust_name = 'The Fudds',  cust_email = 'elmer@fudd.com' WHERE cust_id = 10005;
# 删除某个列的值
UPDATE customers SET cust_email = NULL WHERE cust_id = 10005;
# 删除数据
DELETE FROM customers WHERE cust_id = 10006;

创建和操纵表

# 创建表
CREATE TABLE customers 
(
    cust_id int NOT NULL AUTO_INCREMENT, 
    cust_name char(50) NOT NULL, 
    cust_city char(50) NULL, 
    cust_state char(5) NULL, 
    cust_zip char(10) NULL, 
    cust_country char(50) NULL,
    cust_contact char(50) NULL, 
    cust_email char(255) NULL, 
    PRIMARY KEY (cust_id)
) ENGINE=InnoDB;
// 多个列组成的主键
CREATE TABLE orderitems
(
    order_num int NOT NULL, 
    order_item int NOT NULL, 
    prod_id char(10) NOT NULL, 
    quantity int NOT NULL, 
    item_price decimal(8.2) NOT NULL, 
    PRIMARY KEY(order_num, order_item)
) ENGINE=InnoDB;
// 指定默认值
CREATE TABLE orderitems 
(
    order_num int NOT NULL, 
    order_item int NOT NULL, 
    prod_id char(10) NOT NULL, 
    quantity int NOT NULL DEFAULT 1, 
    item_price decimal(8,2) NOT NULL, 
    PRIMARY KEY (order_num, order_item)
) ENGINE=InnoDB;

# 更新表
// 增加列
ALTER TABLE vendors ADD vend_phone CHAR(20);
// 删除列
ALTER TABLE vendors DROP COLUMN vend_phone;
// 增加外键
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num);

# 删除表
DROP TABLE customers2;

# 重命名表
RENAME TABLE customers2 TO customers; 
RENAME TABLE backup_customers TO customers, backup_vendors TO vendors, backup_products  TO products;

视图

# 创建视图
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;

存储过程

# 创建存储过程
CREATE PROCEDURE productpricing() 
BEGIN 
SELECT avg(prod_price) AS priceaverage FROM products; 
END;
-- 调用存储过程
CALL productpricing();
# 删除存储过程
DROP PROCEDURE productpricing;

# 使用参数
CREATE DEFINER=`root`@`localhost` 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;

# 一个复杂的例子
CREATE DEFINER=`root`@`localhost` PROCEDURE `ordertotal`(
    IN onumber INT,
    IN taxable BOOLEAN,
    OUT ototal DECIMAL(8,2)
)
    COMMENT 'Obtain order total, optionally adding tax'
BEGIN
    -- Declare variable for total
    DECLARE total DECIMAL(8,2);
    -- Declare tax percentage
    DECLARE taxrate INT DEFAULT 6;
    
    -- Get the order total
    SELECT Sum(item_price*quantity) FROM orderitems
    WHERE order_num = onumber INTO total;
    
    -- Is this taxable?
    IF taxable THEN
        SELECT total+(total/100*taxrate) INTO total;
    END IF;
    
    -- And finally, save to out variable
    SELECT total INTO ototal;
END;
-- 调用
CALL ordertotal(20005, 0, @total);
-- 使用
SELECT @total;

# 显示存储过程的信息
SHOW CREATE PROCEDURE ordertotal;
SHOW PROCEDURE STATUS LIKE 'ordertotal';

使用游标

CREATE DEFINER=`root`@`localhost` PROCEDURE `processorders`()
BEGIN
    -- Declare local variables
    DECLARE done BOOLEAN DEFAULT 0;
    DECLARE o INT;
    DECLARE t DECIMAL(8,2);
    
    -- 创建游标 Declare the cursor
    DECLARE ordernumbers CURSOR
    FOR
    SELECT order_num FROM orders;
    
    -- 创建句柄 Declare continue handler
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;

    -- Create a table to store the results
    CREATE TABLE IF NOT EXISTS ordertotals
		(order_num INT, total DECIMAL(8,2));
    
    -- 打开游标 Open the cursor
    OPEN ordernumbers;
    -- 循环语句 Loop through all rows
    REPEAT
	-- 获取数据 Get order number
	FETCH ordernumbers INTO o;
        -- Get the total for this order
        CALL ordertotal(o, 1, t);
        -- Insert order the total into ordertotals
        INSERT INTO ordertotals(order_num, total)
			VALUES(o, t);
    -- End of loop
    UNTIL done END REPEAT;
    
    -- Close the cursor
    CLOSE ordernumbers;
END;

# 调用包含游标的存储过程
CALL processorders;
SELECT * FROM ordertotals;

使用触发器

# 创建触发器
-- 在更新或删除时可以使用old虚拟表来访问之前的值
-- 在更新或插入时可以使用new虚拟表来访问新的值
DROP TRIGGER IF EXISTS t_afterinsert_on_tab1;
CREATE TRIGGER t_afterinsert_on_tab1 
AFTER INSERT ON tab1
FOR EACH ROW
BEGIN
    INSERT INTO tab2(tab2_id) VALUES(new.tab1_id);
END;

# 删除触发器
DROP TRIGGER t_afterinsert_on_tab1;

事务处理

# 回滚
SELECT * FROM ordertotals;
START TRANSACTION;
SET sql_safe_updates=0;
DELETE FROM ordertotals;
SELECT * FROM ordertotals;
ROLLBACK;
SELECT * FROM ordertotals;

# 保留点
SAVEPOINT delete1;
ROLLBACK TO delete1;

# 更改默认的提交行为(非事务语句默认自动提交)
SET autocommit=0;

账号及权限管理

# 查看账号
USE mysql;
SELECT user FROM user;
# 创建账号(无权限)
CREATE USER jesse IDENTIFIED BY 'password';
# 更新用户名
RENAME USER jesse TO sai;
# 删除账号
DROP USER sai;
# 显示账号的权限
SHOW GRANTS FOR jesse;
# 设置权限
GRANT SELECT ON cx.* TO jesse;
撤销权限
# REVOKE SELECT ON crashcourse.* FROM jesse;
更改密码
SET PASSWORD FOR jesse = Password('n3w password');
-- 更改自己账号密码
SET PASSWORD = Password('n3w password');