手写MySQL常用语法

  • Post author:
  • Post category:mysql




01 准备工作

书籍:《MySQL必知必会》

下载:数据库搭建需要的基本数据

跳转


安装:本地安装好mysql,有必要的装一个navicat for mysql(别的可视化工具也行)不会的自行百度

创建好数据库之后执行create.sql和populate.sql创建需要的数据库表并注入数据。

本博客配套源码:

点击这里跳转



02 了解你的数据库

-- 展示当前连接下的所有数据库,包含了系统数据库
SHOW DATABASES;
-- 使用名为mysqldemo的数据库
USE mysqldemo;
-- 展示当前数据库下的所有表
SHOW TABLES;
-- 展示customers表中的列信息,下面两句是等价的
SHOW COLUMNS FROM customers;
DESCRIBE customers;
-- 显示服务器状态信息
SHOW STATUS;
-- 显示我的mysqldemo数据库的创建信息
SHOW CREATE DATABASE mysqldemo;
-- 显示当前数据库下customers表的创建信息
SHOW CREATE TABLE customers;
-- 显示所有用户的权限信息
SHOW GRANTS;
-- 显示服务器错误
SHOW ERRORS;
-- 显示服务器警告
SHOW WARNINGS;



03 检索数据

-- 查询一列
SELECT `cust_name` FROM `customers`;
-- 查询多列
SELECT `cust_id`,`cust_name`,`cust_country` FROM `customers`;
-- 检索所有列的快捷方式,不建议使用,会降低检索和应用程序的性能
SELECT * FROM `customers`;
-- 只显示不同的值,使用distinct关键字
SELECT DISTINCT `cust_country` FROM `customers`;
-- distinct将综合考虑后面所有的列,而不仅仅是distinct后面第一列
-- 举例:cust_country和cust_name中只要有有一个值不同,都被distinct认为是不同的
SELECT DISTINCT `cust_country`,`cust_name` FROM `customers`;
-- 返回前5行
SELECT `order_num` FROM `orderitems` LIMIT 5;
-- 从第2行(从0开始编号的)开始,返回后面5行
-- 没有足够的行将会截断
SELECT `order_num` FROM `orderitems` LIMIT 2,5;
-- 上一句的等价写法
SELECT `order_num` FROM `orderitems` LIMIT 2 OFFSET 5;
-- 使用完全限定的表名、列名,可以用来应对比如一些重名的情况
-- 注意,我这里给表名、列名加上``符号会报错,这种情况不能加引用符号
SELECT orderitems.order_num FROM mysqldemo.orderitems;



04 对数据排序

-- 根据单列排序,默认是升序
SELECT prod_id,note_text FROM productnotes
ORDER BY prod_id;
-- 根据多列排序,将按照列的顺序进行
SELECT prod_id,note_date,note_text FROM productnotes
ORDER BY prod_id,note_date;
-- 可指定排序是升序还是降序DESC,默认是升序ASC
-- 一个关键字仅对一个列生效
SELECT prod_id,note_date,note_text FROM productnotes
ORDER BY prod_id DESC,note_date ASC;
-- 一个小应用:找出单价最高的产品
SELECT prod_name,prod_price FROM products
ORDER BY prod_price DESC
LIMIT 1;



05 使用where进行数据筛选

-- where不区分大小写
-- =等于
SELECT * FROM customers WHERE cust_id=10001;
-- <>不等于
SELECT * FROM customers WHERE cust_id<>10001;
-- !=不等于,和上一句等价
SELECT * FROM customers WHERE cust_id!=10001;
-- <小于
SELECT * FROM customers WHERE cust_id<10003;
-- <=小于等于
SELECT * FROM customers WHERE cust_id<=10003;
-- >大于
SELECT * FROM customers WHERE cust_id>10003;
-- >=大于等于
SELECT * FROM customers WHERE cust_id>=10003;
-- between范围,包含边界
SELECT * FROM customers WHERE cust_id BETWEEN 10001 AND 10003;
-- 为空检查
SELECT * FROM vendors WHERE vend_state IS NULL;
-- 不为空检查
SELECT * FROM vendors WHERE vend_state IS NOT NULL;



06 组合where子句

