《mysql必知必会》学习笔记

217 阅读30分钟

0 数据库脚本

在这里插入图片描述

########################################
# MySQL Crash Course
# http://www.forta.com/books/0672327120/
# Example table creation scripts
########################################


########################
# Create customers 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 ,
  cust_contact char(50)  NULL ,
  cust_email   char(255) NULL ,
  PRIMARY KEY (cust_id)
) ENGINE=InnoDB;

#########################
# Create orderitems table
#########################
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 orders table
#####################
CREATE TABLE orders
(
  order_num  int      NOT NULL AUTO_INCREMENT,
  order_date datetime NOT NULL ,
  cust_id    int      NOT NULL ,
  PRIMARY KEY (order_num)
) ENGINE=InnoDB;

#######################
# Create products table
#######################
CREATE TABLE products
(
  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;

######################
# Create vendors table
######################
CREATE TABLE vendors
(
  vend_id      int      NOT NULL AUTO_INCREMENT,
  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 ,
  PRIMARY KEY (vend_id)
) ENGINE=InnoDB;

###########################
# Create productnotes table
###########################
CREATE TABLE productnotes
(
  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;


#####################
# Define foreign keys
#####################
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num);
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_products FOREIGN KEY (prod_id) REFERENCES products (prod_id);
ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (cust_id) REFERENCES customers (cust_id);
ALTER TABLE products ADD CONSTRAINT fk_products_vendors FOREIGN KEY (vend_id) REFERENCES vendors (vend_id);
########################################
# MySQL Crash Course
# http://www.forta.com/books/0672327120/
# Example table population scripts
########################################


##########################
# Populate customers table
##########################
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)
VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)
VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd');


########################
# Populate vendors table
########################
INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES(1001,'Anvils R Us','123 Main Street','Southfield','MI','48075', 'USA');
INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES(1002,'LT Supplies','500 Park Street','Anytown','OH','44333', 'USA');
INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES(1003,'ACME','555 High Street','Los Angeles','CA','90046', 'USA');
INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES(1004,'Furball Inc.','1000 5th Avenue','New York','NY','11111', 'USA');
INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES(1005,'Jet Set','42 Galaxy Road','London', NULL,'N16 6PS', 'England');
INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES(1006,'Jouets Et Ours','1 Rue Amusement','Paris', NULL,'45678', 'France');


#########################
# Populate products table
#########################
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('ANV01', 1001, '.5 ton anvil', 5.99, '.5 ton anvil, black, complete with handy hook');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('ANV02', 1001, '1 ton anvil', 9.99, '1 ton anvil, black, complete with handy hook and carrying case');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('ANV03', 1001, '2 ton anvil', 14.99, '2 ton anvil, black, complete with handy hook and carrying case');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('OL1', 1002, 'Oil can', 8.99, 'Oil can, red');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('FU1', 1002, 'Fuses', 3.42, '1 dozen, extra long');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('SLING', 1003, 'Sling', 4.49, 'Sling, one size fits all');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('TNT1', 1003, 'TNT (1 stick)', 2.50, 'TNT, red, single stick');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('TNT2', 1003, 'TNT (5 sticks)', 10, 'TNT, red, pack of 10 sticks');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('FB', 1003, 'Bird seed', 10, 'Large bag (suitable for road runners)');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('FC', 1003, 'Carrots', 2.50, 'Carrots (rabbit hunting season only)');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('SAFE', 1003, 'Safe', 50, 'Safe with combination lock');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('DTNTR', 1003, 'Detonator', 13, 'Detonator (plunger powered), fuses not included');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('JP1000', 1005, 'JetPack 1000', 35, 'JetPack 1000, intended for single use');
INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('JP2000', 1005, 'JetPack 2000', 55, 'JetPack 2000, multi-use');



#######################
# Populate orders table
#######################
INSERT INTO orders(order_num, order_date, cust_id)
VALUES(20005, '2005-09-01', 10001);
INSERT INTO orders(order_num, order_date, cust_id)
VALUES(20006, '2005-09-12', 10003);
INSERT INTO orders(order_num, order_date, cust_id)
VALUES(20007, '2005-09-30', 10004);
INSERT INTO orders(order_num, order_date, cust_id)
VALUES(20008, '2005-10-03', 10005);
INSERT INTO orders(order_num, order_date, cust_id)
VALUES(20009, '2005-10-08', 10001);


###########################
# Populate orderitems table
###########################
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20005, 1, 'ANV01', 10, 5.99);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20005, 2, 'ANV02', 3, 9.99);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20005, 3, 'TNT2', 5, 10);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20005, 4, 'FB', 1, 10);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20006, 1, 'JP2000', 1, 55);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20007, 1, 'TNT2', 100, 10);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20008, 1, 'FC', 50, 2.50);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20009, 1, 'FB', 1, 10);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20009, 2, 'OL1', 1, 8.99);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20009, 3, 'SLING', 1, 4.49);
INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20009, 4, 'ANV03', 1, 14.99);

#############################
# Populate productnotes table
#############################
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(101, 'TNT2', '2005-08-17',
'Customer complaint:
Sticks not individually wrapped, too easy to mistakenly detonate all at once.
Recommend individual wrapping.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(102, 'OL1', '2005-08-18',
'Can shipped full, refills not available.
Need to order new can if refill needed.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(103, 'SAFE', '2005-08-18',
'Safe is combination locked, combination not provided with safe.
This is rarely a problem as safes are typically blown up or dropped by customers.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(104, 'FC', '2005-08-19',
'Quantity varies, sold by the sack load.
All guaranteed to be bright and orange, and suitable for use as rabbit bait.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(105, 'TNT2', '2005-08-20',
'Included fuses are short and have been known to detonate too quickly for some customers.
Longer fuses are available (item FU1) and should be recommended.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(106, 'TNT2', '2005-08-22',
'Matches not included, recommend purchase of matches or detonator (item DTNTR).'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(107, 'SAFE', '2005-08-23',
'Please note that no returns will be accepted if safe opened using explosives.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(108, 'ANV01', '2005-08-25',
'Multiple customer returns, anvils failing to drop fast enough or falling backwards on purchaser. Recommend that customer considers using heavier anvils.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(109, 'ANV03', '2005-09-01',
'Item is extremely heavy. Designed for dropping, not recommended for use with slings, ropes, pulleys, or tightropes.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(110, 'FC', '2005-09-01',
'Customer complaint: rabbit has been able to detect trap, food apparently less effective now.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(111, 'SLING', '2005-09-02',
'Shipped unassembled, requires common tools (including oversized hammer).'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(112, 'SAFE', '2005-09-02',
'Customer complaint:
Circular hole in safe floor can apparently be easily cut with handsaw.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(113, 'ANV01', '2005-09-05',
'Customer complaint:
Not heavy enough to generate flying stars around head of victim. If being purchased for dropping, recommend ANV02 or ANV03 instead.'
);
INSERT INTO productnotes(note_id, prod_id, note_date, note_text)
VALUES(114, 'SAFE', '2005-09-07',
'Call from individual trapped in safe plummeting to the ground, suggests an escape hatch be added.
Comment forwarded to vendor.'
);

1 了解SQL

数据库:保存有组织的数据的容器(通常是一个文件或者一组文件)

数据库软件(DBMS):人们通常使用的是数据库软件,数据库是通过DBMS创建和操纵的容器

表:某种特定类型数据的结构化清单

模式:关于数据库和表的布局及特性的信息

列:表中的一个字段,所有表都是由一个或多个列组成的。

行:表中的一个记录

主键:表中每一行都应该有可以唯一标识自己的一列,其值可以唯一区分表中的每个行。没有主键,更新或者删除表中特定的行很困难,因为没有安全的方法保证只涉及相关的行。

Mysql主键规则:

  • 任意两行都不具有相同的主键值

  • 每个行都必须具有一个主键值,主键值不允许为null

2 mysql简介

  • 简单介绍一下Mysql,自行查询相关资料即可

3 使用MySQL

# 创建一个数据库learn_mysql,连接数据库
use learn_mysql;

# 展示某个数据库中的表
show databases;

# 展示某个表中所有列的设计信息:describe table_name等价
show columns from customers;
# 与上面等价
describe customers;

4 检索数据

# 展示的数据是无序的:sql语句返回的是原始、无格式的数据
select prod_name from products;

# 去重检索
select vend_id from products;
select distinct vend_id from products;

# 数据库行数从0开始;rowIndex = (前端页数-1)*每页展示数量
select prod_name  from products limit 0,1;

5 排序检索数据

  • order by :必须在from字句后,如果结合limit,则limit必须在order by之后
# 单个列排列:order by,检索数据通常需要排序才有意义,默认是升序排列
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,prod_name from products order by prod_price desc;

# 前一个升序,后一个降序
select prod_id,prod_price,prod_name from products order by prod_price desc,prod_name;

# 找出最高价格
select prod_price from products order by prod_price desc limit 1;

6 过滤数据

操作符说明
=等于
<>不等于
!=不等于
<小于
<=小于等于
大于
>=大于等于
between两者之间
# where:不区分大小写
select prod_name,prod_price from products where prod_name ='fuses';
select prod_name,prod_price from products where prod_name ='FUses';

# 不等于的两种写法
select vend_id,prod_name from products where vend_id <> 1003;
select vend_id,prod_name from products where vend_id != 1003;

# between:范围内查询
select prod_name,prod_price from products where prod_price between 5 and 10;

# 空值查询:is null
select cust_id from customers where cust_email is null;

7 数据过滤

in 运算符优点:

  1. 语法更清楚和直观
  2. 计算次序更容易管理
  3. in 比 or 计算速度更快
  4. in 最大优点是可以包含其他select 语句
# where+and:两者都要匹配
select prod_id,prod_price,prod_name from products where vend_id = 1003 and prod_price <=10;

# where+or:匹配任何一个都可以
select prod_name,prod_price from products where vend_id = 1002 or vend_id =1003;

# and+or:and的优先级比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;
# or字句需要先执行的话,需要加括号
select prod_name,prod_price from products where (vend_id=1002 or vend_id=1003) and prod_price >=10;

# in:in (a,b) 等价 a or b
select  prod_name,prod_price from products where vend_id in (1002,1003) order by prod_name ;
select  prod_name,prod_price from products where vend_id  = 1002 or  vend_id =1003 order by prod_name;

# not + in :mysql支持对in、between、exists句子使用
select  prod_name,prod_price from products where vend_id not in (1002,1003) order by prod_name ;

8 用通配符的技巧

注意尾部空格:%anvil不能匹配最后一个为一个空格或多个空格,解决办法是在%anvil%或者第11章的去掉首尾空格的函数

注意Null:尽管%可以匹配任何东西,但是不能匹配null值

# 通配符%:默认是不区分大小写
select prod_id,prod_name from products where prod_name like 'Jet%';
select prod_id,prod_name from products where prod_name like 'jet%';

# 通配符%前后都使用:
select prod_id,prod_name from products  where prod_name like '%anvil%';

# 通配符_:匹配单个字符,.5 ton anvil就匹配不出来,%才行
select prod_id,prod_name from products  where prod_name like '_ ton anvil';
select prod_id,prod_name from products  where prod_name like '%ton anvil';

9 用正则表达式进行搜索

空白元字符说明
\\f换页
\\n换行
\\r回车
\\t制表
\\v纵向制表
字符类说明
[:alnum:]任意字母或数字(同[a-zA-Z0-9])
[:alpha:]任意字符(同[a-zA-Z])
[:blank:]空格和制表(同[\\t])
[:cntrl:]ASCII控制字符(ASCII0到32和127)
[:digit:]任意数字(同[0-9])
[:graph:]与[:print:]相同,但不包含空格
[:lower:]任意小写字母(同[a-z])
[:prinit:]任意可打印字符
[:punct:]既不在[:alnum:]又不在[:cntrl:]中的任意字符
[:space:]包括空格在内的任意空白字符(同[\\f\\n\\r\\t\\v])
[:upper:]任意大写字符(同[A-Z])
[:xdigit:]任意十六进制数字(同[a-fA-F0-9])
重复元字符说明
*0个或多个匹配
+1个或多个匹配(等于{1,})
0或1个匹配(等于{0,1})
{n}指定数目的匹配
{n,}不少于指定数目的匹配
{n,m}匹配数目的范围(m不超过255)
定位元字符说明
^文本的开始
$文本的结束
[[:<:]]词的开始
[[:>:]]词的结尾
# regexp:表示使用正则表达式,'1000'等价于%1000%,相当于模糊匹配
select prod_name from products where prod_name regexp '1000';
select prod_name from products where prod_name regexp '.000';

# like和regexp的区别
select prod_name from products where prod_name like '1000';# like匹配整个列,被匹配文本在列值中出现,相应的行也不会被返回
select prod_name from products where prod_name regexp '1000';# regexo匹配整个列,被匹配文本在列值中出现,相应的行会被返回

# regexp:默认是不去区分大小写,如果需要区分大小写,regexp binary
select prod_name from products where prod_name regexp binary 'JetPack .000';

# regexp+|:与or功能类似
select prod_name from products where prod_name regexp '1000|2000' order by prod_name;

# []:表示匹配特定的某个字符
# [123] ton = 严格按照[1|2|3] ton
select prod_name from products where prod_name regexp '[123] ton' order by prod_name;
# ^:取反
select prod_name from products where prod_name regexp '[^123] ton' order by prod_name;
# 1|2|3 ton:列里包含1或者2或者3 ton,与[123 ton]含义区分开
select prod_name from products where prod_name regexp '1|2|3 ton' order by prod_name;
# [1-5]:表示范围
select 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 vend_name from vendors where vend_name regexp '\\.' order by vend_name;

# 匹配(数字 stick)或者(数字 sticks)
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:] sticks?\\)' order by prod_name;

# 匹配包含4个数字
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;

10 创建计算字段

字段:基本上与列意思相同,但是列名是数据库实际存储的列的名字,而字段通常是经过用在计算字段的连接上

拼接(concatenate):将值拼接到一起构成单个值

# CONCAT使用拼接,mysql的拼接是使用concat,别的sql会有区别
select CONCAT(vend_name,' (',vend_country,')')  from vendors order by vend_name;
# Rtrim:去掉右边的空格
select CONCAT(Rtrim(vend_name),' (',Rtrim(vend_country),')')  from vendors order by vend_name;
# 取别名
select CONCAT(Rtrim(vend_name),' (',Rtrim(vend_country),')')  as vend_title from vendors order by vend_name;

# 执行计算,mysql算术运算符支持+,-,*,/四种
select prod_id,quantity,item_price from orderitems where order_num =20005;
select prod_id,quantity,item_price,quantity*item_price as expanded_price from orderitems where order_num =20005;

# mysql的select可以省略from字句便于简单地访问和处理数据
select 3*2;
select trim(' abc ');
select Now();# 返回当前日期和时间

11 使用数据处理函数

常用的文本处理函数说明
Left()返回串左边的字符
Length()返回串的长度
Locate()找出串的一个子串
Lower()将串转换为小写
LTrim()去掉串左边空格
Right()返回串右边的字符
Rtrim()去掉串右边的空格
Soundex()返回串的SoundDex值
SubString()返回子串的字符
Upper将串转换为大写

Soundex():将任何文本串转换为描述其语言表示的字母数字模式的算法,以下举个例子

# SOUNDEX():匹配发音相似的文本
select cust_name,cust_contact from customers where cust_contact = 'Y. Lie';
select cust_name,cust_contact from customers where soundex(cust_contact) = soundex('Y. Lie');
常用日期和时间处理函数说明
AddDate()添加一个日期(天、周等)
AddTime()添加一个日期(时、分等)
CurDate()返回当前日期
CurTime()返回当前时间
Date()返回日期时间的日期部分
DateDiff()计算两个日期之差
Date_Add()高度灵活的日期运算函数
Date_Format()返回一个格式化的日期或时间串
Day()返回一个日期的天数部分
DayOfWeek()对于一个日期,返回对应的星期几
Hour()返回小时部分
Minute()返回分钟部分
Month()返回月份部分
Now()返回当前日期和时间
Second()返回秒部分
Time()返回时间部分
Year()返回年份部分
常用数值处理函数说明
Abs()绝对值
Cos()余弦
Exp()指数值
Mod()除操作的余数
Pi()圆周率
Rand()随机数
Sin()正弦
Sqart()平方根
Tan()正切
# 文本处理函数:Upper
select vend_name,Upper(vend_name) as vend_name_upcase from vendors order by vend_name;

# 日期yyyy-mm-dd,时间HH:MM:SS
# 这样日期是不严谨的,只能查出时间默认为0的日期,比如2005-09-01 00:00:00
# 但是2005-09-01 10:20:30就无法查出想要的结果
select cust_id,order_num from orders where order_date = '2005-09-01';
# 使用Date()和Time()函数
select cust_id,order_num from orders where Date(order_date) = '2005-09-01';
select cust_id,order_num from orders where Time(order_date) = '01:00:00';

# 搜索2005年9月以内的订单
# 方法1:需要知道月份的具体天数
select cust_id,order_num from orders where Date(order_date) between '2005-09-01' and '2005-09-30';
# 方法2:不需要知道具体的天数,最方便
select cust_id,order_num from orders where Year(order_date) = '2005' and Month(order_date)='9';

12 汇总数据

聚集函数说明
AVG()平均值,忽略null值
COUNT()行数
MAX()最大值
MIN()最小值
SUM()之和
# avg:算平均值
select avg(prod_price) as avg_price from products;
# 算特定列的平均值
select avg(prod_price) as avg_price from products where vend_id=1003;
# 算去重后特定列的平均值
select avg( distinct prod_price) as avg_price from products where vend_id=1003;

# count(*)和(1):算行数
select count(*) as num_cust from customers;
select count(1) 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 max_price from products;

# sum
select sum(quantity) as times_ordered from orderitems where order_num =20005;
# 数学运算后的sum值
select sum(item_price*quantity) as total_price from orderitems where order_num =20005;

# 组合聚集函数
select 
	count(*) as num_items,
	min(prod_price) as min_price,
	max(prod_price) as max_price,
	avg(prod_price) as avg_price
from products;

13 分组数据

使用group by的注意事项:

  • group by字句可以包含任意数目的列。这使得能对分组进行嵌套而数据分组提供更细致的控制
  • 如果在group by字句嵌套了分组,数据将在最后规定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算
  • group by字句中列出的每个列都必须是检索列或有效的表达式(但不是聚集函数)。如果在select 中使用表达式,则必须在group by 字句中指定相同的表达式
  • 除聚集计算语句外,select 语句中的每个列都必须在group by字句中给出
  • 如果分组列中具有null值,则null将作为一个分组返回。如果列中有多行null值,他们将分在一组
  • group by 字句必须出现在where之后,order by之前

having和where区别:

  • where在分组前进行过滤,having 在数组分组后进行过滤
order bygroup by
排序产生的输出分组行。但输出可能不是分组的顺序
任意列都可以使用只可能使用选择列或表达式列,而且必须使用每个选择列表达式
不一定需要如果和聚集函数一起使用列(或表达式),则必须使用
字句执行顺序说明是否必须使用
select要返回的列或表达式
from从中检索数据的表仅在从表选择数据时使用
where行级过滤
group by分组说明仅在按组计算聚焦使用
having组级过滤
order by输出排序顺序
limit要检索的行数
# 如果要返回每个供应商提供的产品数目怎么办?
select count(1) from products  where vend_id =1003;

# group by:将数组进行分组,对每个组而不是每个结果集进行了聚集
# 使用group by输出可能不是分组的原本排序,所以好习惯是group by + order  by一起使用
select vend_id,count(*) as num_prods from products group by vend_id order by vend_id; 
# group by +  rollup:对每个分组汇总了总数(级别)
select vend_id,count(*) as num_prods from products group by vend_id with rollup order by vend_id ;  

# gruop by是分组,+having 就是过滤分组
select cust_id,count(*) as orders from orders group by cust_id having count(*) >=2;

# where+group by组合使用:where先对数据进行过滤,然后再分组
select vend_id,count(*) as num_prods from products where prod_price >=10 group by vend_id having count(*)>=2 order by vend_id; 
select vend_id,count(*) as num_prods from products group by vend_id having count(*)>=2 order by vend_id; 

# 检索总计订单价格>=50的订单号和总结订单价格,使用group by + order by好习惯的由来
select order_num ,sum(quantity * item_price) as ordertotal  from orderitems group  by order_num having sum(quantity * item_price) >=50;
select order_num ,sum(quantity * item_price) as ordertotal  from orderitems group  by order_num having sum(quantity * item_price) >=50 order by ordertotal;

14 使用子查询

子查询最常见的使用是在where字句的in操作符中+以及用来填充计算列。

# 列出订购物品TNT2的所有客户
# 1.检索包含物品TNT2的所有订单的编号
# 2.检索具有前一步列出的订单编号的所有客户ID
# 3.检索前一步返回的所有客户ID的客户信息
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' ) );

# customers表中每个客户的订单总数,订单和相应客户的ID存储在orders表中
# 该子查询对检索出的每个客户执行了一次,子查询执行了5次,因此检索出了5个客户
select  
 cust_name,
 cust_state,
 (
	select count(*) from orders where orders.cust_id = customers.cust_id
 ) as orders
from customers order by cust_name;	

15 使用联结

笛卡尔积:

  • 由没有联结条件的表关系返回的结构为笛卡尔积。检索出的行数目将是第一个表中的行数乘以第二个表中的行数
  • 说明:使用多表查询时候,请必须使用where搭配,否则会出现很多虚假数据
# 两张表联结查询
select vend_name,prod_name,prod_price from vendors,products where vendors.vend_id = products.vend_id order by vend_name,prod_name;
# 多表连接查询时,请使用where搭配,否则会出现很多无用数据
select vend_name,prod_name,prod_price from vendors,products 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 order by vend_name,prod_name;

# 联结多个表
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; 
	
# 14章的订购产品TNT2的客户列表改成联结查询
# 14章子查询
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_contact 
FROM
	customers,
	orders,
	orderitems 
WHERE
	customers.cust_id = orders.cust_id 
	AND orderitems.order_num = orders.order_num 
	AND prod_id = 'TNT2';

16 高级联结

使用联结和联结条件:

  • 注意使用的联结类型。一般我们使用内部联结,但使用外部联结也是有效的。
  • 保证使用正确的联结类型,否则将返回不正确的数据
  • 应该总是提供联结条件,否则会出现笛卡尔积
  • 在一个联结中可以包含多个表,甚至对于每个联结可以采用不同的联结类型。但是要做好测试,便于排查和书写
# 列别名
select CONCAT(Rtrim(vend_name),' (',Rtrim(vend_country),')')  as vend_title from vendors order by vend_name;
# 表别名:不仅作用于where字句,select列表,order by字句以及语句的其他部分 
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';
	
# 查询生产DTNTR的供应商其他产品
# 自联结
select prod_id,prod_name from products where vend_id = (select vend_id from products where prod_id  = 'DTNTR');
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
	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 = 'FB';
	
# 内连接:检索出所有的客户和订单,不会产生null值,因为不以哪个表为主表
select customers.cust_id ,orders.order_num from customers inner join orders on customers.cust_id = orders.cust_id;
select customers.cust_id ,orders.order_num from customers left join orders on customers.cust_id = orders.cust_id;

# 外部联结:包括没有关联的行也展示出来,所以必须搭配left或right
select customers.cust_id ,orders.order_num from customers right outer  join orders on customers.cust_id = orders.cust_id order by customers.cust_id;
select customers.cust_id ,orders.order_num from customers left outer join orders on customers.cust_id = orders.cust_id order by customers.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;
	
# 使用聚集函数的外部联结
SELECT
	customers.cust_name,
	customers.cust_id,
	count( orders.order_num ) AS num_ord 
FROM
	customers
	left outer join orders ON customers.cust_id = orders.cust_id 
GROUP BY
	customers.cust_id;

17 组合查询

union规则:

  • union必须有2条或2条以上的select语句组成,语句之间用关键字union分隔
  • union中的每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次序列出)
  • 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐式转换的类型
# 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与where多条件查询相同
select vend_id,prod_id,prod_price from products where prod_price <=5  or vend_id in(1001,1002) ;

# union会默认取消重复的行,使用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);

# union的排序用order by,并且是作用与所以的表,因为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) order by vend_id,prod_price;

18 全文本搜索

全文本布尔操作符说明
+包含,词必须存在
-排除,词必须不存在
包含,但增加等级值
<包含,降低等级值
()把词组成子表达式,允许这些子表达式作为一个组被包含、排序、排列等
~取消一个词的等级值
*词尾通配符
“”定义一个短语(与单个词的列表不同,它匹配整个短语以便包含或排除这个短语)

全文本搜素使用说明:

  • 在搜素全文本数据时,短词被忽略且从索引中排除。短词定义为那些具有3个或3个以下字符的词
  • Mysql带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。
  • 许多词出现的频率很高,搜索它们没有用处(返回太多的结果),因此,mysql规定了一个50%的规则,如果一次出现在50%以上的行中,则将它作为一个非用词忽略。50%规则不用于in boolean mode
  • 如果表中的行数<3行,则全文本搜索不返回结果(因为每个次或者不出现,或者至少出现50%的行中)
  • 忽略词中的单引号。
  • 不具有词的分隔符的语言不能恰当地返回全文本搜索结果
  • 仅在MyISAM数据库引擎中支持全文本中搜索
# 全文本搜索仅支持MyISAM引擎
select note_text from productnotes where match(note_text) against('rabbit');
# 虽然like模糊查询也可实现但排序是随机的,全文本匹配可以按照文本匹配程度由高到底排序
select note_text from productnotes where note_text like '%rabbit%';

# 查询扩展:不止全文本匹配除你想的文本,还搜索出可能与之相关的行
select note_text from productnotes where match(note_text) against('anvils');
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);
# +必须包含:说明同时包含rabbit bait
select note_text from productnotes where match(note_text) against('+rabbit +bait' in boolean mode);
# 没有+,说明rabbit bait中至少一个都行
select note_text from productnotes where match(note_text) against('rabbit bait' in boolean mode);
# "rabbit bait"是匹配短语rabbit bait
select note_text from productnotes where match(note_text) against('"rabbit bait"' in boolean mode);
# >:增加等级值,<:减少等级值
select note_text from productnotes where match(note_text) against('>rabbit <bait"' in boolean mode);
# ();把词组成子表达式,允许这些子表达式作为一个组被包含、排除、排列等
select note_text from productnotes where match(note_text) against('+safe +(<combination)"' in boolean mode);

19 插入数据

# 不安全的插入一整行,因为表列顺序可能会改变
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'
);
insert into customers (
	cust_name,
	cust_address,
	cust_city,
	cust_state,
	cust_zip,
	cust_country
)
values(
	'M. Martin',
	'100 Main Street',
	'Los Angeles',
	'CA',
	'90046',
	'USA'
);

# 多条数据一次书写,优点是书写简单,缺点性能没有单条数据逐行插入快
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. Martin',
	'100 Main Street',
	'Los Angeles',
	'CA',
	'90046',
	'USA'
);
# 创建custnew表,模拟insert select操作
CREATE TABLE custnew
(
  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 ,
  cust_contact char(50)  NULL ,
  cust_email   char(255) NULL ,
  PRIMARY KEY (cust_id)
) ENGINE=InnoDB;

# 复制customers表中一行数据到custnew中,注意将主键cust_id修改为customers中未出现的主键值 
INSERT INTO `learn_mysql`.`custnew`(`cust_id`, `cust_name`, `cust_address`, `cust_city`, `cust_state`, `cust_zip`, `cust_country`, `cust_contact`, `cust_email`) VALUES (20001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');

# insert 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;

20 更新和删除数据

更新和删除的指导原则

  • 除非实在打算删除更新和删除每一行,否则绝不使用不带where字句的update,delete语句
  • 保证每个表都有主键,尽可能像where字句那样使用它
  • 在对update或delete语句使用where时,最好先用select保证where过滤的正确性
  • 使用强制实施引用完整性的数据库,mysql将不允许删除具有其他表关联的数据行
# update更新语句要与where搭配使用,否则将更新整个列的数据
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;
	
# delete删除也得搭配where使用,否则也是删除全部数据 
# 如果想表中删除所有行,用delete;删除原来的表并重新创建一个,用truncate table 
delete from customers where cust_id = 10006;

21 创建和操作数据库表

使用auto_increment:

  • 每个表只允许一个auto_increment列,并且它碧玺被索引(比如让它成为主键)
  • select last_insert_id()查询最后一次auto_increment的值

引擎类型:

  • InnoDB是一个可靠的事务处理引擎,但是不支持全文本搜索
  • Memery在功能上等于MyISAM,但由于数据存储在内存,速度很快(适用于临时表)
  • MyISAM是一个性能极高的引擎,它支持全文本搜索,但不支持事务
  • 外键不能跨引擎使用,所以定义表就显得尤其重要
# 创建表:create table table
CREATE TABLE customers
(
	# AUTO_INCREMENT每个表只能有一个,并且必须被索引
  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 ,
  cust_contact char(50)  NULL ,
  cust_email   char(255) NULL ,
  PRIMARY KEY (cust_id)
) ENGINE=InnoDB;

# select last_insert_id()查询最后一次auto_increment的值
select last_insert_id();


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,# 不指定插入默认是1
  item_price decimal(8,2) NOT NULL ,
  PRIMARY KEY (order_num, order_item) # 两个主键
) ENGINE=InnoDB;

CREATE TABLE productnotes
(
  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;# 指定引擎为MyISAM
# 更新表alter table xx
# 增加一列
alter table vendors add vend_phone char(20);

# 删除一列
alter table drop column vend_phone;

# alter创建外键
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num);
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_products FOREIGN KEY (prod_id) REFERENCES products (prod_id);
ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (cust_id) REFERENCES customers (cust_id);
ALTER TABLE products ADD CONSTRAINT fk_products_vendors FOREIGN KEY (vend_id) REFERENCES vendors (vend_id);

# 删除整个表
drop table custnew;

# 重命名一个表
rename table  customers to customers1;
rename table  customers1 to customers;

22 使用视图

视图

  • 是虚拟的表,是mysql对select语句的封装,可用来简化数据处理以及重新格式化基础数据或保护基础数据

  • 视图本身不包含数据,因此他们返回的数据是从其他表中检索出来的。在添加和改变这些表中的数据时,视图将返回改变过的数据

  • crate view创建视图,show craete view viewname查看创建的视图

  • drop view删除视图

  • 更新视图时,可以吸纳drop再crate,或者 create or replace view

  • 如果视图定义中有如下操作,则视图不能更新,需要手动删除视图再创建,一般来说视图只用来查询!

    • 分组(group by和having)
    • 联结
    • 子查询
    • 聚集函数
# 第15章的多表联结查询,可以使用视图将它封装
SELECT
	cust_name,
	cust_contact 
FROM
	customers,
	orders,
	orderitems 
WHERE
	customers.cust_id = orders.cust_id 
	AND orderitems.order_num = orders.order_num 
	AND prod_id = 'TNT2';
	
# 创建视图,可以像执行简单sql哪样执行原本复杂sql
CREATE VIEW productcustomers AS SELECT
	cust_name,
	cust_contact, 
	prod_id #需要添加后续需要where查询的列名 
FROM
	customers,
	orders,
	orderitems 
WHERE
	customers.cust_id = orders.cust_id 
AND orderitems.order_num = orders.order_num;
	
select 
	cust_name,
	cust_contact 
from productcustomers
where prod_id = 'TNT2';


# 创建视图,可以便于重新格式化之前的数据
create view vendorlocations as 
SELECT
	CONCAT( Rtrim( vend_name ), ' (', Rtrim( vend_country ), ')' ) AS vend_title 
FROM
	vendors 
ORDER BY
	vend_name;

select * from vendorlocations;

# 创建视图过滤不想要的数据
create view customeremaillist as
select cust_id,cust_name,cust_email from customers where cust_email is not null;

select * from customeremaillist;

# 创建视图,保存计算结果,便于where使用
create view  orderitemsexpanded as  
SELECT
	order_num,
	prod_id,
	quantity,
	item_price,
	quantity * item_price AS expanded_price 
FROM
	orderitems;

select * from orderitemsexpanded where order_num = 20005;

23 使用存储过程

# 创建存储过程并使用,注意逗号分号的使用
create procedure productpricing()
begin
	select avg(prod_price) from products;
end;
# 使用存储过程
call productpricing();

# 删除存储过程,不需要()
drop procedure productpricing;

# 函数式存储过程:mysql只支持in、out、inout三个类型的函数参数
# 三个out
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;

# mysql变量都以@开头,是一段临时存储的数据
call productpricing(
	@pricelow,
	@pricehigh,
	@pricehigh
);

select @pricelow,@pricehigh,@pricehigh

# in和out使用
create procedure ordertotal(
	in onumber  int,
	out ototal decimal(8,2)
)
begin
	select sum(item_price * quantity) from orderitems where order_num = onumber
	into ototal;
end;
# 查询20005订单的合计价格
call ordertotal(20005,@totoal);

select @totoal;

建立智能存储过程:需要对订单合计增加营业税

  • 获得合计(与以前一样)
  • 把营业税有条件地添加到合计
  • 返回合计
create procedure ordertotal(
	in onumber int,
	in taxable boolean,
	out ototal decimal(8,2)
)comment'选择性计算有/无税收下的订单总价'
begin
	# 定义临时变量total
	declare total decimal(8,2);
	# 税率为6个点
	declare taxrate int default 6;
	# 查询出无税收时订单总价
	select sum(quantity * item_price) from orderitems where order_num = onumber into total; 
	# 是否存在税收
	if taxable then 
		select total+(total/100 * taxrate) into total;
	end if;
	# 临时变量结果返回给函数返回参数
	select total into ototal;
	
end;

call ordertotal(20005,0,@total);
select @total;

call ordertotal(20005,1,@total);
select @total;
# 查看存储过程等详细信息
show procedure status;

24 使用游标

使用游标:

  • 在能够使用游标前,必须声明它。这个过程实际上没有检索数据,它只是定义要使用的select语句
  • 一旦声明后,必须打开游标以供使用。这个过程用前面定义的select语句把游标实际检索出来
  • 对于填有数据的游标,根据需要取出各行
  • 在结束游标使用时,必须关闭游标
 create procedure processorders(
   out outnumber int
 )
 begin
	# 临时变量
	declare o int;
	# 创建游标
	declare ordernumbers cursor
	for 
	select order_num from orders;
	# 开启游标
	open ordernumbers;
	# 使用游标
	fetch ordernumbers into o;
	
	# 返回数据给函数参数
	select o into outnumber;
	
	# 关闭游标
	close ordernumbers;
end;

call processorders(@outnumber);

select @outnumber;

25 触发器

mysql5以后不支持触发器返回结果,自己手动添加一个变量@变量名,返回该结果可以看到触发器结果;触发器只对insert、delete、update有用

# mysql5以后不支持触发器返回结果,添加一个变量@变量名,返回该结果就行

# 第一次使用触发器:在每次往products添加一行时,都返回“Product added”
create trigger newproduct after insert on products 
for each row select 'Product added' into @ee;

insert into products(prod_id,vend_id,prod_name,prod_price,prod_desc) 
VALUES("ANV011","1001","3 ton anvil",2.33,"good");

select @ee;

insert触发器:

  • 在insert触发器代码内,可以用一个名为new的虚拟表,访问被插入的行
  • 在before insert触发器中,new中的值也可以被更新(允许更改被插入的值)
  • 对于auto_increment列,new在insert执行之前包含0,在insert之后包含新的自动生成值
# insert触发器:auto_increment列,new在insert执行前包含0,insert执行后包含自动生成值
create trigger neworder after insert on orders
for each row select NEW.order_num into @neworderRes;

insert into orders(order_date,cust_id) values(Now(),10001);

select @neworderRes;

drop trigger neworder;

delete触发器:

  • 在delete触发器代码内,可以引用一个名为old的虚拟表,访问被删除的行
  • old中的值全是只读的,不能更新
# delete触发器:任意订单被删除之前,old都将结果存回achive_orders表中
create trigger deleteorder before delete on orders
for each row 
begin
	insert into achive_orders(order_num,order_date,cust_id) values(old.order_num,old.order_date,old.cust_id);
end;

update触发器:

  • 在update触发器代码中,可以引用一个名为old的虚拟表访问之前(update语句值),引用一个名为new的虚拟表访问新更新的值
  • 在before update触发器中,new中的值可能也会被更新(允许更改将要用于update语句中的值)
  • old中的值全是只读的,不能更新
# update触发器:new和old都可以使用
create trigger updatevender before  update on vendors
from each row set new.vend_state = upper(new.vend_state) into @updatevenderRes; 

26 管理事务处理

事务(transaction):指一组sql语句

回退(rollback):指撤销指定sql的过程

提交(commit):值将未存储的sql语句结果写入数据库表

保留点(savepoint):指事务处理设置中的临时占位符(place-holder,可以对它发布回退)

# 开启事务:学习回滚
start TRANSACTION;

	select * from orderitems;
	delete from orderitems;
	select * from orderitems;
	# 回退事务,TRANSACTION之后的语句全部不执行
ROLLBACK;

select * from orderitems;

# 开启事务:学习commit
start transaction;

	delete from orderitems where order_num = 20010;
	delete from orders where order_num = 20010;
# 事务块内,commit提交一段sql,前提是没报错
commit;

# 开启事务:设置保留点
start TRANSACTION;
	# 设置一个事务的保留点()
	savepoint deletel;
	# 如果出现问题,就回退到设置的保留点
rollback to deletel;

# 设置mysql不自动提交 
set autocommit = 0;
set autocommit = 1;

27 全球化和本地化

# 显示所有可用的字符集,以及他们默认的校队
show character set;
# 显示所有可用的校队,以及他们默认的字符集
show collation;


# 显示所有的字符集和校队的名字
show variables like 'character%'; 
show variables like 'collation%';


# 创建表指定字符集和校队方式
create table mytable(
	column1 int,
	column2 varchar(10)
)engine = Innodb default character set hebrew collate hebrew_general_ci; 

# 列指定字符集和校队方式
create table mytable1(
	column1 int,
	column2 varchar(10),
	column3 varchar(100) character set latin1 collate latin1_general_ci
)engine = Innodb default character set hebrew collate hebrew_general_ci; 

# 查询语句时order by指定校队方式
select * from customers order by cust_name collate utf8_general_ci;

28 安全管理

# mysql的用户权限,放在mysql库(默认的一个库)里的user表(默认有的一个表)
use mysql;
select * from user;

# 创建一个数据库用户:ben 
# 账户:ben
# 密码:password
create user ben identified by 'password';
# 但是这样创建的用户没有任何权限,查询一下就知道
select * from user;
# 重命名数据库用户名
rename user ben to  ben1;
rename user ben1 to  ben;

# 查看ben用户的权限
show grants for ben;

# 给ben用户添加learn_mysql库的查询权限
grant select on learn_mysql.* to ben;

# 撤销ben用户查询权限
revoke select on learn_mysql.* to ben;

# 修改用户密码,但是必须有权限才行
set password for ben = PASSWORD('password1');
# 不指定用户名,默认是修改本次登录的用户的密码
set password = PASSWORD('password1');

29 数据库维护

# analyze:分析表建是否有问题
analyze table orders;

# check:检查一个表
check table orders,orderitems;

30 改善性能

  • 查看当前配置:show variables;show status;

  • 显示所有进程:show processlist(服务器上查看ip非常有用)

  • explain来查看select语句性能,再优化

  • 绝不要检索比需求还要多的数据,也就是select *减少使用

  • select 检索数据时or使用过多,可以用union来拼接多条select语句,性能好很多

  • like很慢,最好是使用fulltext而不是like