-- AND且
SELECT vend_id,vend_name FROM vendors
WHERE vend_id > 1003 AND vend_id < 1005;
-- OR或
SELECT vend_id,vend_name FROM vendors
WHERE vend_id < 1003 OR vend_id > 1005;
-- 组合多条,AND优先于OR
SELECT vend_id,vend_name FROM vendors
WHERE (vend_id > 1003 OR vend_id < 1005) AND vend_id = 1004;
-- IN指定范围
SELECT vend_id,vend_name FROM vendors
WHERE vend_id IN (1001,1003,1004);
-- NOT表示非
SELECT vend_id,vend_name FROM vendors
WHERE vend_id NOT IN (1001,1003);



07 使用通配符

-- 最常使用的%表示任意字符出现任意次数,包括0次
-- mysql的匹配默认是不区分大小写的,可以通过设置使它区分大小写
-- 找出所有cust_name以c开头的记录
SELECT * FROM customers WHERE cust_name LIKE 'c%';
-- 匹配所有cust_name包含c的记录
SELECT * FROM customers WHERE cust_name LIKE '%c%';
-- 匹配所有cust_name包含以w开头s结尾的记录
SELECT * FROM customers WHERE cust_name LIKE 'w%s';
-- 下划线_匹配单个字符
-- 匹配所有cust_name为?ascals的记录
SELECT * FROM customers WHERE cust_name LIKE '_ascals';



08 正则表达式

-- 正则表达式用来匹配文本特殊的串,使用关键字REGEXP
-- REGEXP匹配列中的值,LIKE匹配整个列
-- 匹配prod_id包含0的记录
SELECT * FROM orderitems
WHERE prod_id REGEXP '0';
-- 小数点表示匹配任意一个字符
SELECT * FROM products
WHERE prod_name REGEXP '.on';
-- 竖线|表示OR匹配
SELECT * FROM products
WHERE prod_name REGEXP 'on|an';
-- 中括号[]表示匹配括号中任意一个字符,相当于另一种形式的OR
SELECT * FROM products
WHERE prod_name REGEXP '[ao]n';
-- 加上^表示否定
-- 表示除了an on以外的?n
SELECT * FROM products
WHERE prod_name REGEXP '[^ao]n';
-- 使用-表示范围
-- [2-6]表示匹配2、3、4、5、6中的一个
SELECT * FROM products
WHERE prod_name REGEXP '[2-6]';
-- 匹配特殊字符需要加上\\用于转义
-- 匹配小数点
SELECT * FROM products
WHERE prod_name REGEXP '\\.';
-- 匹配单斜杠\,书上说的是三个斜杠,但我的mysql要写四个斜杠
SELECT * FROM products
WHERE prod_name REGEXP '\\\\';
-- 还有一些空白元字符:\\f表示换页,\\n表示换行,\\r表示回车,\\t表示制表,\\v表示纵向制表
-- 可以自行尝试

-- 匹配预定义的字符集,即字符类
-- [:alnum:]表示任意字母和数字,相当于[a-zA-Z0-9]
-- [:alpha:]表示任意字母,相当于[a-zA-Z]
-- [:blank:]表示空格和制表符,相当于[\\t]
-- [:cntrl:]表示ASCII控制字符
-- [:digit:]表示任意数字
-- [:lower:]表示任意小写字母
-- [:print:]表示任意可打印字符
-- [:punct:]表示既不在[:alnum:]又不在[:cntrl:]中的任意字符
-- [:space:]表示任意空白字符,等同于[\\f\\n\\r\\t\\v]
-- [:upper:]表示任意大写字母
-- [:xdigit:]表示任意十六进制数字,相当于[a-fA-F0-9]

-- 下面的重复元字符挺重要的
-- ?表示0个或1个匹配,下面这条语句匹配stick和sticks
SELECT * FROM products 
WHERE prod_name REGEXP 'sticks?';
-- {n,}匹配指定数目的匹配
-- 匹配连续出现的三个0
SELECT * FROM products 
WHERE prod_name REGEXP '[0]{3}';
-- {n,}匹配不少于指定数目的匹配
-- 匹配至少有一个连续的0
SELECT * FROM products 
WHERE prod_name REGEXP '[0]{1,}';
-- {n,m}匹配连续出现n~m次
-- 匹配至少有一个连续的0
SELECT * FROM products 
WHERE prod_name REGEXP '[0]{1,2}';
-- *匹配0~任意个重复字符
SELECT * FROM products 
WHERE prod_name REGEXP '[0]*';
-- +匹配一个或多个重复字符
SELECT * FROM products 
WHERE prod_name REGEXP '[0]+';

-- 定位符
-- ^表示文本的开始,注意,这个符号也可以用来表示否定,在上面已经说过了
-- 匹配值以数字或小数点开始的记录
SELECT * FROM products 
WHERE prod_name REGEXP '^[0-9\\.]';
-- $表示文本的结尾
-- 匹配值以l结尾的记录
SELECT * FROM products 
WHERE prod_name REGEXP '[l]$';



09 计算字段

-- 拼接字段Concat函数
-- 使用AS关键字可以创建别名
-- 可以对字段进行+-*/
SELECT Concat(prod_id,'(',item_price*quantity,'$)') AS '总价'
FROM orderitems;
-- 删除右侧空格RTrim函数,此外还有删除左空格LTrim、删除两端空格Trim
SELECT Concat(RTrim(prod_id),'(',item_price*quantity,'$)') AS '总价'
FROM orderitems;
-- 可以省略FROM子句,仅仅是计算表达式
SELECT 1*10;



10 使用函数

-- 文本处理函数
-- Upper将文本全部转换为大写
SELECT Upper(prod_name) FROM products;
-- 此外还有:Left返回串左边的字符,Right返回串右边的字符,
-- Length返回串的长度,Locate找出串的一个子串的位置
-- Lower转换为小写,Upper转换为大写,LTrim去掉左边空格,RTrim去掉右边空格,Trim去掉两边空格
-- SubString返回指定位置的子串
-- Soundex返回串的读音

-- ton为子串,将返回子串在prod_name中的位置
SELECT Locate('ton',prod_name) FROM products;
-- 1表示从第一个字符开始,3表示偏移量为3
SELECT SubString(prod_name,1,3) FROM products;
-- Y Lee和Y Lie读音相同,将会被检索出来
SELECT * FROM customers
WHERE Soundex(cust_contact) = Soundex('Y lie');

-- 时间和日期格式
-- Mysql首选日期格式为yyyy-mm-dd
-- Date返回日期和时间的yyyy-mm-dd部分
SELECT * FROM orders
WHERE Date(order_date)='2005-09-01';
-- 支持范围查询
SELECT * FROM orders
WHERE Date(order_date) BETWEEN '2005-09-01' AND '2005-09-12';
-- Year返回日期和时间的年份
SELECT * FROM orders
WHERE Year(order_date)='2005';
-- Month返回日期和时间的月份,9或者09都能匹配
SELECT * FROM orders
WHERE Month(order_date)='9';
-- Day返回日期和时间的具体几号
SELECT * FROM orders
WHERE Day(order_date)='1';

-- 数值处理函数
-- Abs绝对值、Cos余弦三角函数、Exp指数、Mod求余、Pi返回圆周率、Rand返回一个随机数
-- Sin正弦三角函数、Sqrt平方根、Tan正切三角函数
SELECT Abs(-1);



11 汇总数据

-- SQL聚集函数:
-- AVG返回某列平均值,忽略值为NULL的行
SELECT AVG(item_price) AS avg_price FROM orderitems;
-- COUNT统计某列行数,COUNT(列名)忽略NULL行,COUNT(*)不忽略
SELECT COUNT(item_price) AS count FROM orderitems;
-- MAX最大值,忽略NULL
SELECT MAX(item_price) AS max_price FROM orderitems;
-- MIN最小值,忽略NULL
SELECT MIN(item_price) AS min_price FROM orderitems;
-- SUM求和,忽略NULL
SELECT SUM(item_price) AS sum_price FROM orderitems;

-- 对于以上所有函数,可在列名前使用DISTINCT,对该列仅处理非重复行
-- 以下两句将返回不同的结果
SELECT COUNT(item_price) AS count FROM orderitems;
SELECT COUNT(DISTINCT item_price) AS count FROM orderitems;



12 分组数据、子句顺序

-- 创建分组
-- 如果分组列中有NULL值,他们将会被分为一组
SELECT prod_id,COUNT(note_id) AS count 
FROM productnotes 
GROUP BY prod_id 
ORDER BY prod_id;

-- 过滤分组
-- where针对行过滤,而having针对组过滤,换句话说:where进行分组前的过滤,having进行分组后的过滤
SELECT prod_id,COUNT(note_id) AS count
FROM productnotes 
WHERE Date(note_date) >= '2005-8-20'
GROUP BY prod_id 
HAVING count>=2
ORDER BY prod_id;

-- select子句顺序:
SELECT prod_id,COUNT(note_id) AS count
FROM productnotes 
WHERE Date(note_date) >= '2005-8-20'
GROUP BY prod_id 
HAVING count>=1
ORDER BY count
LIMIT 6;



13 使用子查询

-- 子查询:组合查询语句
-- 子查询可以嵌套很多层,但是可读性差,性能不高
-- 查询order_item>=3的订单所涉及的产品详细信息
SELECT * FROM products WHERE prod_id 
IN(SELECT prod_id FROM orderitems WHERE order_item>=3);

-- 作为计算字段使用子查询
-- 产品详细信息,以及每个产品所涉及的order_item总数
SELECT prod_id,prod_name,prod_price, (SELECT COUNT(*) FROM orderitems WHERE orderitems.prod_id=products.prod_id) AS total
FROM products;



14 使用联结

-- 联结是一种机制,用来在一条SELECT语句中关联表
-- 相比嵌套的子查询,联结的可读性更好
-- 要用where子句进行过滤,否则会返回表之间的笛卡尔积

-- 内部联结/等值联结
-- 写法一
SELECT O.prod_id,P.prod_name,O.item_price FROM orderitems AS O,products AS P
WHERE O.prod_id=P.prod_id;
-- 写法二
SELECT O.prod_id,P.prod_name,O.item_price FROM orderitems AS O INNER JOIN products AS P
ON O.prod_id=P.prod_id;

-- 自联接:同一张表和自己的联结
-- 在product表中,查找prod_id为ANV01的记录涉及的vend_id,然后再查看这个vend_id所涉及的所有记录
-- 写法一:使用子查询
SELECT prod_id,vend_id,prod_name,prod_price FROM products WHERE vend_id IN
(SELECT vend_id FROM products WHERE prod_id='ANV01')
-- 写法二:使用自联接
SELECT p1.prod_id,p1.vend_id,p1.prod_name,p1.prod_price FROM products AS p1
INNER JOIN products AS p2
ON p1.vend_id=p2.vend_id AND p2.prod_id='ANV01';

-- 自然联结:没有重复列的联结,由写sql的人实现

-- 外部联结:一个表和另一个表联结,包含了没有关联行的那些行
-- 左外部联结:将左边表未被关联的行也包括进来
SELECT c.cust_name,count(o.order_num) AS order_count 
FROM customers AS c LEFT OUTER JOIN orders AS o
ON c.cust_id=o.cust_id
GROUP BY c.cust_name;
-- 右外部联结:将右边表未被关联的行也包括进来
SELECT c.cust_name,count(o.order_num) AS order_count 
FROM orders AS o RIGHT OUTER JOIN customers AS c
ON c.cust_id=o.cust_id
GROUP BY c.cust_name
ORDER BY order_count;



15 组合查询

-- 组合查询
-- UNION和使用子查询在不同情况下各有优劣

-- UNION使用规则:
-- UNION中的每个查询必须包含相同的列、表达式、聚集函数,顺序可以不一样
-- UNION将对结果自动去除重复的行,使用UNION ALL可以不去除重复的行

-- ORDER BY将对全部结果进行排序
SELECT order_num,prod_id,quantity FROM orderitems WHERE quantity=1
UNION
SELECT order_num,prod_id,quantity FROM orderitems WHERE quantity=50
ORDER BY order_num;



16 全文本搜索

-- 全文本搜索
-- MyISAM支持全文本搜索,InnoDB不支持
-- 使用全文本搜索必须索引被搜索的列,且要保持更新
-- 全文搜索由于使用了索引,所以速度相当快

-- 启用全文搜索:
-- 可以在创建表时启用
-- 也可以在已经创建表的情况下启用,所有数据会被立即索引
-- 不应该在导入数据时启用,因为更新操作会导致索引不断被更新
-- CREATE TABLE texttable(
-- 	text_id	int NOT NULL AUTO_INCREMENT,
-- 	text_content text NULL,
-- 	PRIMARY KEY(text_id),
-- 	FULLTEXT(text_content)
-- )ENGINE=MyISAM;

-- 使用全文搜索:
-- Match()指定要搜索的列,Against()指定搜索表达式
-- select的列和match的列必须完全一致,包括次序
-- 全文搜索忽略大小写
-- 全文搜索将对搜索结果根据相关性排序
SELECT note_text From productnotes
WHERE Match(note_text) Against('customer');

-- 使用查询扩展:
-- 先全文搜索出能匹配的行,然后在匹配上的行中选出有用的其他词,再进行一次全文搜索,将结果放入返回结果中
SELECT note_text From productnotes
WHERE Match(note_text) Against('customer' WITH QUERY EXPANSION);

-- 布尔文本搜索
-- 可以定义要匹配的词、要排斥的词、词的重要性等等
-- 匹配heavy但排除以rope开始的词
SELECT note_text From productnotes
WHERE Match(note_text) Against('customer -stick*' IN BOOLEAN MODE);
-- 全文本布尔操作符:
-- +包含,-排除,>包含且增加等级值,<包含且减少等级值,~取消一个词的排序值
-- *词尾的通配符,""定义一个短语,()把词组成子表达式
-- +表示必须包含
SELECT note_text From productnotes
WHERE Match(note_text) Against('+customer +stick*' IN BOOLEAN MODE);
-- 这种写法,只要含有一个词就能被检索到
SELECT note_text From productnotes
WHERE Match(note_text) Against('customer stick*' IN BOOLEAN MODE);



17 插入数据

-- 插入完整的行:这种写法依赖于表结构中列的特定次序,不安全
INSERT INTO products
VALUES('TRACY00',1001,'pencil00',0.25,'Carrots (rabbit hunting season only)');
-- 这种写法可以插入完整的行,也可以插入部分列的值,并且不依赖于表结构中列的顺序
INSERT INTO products(prod_id,vend_id,prod_name,prod_price,prod_desc)
VALUES('TRACY01',1001,'pencil01',0.45,'Carrots (rabbit hunting season only)');

-- 省略部分列应注意:该列允许为NULL,或者给出了默认值

-- 添加关键字LOW_PRIORITY
-- 有时间INSERT语句会很耗时,可以在INSERT和INTO之间添加LOW_PRIORITY关键字来降低INSERT语句的优先级
INSERT LOW_PRIORITY INTO products(prod_id,vend_id,prod_name,prod_price,prod_desc)
VALUES('TRACY02',1001,'pencil02',0.75,'Carrots (rabbit hunting season only)');

-- 插入多个行:相比执行多条INSERT语句会更快
INSERT INTO products(prod_id,vend_id,prod_name,prod_price,prod_desc)
VALUES
('TRACY03',1001,'pencil03',0.45,'Carrots (rabbit hunting season only)'),
('TRACY04',1001,'pencil04',0.45,'Carrots (rabbit hunting season only)'),
('TRACY05',1001,'pencil05',0.45,'Carrots (rabbit hunting season only)'),
('TRACY06',1001,'pencil06',0.45,'Carrots (rabbit hunting season only)'),
('TRACY07',1001,'pencil07',0.45,'Carrots (rabbit hunting season only)');


-- 插入检索出来的行,根据列的位置而不是列名填充数据,列名甚至可以匹配不上
INSERT INTO customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email)
SELECT CONCAT(cust_name,'_new'),cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email
FROM customers
WHERE cust_id>10003;



18 修改和删除数据

-- 修改表数据
-- 注意:如果忘写where子句将会修改所有行的数据
UPDATE products 
SET prod_price=6.00,prod_name=CONCAT(prod_name,'_updated')
WHERE prod_price>=50;

-- 使用ignore关键字
-- 一般情况下,执行update语句的过程中如果发生错误,将会更新失败
-- 使用ignore关键字,发生错误,语句会忽略错误,继续执行
UPDATE IGNORE products 
SET prod_price=6.00,prod_name=CONCAT(prod_name,'_updated')
WHERE prod_price>=50;

-- 删除表数据
-- 我先插入两条语句供后面删除
INSERT INTO products(prod_id,vend_id,prod_name,prod_price,prod_desc)
VALUES('SAFE0',1003,'Safe_updated',7.00,'Safe with combination lock'),
('SAFE1',1003,'Safe_updated',7.00,'Safe with combination lock');
-- 从表中删除特定行
-- 注意:如果忘写where子句将会删除所有行的数据
DELETE FROM products 
WHERE prod_price=7.00;

-- 更快的删除
-- 如果要删除整张表,使用下面的语句会更快,会重新创建一个表,并删除原来的表
-- 我先创建一个表并插入数据
CREATE TABLE products_new
(
  prod_id    char(10)      NOT NULL,
  vend_id    int           NOT NULL ,
  prod_name  char(255)     NOT NULL ,
  prod_price decimal(8,2)  NOT NULL ,
  prod_desc  text          NULL ,
  PRIMARY KEY(prod_id)
) ENGINE=InnoDB;
INSERT INTO products_new
SELECT * FROM products;
-- 删除所有的行,这个表会变成空表
TRUNCATE TABLE products_new;



19 创建和操纵表

-- 创建表
-- test_id设置为自增主键,存储引擎设置为InnoDB
-- test_quantity字段指定默认值为1
-- DROP TABLE IF EXISTS tb_test意思是在创建表之前先确认并删除已经存在的重名表
DROP TABLE IF EXISTS tb_test;
CREATE TABLE tb_test
(
	test_id INT NOT NULL AUTO_INCREMENT,
	test_name VARCHAR(20) NOT NULL,
	test_address CHAR(50) NULL,
	test_quantity INT NOT NULL DEFAULT 1,
	PRIMARY KEY (test_id)
)ENGINE=InnoDB;

-- 获取最后一个AUTO_INCREMENT值
SELECT last_insert_id() FROM tb_test;

-- 修改表结构:添加一个列
ALTER TABLE tb_test
ADD tb_phone CHAR(11) NOT NULL;

-- 修改表结构:删除一个列
ALTER TABLE tb_test
DROP COLUMN tb_phone;

-- 一条语句作多条更改
ALTER TABLE tb_test
ADD tb_phone CHAR(11) NOT NULL,
DROP COLUMN tb_phone;

-- 删除表
DROP TABLE tb_test;

-- 重命名表
RENAME TABLE products TO products1;
RENAME TABLE products1 TO products;

-- 批量重命名
RENAME TABLE products TO products1,
						products1 TO products;



20 使用视图

-- 视图
-- 包含的不是实际的数据,而是一个SQL查询
-- 视图相当于虚拟的表,因此可以使用where查询子句、逻辑运算符、函数等
-- 优点:重用SQL、简化查询操作、格式化查询结果

-- 创建视图
CREATE VIEW viewdemo AS
SELECT p1.prod_id,p1.vend_id,p1.prod_name,p1.prod_price FROM products AS p1
INNER JOIN products AS p2
ON p1.vend_id=p2.vend_id AND p2.prod_id='ANV01';

-- 使用视图,可将其视作一张虚拟的表
SELECT * FROM viewdemo
WHERE prod_price=5.99;

-- 通过视图将查询结果包装成格式化的结果
CREATE VIEW customers_list AS
SELECT CONCAT('(',cust_id,')',cust_name) AS customers_list FROM customers;
-- 查询
SELECT * FROM customers_list;

-- 视图的删除
DROP VIEW customers_list;



21 存储过程

-- 创建存储过程
CREATE PROCEDURE productpricing()
BEGIN
	SELECT MAX(prod_price) AS pricehigh,MIN(prod_price) AS pricelow,AVG(prod_price) AS priceaverage
	FROM products;
END;


-- 使用存储过程
CALL productpricing();


-- 删除存储过程
DROP PROCEDURE IF EXISTS productpricing;


-- 使用参数
-- OUT声明的是返回的参数
DROP PROCEDURE IF EXISTS productpricing;
CREATE PROCEDURE productpricing(
	OUT p1 DECIMAL(8,2),
	OUT p2 DECIMAL(8,2),
	OUT p3 DECIMAL(8,2)
)
BEGIN
	SELECT MAX(prod_price) INTO p1 FROM products;
	SELECT MIN(prod_price) INTO p2  FROM products;
	SELECT AVG(prod_price) INTO p3  FROM products;
END;

-- 调用需要指定3个变量名
CALL productpricing(@pricehigh,@pricelow,@priceavg);

-- 由于调用了存储过程,因此获得了三个存放了返回值的变量,可以检索它们的值
SELECT @pricehigh,@pricelow,@priceavg;


-- 使用参数
-- IN声明的是传入的参数
DROP PROCEDURE IF EXISTS productpricing;
CREATE PROCEDURE productpricing(
	IN lim DECIMAL(8,2),
	OUT p1 DECIMAL(8,2),
	OUT p2 DECIMAL(8,2),
	OUT p3 DECIMAL(8,2)
)
BEGIN
	SELECT MAX(prod_price) FROM products WHERE prod_price>=lim INTO p1;
	SELECT MIN(prod_price) FROM products WHERE prod_price>=lim INTO p2;
	SELECT AVG(prod_price) FROM products WHERE prod_price>=lim INTO p3;
END;

-- 现在调用存储过程会得到不同的结果
CALL productpricing(10.00,@pricehigh,@pricelow,@priceavg);
SELECT @pricehigh,@pricelow,@priceavg;



22 触发器

-- 触发器
-- 每个表可创建6个触发器,每条INSERT、UPDATE、DELETE之前和之后
-- 仅支持表,不支持视图

-- 创建触发器
-- NEW表示插入语句执行后的表
CREATE TRIGGER customer_insert AFTER INSERT ON customers
FOR EACH ROW SELECT NEW.cust_id INTO @newcust_id;

-- 插入一条数据试试
INSERT INTO customers(cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUE('Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');

-- 获取触发器记录的变量值
SELECT @newcust_id;

-- 删除触发器
DROP TRIGGER customer_insert;

-- 再创建一个UPDATE触发器
-- OLD表示修改之前的表
CREATE TRIGGER customer_update AFTER UPDATE ON customers
FOR EACH ROW SELECT OLD.cust_name INTO @newcust_name;



23 事务管理

-- INNODB支持事务管理,MyISAM不支持
-- 几个关键词:事务、提交、回退、保存点
START TRANSACTION;
DELETE FROM customers WHERE cust_id='10001';
-- 设置一个保存点point1
SAVEPOINT point1;
DELETE FROM customers WHERE cust_id='10002';
DELETE FROM customers WHERE cust_id='10003';
-- 回退到START TRANSACTION,只能针对UPDATE、INSERT、DELETE回退
ROLLBACK;
DELETE FROM customers WHERE cust_id='10004';
-- 回退到保留点point1
ROLLBACK TO point1;
DELETE FROM customers WHERE cust_id='10005';
-- 提交事务
COMMIT;



24 安全管理

-- 查看系统用户名
USE mysql;
SELECT user FROM user;

-- 创建一个用户,名为tracy密码为tracypassword
CREATE USER tracy IDENTIFIED BY 'tracypassword';

-- 重命名用户
RENAME USER tracy to tracynew;

-- 删除用户
DROP USER tracynew;

-- 新创建的用户没有访问权限,只能登录,但无法访问数据

-- 查看用户权限
-- GRANT USAGE ON *.* TO `tracy`@`%`表示tracy用户还没有权限
-- `tracy`@`%`中的%表示不限制主机名
SHOW GRANTS FOR tracy;

-- 授予用户权限
-- 将mysqldemo数据库中所有表的SELECT权限授予给tracy用户
GRANT SELECT ON mysqldemo.* TO tracy;

-- 撤销用户权限
-- 将mysqldemo数据库中所有表的SELECT权限授予给tracy用户
REVOKE SELECT ON mysqldemo.* FROM tracy;

-- 授予全部权限
GRANT ALL ON mysqldemo.* TO tracy;

-- 撤销全部权限
REVOKE ALL ON mysqldemo.* FROM tracy;

-- 修改用户密码
ALTER USER tracy IDENTIFIED BY 'newpassword';



版权声明:本文为Tracycoder原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。