mysql -u root -proot -P 3306
登录root用户,密码为root,端口号为3306mysql>
之后;
或\g
结束help
或\h
获得帮助。如help select
、\h select
。exit
或quit
退出命令行实用程序。USE database_name; -- 选择数据库,名字叫database_name
SHOW DATABASES;
USE bookstore;
SHOW TABLES;
SHOW COLUMNS FROM book_list; -- 显示所有列信息,在一个表中
DESCRIBE book_list; -- 上下两个命令等价
输出,分别显示字段名、数据类型、是否允许null、键信息、默认值以及其他信息(如字段cust_id
的auto_increment
)。
+-----------+------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| price | decimal(10,2) unsigned | NO | | NULL | |
| author | varchar(100) | YES | | NULL | |
| sales | int(11) | YES | | NULL | |
| inventory | int(11) | YES | | NULL | |
+-----------+------------------------+------+-----+---------+----------------+
auto_increment
SHOW STATUS;
SELECT id
FROM book_list;
返回数据的顺序可能是数据被添加到表中的顺序,也可能不是。只要返回相同数目的行,就是正常的。
SELECT id, `name`
FROM book_list;
SELECT *
FROM book_list;
虽然使用通配符可能会使你自己省事,不用明确列出所需列,但检索不需要的列通常会降低检索和不用程序的性能。所以,最好别用。
SELECT DISTINCT author
FROM book_list;
只显示不同的行,不显示重复的。
SELECT *
FROM book_list
LIMIT 5; -- 只显示前5行
SQL默认第一行为0。
SELECT *
FROM book_list
LIMIT 5, 5; -- 开始行、行数
SELECT *
FROM book_list
LIMIT 4 OFFSET 3; -- 显示4行, 从第4行开始
SELECT id, `name`, price
FROM book_list
ORDER BY price; -- 以价格升序排列
SELECT id, `name`, price, sales
FROM book_list
ORDER BY price, sales; -- 首先按价格排序,其次按销量排序,均是升序
SELECT id, `name`, price, sales
FROM book_list
ORDER BY price DESC; -- DESC按降序排列,ASC按升序排列
排序中,默认不区分大小写
SELECT id, `name`, price, sales
FROM book_list
ORDER BY price DESC
LIMIT 1; -- 只显示第一行,所以就是找出最大值了
SELECT id, `name`, price, sales
FROM book_list
WHERE price=20; -- 筛选出价格为20的所有商品
ORDER BY
和WHERE
子句时,应该让ORDER BY
位于WHERE
之后,否则将会产生错误。SELECT id, `name`, price, sales
FROM book_list
WHERE `name` REGEXP '[a-z]'
ORDER BY price;
SELECT id, `name`, price, sales
FROM book_list
WHERE price BETWEEN 20 AND 100; -- 左闭右闭的区间
SELECT id, `name`, price, sales
FROM book_list
WHERE sales IS NULL; -- 挑选为sales为控制的内容
where
子句SELECT id, `name`, price, sales
FROM book_list
WHERE id <= 30 AND price <= 20; -- 多条件组合判断
SELECT id, `name`, price, sales
FROM book_list
WHERE id = 26 OR id = 30; -- 或判断,切记等于号用`=`
OR
操作符前,优先处理AND
操作符。因此,保险起见,如果处理多个逻辑运算符的时候,最好用()
扩起来。IN
操作符SELECT id, `name`, price, sales
FROM book_list
WHERE id IN (20, 30); -- id为20或30的所有行,枚举条件在括号里面
%
通配符%
代表给定位置的0个、1个或多个字符。
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` LIKE 'java%'; -- `%`告诉MySQL接受java之后的任意字符,不管它有多少字符。
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` LIKE '%java%'; -- `%`告诉MySQL接受包含“java”的任意字符,不管它有多少字符。
_
通配符_
匹配单个字符,不能多也不能少
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` LIKE 'java_'; -- `%`告诉MySQL接受“java”后面包含有且仅有一个字符的字符串
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` REGEXP 'java'; -- 匹配该列中,包含'java'字符串的所在行
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` REGEXP '.ava'; -- . 是正则表达式语言中一个特殊的字符,它匹配任意一个字符
正则表达式在MySQL中不区分大小写,如果要区分大小写,需要在匹配的字符串前面添加上BINARY
关键字
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` REGEXP BINARY 'java';
OR
匹配SELECT id, `name`, price, sales
FROM book_list
WHERE `name` REGEXP BINARY 'java|JAVA'; -- `|`字符表示OR运算,表示匹配包含`java`或`JAVA`的字符串
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` REGEXP '[tj]'; -- 匹配字符串中包含t或j的行
SELECT id, `name`, price, sales
FROM book_list
WHERE `name` REGEXP '[a-z]'; -- 匹配字符串中包含任意字母的行
SELECT vend_name
FROM vendors
WHERE vend_name REGEXP '\\.' -- 特殊字符,需要通过转义字符`\\`表示
匹配字符类
类 | 说明 |
---|---|
[:alpha:] |
任意字符,同[a-zA-Z] |
[:lower:] |
任意小写字母,同[a-z] |
[:upper:] |
任意大写字母,同[A-Z] |
[:alnum:] |
任意字母和数字,同[a-zA-Z0-9] |
[:digit:] |
任意数字,同[0-9] |
[:xdigit:] |
任意十六进制数字,同[a-fA-F0-9] |
[:blank:] |
空格和制表,同[\\t] |
[:space:] |
包括空格在内的任意空白字符,同[\\f\\n\\r\\t\\v] |
[:cntrl:] |
ASCII控制字符(ASCII 0到31和127) |
[:print:] |
任意可打印字符 |
[:graph:] |
与[:print:] 相同,但不包括空格 |
[:punct:] |
既不在[:alnum:] ,也不在[:cntrl:] 中的任意字符 |
重复元字符
元字符 | 说明 |
---|---|
* |
0个或多个匹配 |
+ |
1个或多个匹配,等于{1,} |
? |
0个或1个匹配,等于{0, 1} |
{n} |
指定数目的匹配 |
{n,} |
不少于指定数目的匹配 |
{n, m} |
匹配数目的范围(m不超过255) |
SELECT prob_name
FROM products
-- \\( 匹配 (
-- [0-9] 匹配数字
-- ? 匹配一个或0个任意字符
-- \\) 匹配 )
WHERE prob_name REGEXP '\\([0-9] sticks?\\)'
SELECT prob_name
FROM products
-- [:digit:] 匹配任意数字
-- {4} 确切地要求前面的字符出现4次
WHERE prob_name REGEXP '[[:digit:]]{4}'
ORDER BY prob_name;
元字符 | 说明 |
---|---|
^ |
文本的开始 |
$ |
文本的结束 |
[[:<:]] |
词的开始 |
[[:>:]] |
词的结束 |
SELECT prod_name
FROM products
WHERE prod_name REGEXP '^[0-9\\.]' // 匹配以数字或小数点开头的字符串
^
在[]
中表示否定该集合,取该集合的反,如[^123]
;否则用来指串的开始处
SELECT CONCAT('a', 'b', 'c'); -- 结果为’abc‘
多数DBMS使用+或||来实现拼接,MySQL则使用CONCAT()
函数来实现。
SELECT RTRIM('FADFA '); -- 清除右边的空格
SELECT LTRIM(' FADFA'); -- 清除左边的空格
SELECT TRIM('FADFA'); -- 清除所有的空格
为了更好的引用某一列,可以对某一列取一个别名
SELECT CONCAT(RTRIM(vend_name), ' (', RTRIM(vend_country), ')') AS vend_title -- AS后面就是别名
FROM vendors;
函数 | 说明 |
---|---|
Left() |
返回串左边的字符 |
Right() |
返回串右边的字符 |
Length() |
返回串的长度 |
Lower() |
将串转换为小写 |
Upper() |
将串转换为大写 |
LTrim() |
去除串左边的字符 |
RTrim() |
去除串右边的字符 |
Locate() |
找出串的一个字串 |
Soundex() |
返回串的SOUNDEX值 |
SubString() |
返回字串的字符 |
-- 从左开始数的,截取字符串前n个
SELECT LEFT('foobarbar', 5) -- -> 'fooba'
-- 从右开始数的,截取字符串后n个
SELECT RIGHT('foobarbar', 4) -- -> 'rbar'
-- 定位子字符串在字符串中的第一个位置,从1开始。如果找不到就返回0
SELECT LOCATE('bar', 'foobarbar') ---> 4
SELECT LOCATE('xbar', 'foobar') -- -> 0
SELECT LOCATE('bar', 'foobarbar', 5) -- -> 7
-- 子字符串,从第n个开始截取
SELECT SUBSTRING('Quadratically',5) -- -> 'ratically'
-- 子字符串,从第n个开始截取
SELECT SUBSTRING('foobarbar' FROM 4) -- -> 'barbar'
-- 子字符串,从第n个开始截取m个
SELECT SUBSTRING('Quadratically',5,6) -- -> 'ratica'
-- 子字符串,从倒数第n个开始截取至字符串尾
SELECT SUBSTRING('Sakila', -3) -- -> 'ila'
-- 子字符串,从倒数第n个开始,往前截取m个
SELECT SUBSTRING('Sakila', -5, 3) -- -> 'aki'
-- 子字符串,从倒数第n个开始,往前截取m个
SELECT SUBSTRING('Sakila' FROM -4 FOR 2) ---> 'ki'
SELECT SOUNDEX('Hello') -- -> 'H400'
SELECT SOUNDEX('Quadratically') ---> 'Q36324'
-- 查找匹配所有发音类似的字符串
SELECT cust_name, cust_contact
FROM customers
WHERE Soundex(cust_contact) = Soundex('Y Lie');
函数 | 说明 |
---|---|
CurDate() |
返回当前日期 |
CurTime() |
返回当前时间 |
Now() |
返回当前日期和时间 |
Date() |
返回日期时间的日期部分 |
Time() |
返回日期时间的时间部分 |
Year() |
返回日期时间的年份部分 |
Month() |
返回日期时间的月份部分 |
Day() |
返回日期时间的天数部分 |
Hour() |
返回日期时间的小时部分 |
Minute() |
返回日期时间的分钟部分 |
Second() |
返回日期时间的秒部分 |
AddDate() |
增加一个日期(天、周等) |
AddTime() |
增加一个时间(时、分等) |
DateDiff() |
返回两个日期之差 |
Date_Add() |
高度灵活的日期运算函数 |
Date_Format() |
返回一个格式化之后的日期或时间串 |
DayOfWeek() |
对于一个日期,返回对应的星期几 |
SELECT NOW() -- -> '2007-12-15 23:50:26'
SELECT NOW() + 0 -- -> 20071215235026.000000
SELECT ADDDATE('2008-01-02', 31) -- -> '2008-02-02'
SELECT ADDTIME('2007-12-31 23:59:59.999999', '1 1:1:1.000002') -- -> '2008-01-02 01:01:01.000001'
SELECT ADDTIME('01:00:00.999999', '02:00:00.999998') -- -> '03:00:01.999997'
SELECT DATEDIFF('2007-12-31 23:59:59','2007-12-30') -- -> 1
SELECT DATEDIFF('2010-11-30 23:59:59','2010-12-31') ---> -31
SELECT DAYOFWEEK('2007-02-03') -- -> 7
WHERE
子句进行过滤,日期必须为yyyy-mm-dd
格式。WHERE order_date = '2005-09-01'
不可靠。因为order_time
的数据类型为datatime
,这种类型存储日期及时间值。默认是时间值为00:00:00
。因此,通过WHERE
筛选日期的时候,容易因为时间对不上而无法筛选出来。更安全的做法是:SELECT cust_id, order_num
FROM orders
WHERE Date(order_date) = '2005-09-01';
Date()
,这是一个良好的习惯。-- 筛选某个月份的行
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;
函数 | 说明 |
---|---|
Abs() |
返回绝对值 |
Sin() |
返回一个角度的正弦(弧度制) |
Cos() |
返回一个角度的余弦 |
Tan() |
返回一个角度的正切 |
Exp() |
返回一个角度的指数值 |
Mod() |
返回除操作的余数 |
Sqrt() |
返回一个数的平方根 |
Pi() |
返回圆周率 |
Rand() |
返回一个随机数 |
函数 | 说明 |
---|---|
COUNT() |
返回某列的行数 |
MAX() |
返回某列的最大值 |
MIN() |
返回某列的最小值 |
AVG() |
返回某列的平均值 |
SUM() |
返回某列值之和 |
null
的行。COUNT()
函数时,如果使用COUNT(*)
,会对所有行进行计数,但是当使用COUNT(COLUMNS_NAME)
时,如果该列的某行为null
,则会被直接忽略。AVG()
函数时,如果某行为null
,则不会纳入计算的范围,既不占个数,也不对值产生影响,会被直接忽略。DISTINCT
只计算包含不同的值。SELECT COUNT(DISTINCT price)
FROM book_list;
SELECT AVG(DISTINCT price)
FROM book_list;
SELECT price, COUNT(price)
FROM book_list
GROUP BY price;
GROUP BY
子句必须出现在WHERE
子句之后,ORDER BY
子句之前。即,先筛选,后分组,最后排序。
SELECT price, COUNT(price)
FROM book_list
GROUP BY price
ORDER BY price;
SELECT price, COUNT(price)
FROM book_list
GROUP BY price
HAVING COUNT(price) > 8;
HAVING
支持所有WHERE
操作符。所学到的有关WHERE
的所有这些技术和选项都适用于HAVING
。它们句式相同,只是关键字有差别。WHERE
在数据分组前进行过滤,HAVING
在数据分组后进行过滤。这是一个重要的区别, WHERE
排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING
子句中基于这些值过滤掉的分组。GROUP BY
子句时,应该也给出ORDER BY
子句,这是保证数据正确排序的唯一办法。千万不要仅仅依赖GROUP BY
排序数据。SELECT
语句中子句的顺序。SELECT
|要返回的列或表达式|是|FROM
|从中检索数据的表|仅在从表选择数据时使用|WHERE
|行级过滤|否|GROUP BY
|分组说明|仅在按组计算聚集时使用|HAVING
|组级过滤|否|ORDER BY
|输出排序顺序|否|LIMIT
|要检索的行数|否|-- 查询所有购买`TN2`的用户的姓名
SELECT user_name
FROM user_info
WHERE id in (
SELECT id
FROM orders
WHERE prod_id = 'TN2'
);
in
括号里面的查询,然后处理外面的查询。WHERE
子句中使用子查询能够编写出功能很强并且很灵活的SQL语句。但在实际使用时,由于性能的限制,不能嵌套太多的子查询WHERE orders.cust_id = customers.cust_id;
join
)表。CREATE TABLE departments(
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(20) NOT NULL
);
CREATE TABLE employees (
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20) NOT NULL,
gender VARCHAR(10),
email VARCHAR(30),
dept_id INT,
CONSTRAINT fk_emp_dept -- 外键
FOREIGN KEY(dept_id) REFERENCES departments(dept_id)
);
SELECT vend_name, prod_name, prod_price
FROM vendors, products
WHERE vendors.vend_id = products.vend_id
ORDER BY vend_name, prod_name;
FROM
子句列出了两个表,它们就是这条SELECT
语句联结的两个表的名字。两个表用WHERE
子句正常联结。WHERE
作为联结之后的过滤条件,如果不使用WHERE
的话,就将第一个表的每个行与第二个表的每个行以组合的形式,一一出现。(笛卡尔积)SELECT vend_name, prod_name, prod_price
FROM vendors INNER JOIN products
ON vendors.vend_id = products.vend_id;
INNER JOIN...ON...
的方法联结。INNER JOIN
语法。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_name = 20005;
-- 子查询的方式
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 = 'DTNDR';
SELECT *
),对其他表的列使用明确的子集来完成。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';
SELECT customers.cust_id, orders.order_num
FROM customers LEFT OUTER JOIN orders
ON customers.cust_id = orders.cust_id;
OUTER JOIN
的时候,必须用RIGHT
或LEFT
关键字指定包括其所有行的表。RIGHT
指出的是OUTER JOIN
右边的表,LEFT
指出的是左边的表。两种类型实际上效果是一样的,只是顺序不一样而已。RIGHT
/LEFT
的所有与另一边的交集进行合并。另一边不存在的部分,就用null
来表示。SELECT customers.cust_name, customers.cust_id, COUNT(orders.order_name) 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
规则:SELECT
语句组成,语句之间用关键字UNION
分隔。UNION
中的每个查询必须包含相同的列、表达式或聚集函数。(不过各个列之间不需要以相同的顺序出现。)UNION
中,重复的行被自动取消。如果不想去除重复行,那就使用UNION ALL
ORDER BY
语句即可。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_id) -- 全文本搜索索引
) ENGINE=MyISAM; -- 指定搜索引擎
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('rabbit'); -- 在note_text列搜索所有包含'rabbit'的列
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('anvils' WITH QUERY EXPANSION);
FULLTEXT
索引也可以使用,但这是一种非常缓慢的操作。SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy' IN BOOLEAN MODE); -- 使用布尔文本搜索
SELECT note_text
FROM productnotes
WHERE Match(note_text) Aganist('heavy -rope*' IN BOOLEAN MODE); -- 匹配'heavy' 但不匹配包含'rope'开头的词的句子
布尔操作符 | 说明 |
---|---|
+ | 包含,词必须存在 |
- | 排除,词必须不出现 |
> | 包含,而且增加等级值 |
< | 包含,且减少等级值 |
() | 把词组成子表达式(允许这些子表达式作为一个组被包含、排除、排列等) |
~ | 取消一个词的排序值 |
* | 词尾的通配符 |
“” | 定义一个短语(与单个词的列表不一样,它匹配整个短语以便包含或排除这个短语) |
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'的行(至少有一个)
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 IN BOOLEAN MODE) -- 包含rabbit或carrot,且增大rabbit的等级,减小carrot的等级
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('+safe +( IN BOOLEAN MODE) -- 必须包含两个词,且降低'combination'的等级
INSERT INTO customers
VALUES(NULL, 'Pep', '100', 'Los Angeles', 'CA', '90046', 'USA', NULL, NULL);
NULL
值。虽然这种语法很简单,但是并不安全,应该尽量避免使用。上面SQL高度依赖于表中列的定义次序,并且还依赖于其次序容易获得的信息,也不能保证下一次表结构变动后各个列保持完全相同的次序。INSERT INTO customers(cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_eamil)
VALUES('Pep', '100', 'Los Angeles', 'CA', '90046', 'USA', NULL, NULL);
INSERT
语句,使用列的列表能使SQL代码继续发挥作用,即使表结构发生了变化。INSERT
和INTO
之间添加关键字LOW_PRIORITY
,指示MySQL降低INSERT
语句的优先级(也适用于UPDATE
和DELETE
):INSERT LOW_PRIORITY INTO
INSERT
处理多个插入比使用多条INSERT
语句快。INSERT INTO customers(cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_eamil)
VALUES('Pep', '100', 'Los Angeles', 'CA', '90046', 'USA', NULL, NULL),
('M', '42', 'New York', 'NY', '11213', 'USA');
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;
INSERT SELECT
通过列位置的一一对应关系进行插入,列名不一定要一样。
UPDATE
组成部分UPDATE customers
SET cust_email = '[email protected]' -- 设置新值
WHERE cust_id = 1005; -- 筛选条件
UPDATE customers
SET cust_name = 'The Fudds', -- 设置多列
cust_email = '[email protected]'
WHERE cust_id = 1005; -- 筛选条件
IGNORE
关键字UPDATE
语句更新多行时一行或多行出现错误,也继续进行更新,可以使用IGNORE
关键字。UPDATE IGNORE customers...
NULL
UPDATE customers
SET cust_email = NULL -- 去除某列的值
WHERE cust_id = 10005;
DELETE FROM customers
WHERE cust_id = 10006;
DELETE FROM customers;
TRUNCATE TABLE customers;
UPDATE
和DELETE
时所遵循的习惯:WHERE
WHERE
子句使用主键进行筛选UPDATE
和DELETE
之前,使用SELECT
子句进行测试,保证筛选的数据是正确的。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;
DROP TABLE IF EXISTS customers;
CREATE TABLE customers IF NOT EXISTS
(
...
);
NULL
值NULL
列,或者是NOT NULL
列,这种状态在创建时由表的定义规定。使用NOT NULL
的列不接受该列没有值的列,换句话说,在插入或更新行时,该列必须有值。NULL
为默认设置,如果不指定NOT NULL
,则认为指定的是NULL
。PRIMARY KEY (vend_id)
PRIMARY KEY (order_num, order_item)
AUTO_INCREMENT
AUTO_INCREMENT
告诉MySQL,本列每当增加一行时,自动增量。每个表只允许一个AUTO_INCREMENT
列,而且它必须被索引。AUTO_INCREMENT
,则它可以简单地INSERT
指定一个值,只要它是唯一的。后续的增量将开始使用该手工插入的值。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;
NULL
列,特别是用于计算或数据分组的列更是如此。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);
复杂的表结构一般需要手动删除过程,它涉及以下步骤:
INSERT SELECT
语句从旧表复制数据到新表。DROP TABLE customers2;
RENAME TABLE customers2 to customers;
RENAME TABLE customers2 to customers,
customers3 to customers2,
customers4 to customers3;
ORDER BY
可以用在视图中,但如果从该视图检索数据的SELECT
语句中也含有ORDER BY
,那么该视图中的ORDER BY
将被覆盖SELECT
语句CREATE VIEW
语句来创建SHOW CREATE VIEW viewname
来查看创建视图的语句DROP VIEW viewname
删除视图DROP
再用CREATE
,也可以直接用CREATE OR REPLACE VIEW
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;
view
SELECT cust_name, cust_contact -- 列
FROM productcustomers -- VIEW
WHERE prod_id = 'TNT2'; -- 筛选条件
利用视图,可一次性编写基础的SQL,然后根据需要多次使用。
CREATE VIEW vendorlocations AS -- 创建视图
SELECT Concat(RTrim(vend_name), '(', RTrim(vend_country), ')') -- 格式化检索出来的数据
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;
CREATE VIEW orderitemsexpanded AS
SELECT order_num, prod_id, quantity, item_price, quantity*item_price AS expanded_price
FROM orderitems;
INSERT
、UPDATE
、DELETE
操作。如果对视图增加或删除行,实际上是对其基表的增加或删除行。Min()
、Count()
、Sum()
等)DISTINCE
CREATE PROCEDURE productpricing() -- 定义存储过程,如果接受参数,则在括号里面定义
BEGIN -- 限制过程体
SELECT Avg(prod_price) AS priceaverage
FROM products;
END;
;
作为语句分隔符,内部也有;
,会造成过程体提前结束的误会。因此,需要临时修改语句分隔符。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 -- 将查询结果保存在pl中
FROM products;
SELECT Max(prod_price)
INTO ph
FROM products;
SELECT Avg(prod_price)
INTO pa
FROM products;
END;
IN
(传递给存储过程)、OUT
(从存储过程传出)和INOUT
(对存储过程传入和传出)类型的参数。CALL productpricing(@pricelow, -- MySQL所有变量都必须以@开始
@pricehigh,
@priceage
);
SELECT @pricelow; -- 显示检索结果
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;
CALL ordertotal(20005. @total); -- 调用存储过程
SELECT @total; -- 查看结果
CREATE PROCEDURE ordertotal(
IN onumber INT,
IN taxable BOOLEAN,
OUT ototal DECIMAL(8,2)
) COMMENT 'Obtain order total, optionally adding tax' -- 将在`SHOW PROCEDURE STATUS`上显示这句话
BEGIN
DECLARE total DECIMAL(8,2); -- 声明总量的变量
DECLARE taxrate INT DEFAULT 6; -- 声明税率百分比
SELECT Sum(item_price*quantity)
FROM orderitems
WHERE order_num = onumber
INTO total; -- 将计算出来的总量,存储在total变量中
IF taxable THEN -- 是否应税的
SELECT total+(total/100*taxrate) INTO total;
END IF;
SELECT total INTO ototal; -- 将结果保存在ototal上
END;
调用:
CALL ordertotal(20005, 0, @total); -- 调用存储过程
SELECT @total; -- 显示结果
SHOW CREATE PROCEDURE ordertotal; -- 显示用来创建一个存储过程的CREATE语句
SHOW PROCEDURE STATUS; -- 将显示现存在的所有存储过程的信息,包括何时、由谁创建等详细信息
SHOW PROCEDURE STATUS LIKE 'search_user'; -- 通过通配符进行筛选
CREATE PROCEDURE processorders()
BEGIN
DECLARE ordernumbers CURSOR -- 声明游标
FOR
SELECT order_num FROM orders;
OPEN ordernumbers; -- 开启游标
CLOSE ordernumbers; -- 关闭游标
END;
FETCH
来检索当前行的数据(默认从第一行开始)DECLARE o INT; -- 声明变量
OPEN ordernumbers; -- 开启游标
FETCH ordernumbers INTO o; -- 将当前行的数据获取并保存在变量o中
CLOSE ordernumbers; -- 关闭游标
CREATE PROCEDURE processorders()
BEGIN
DECLARE done BOOLEAN DEFAULT 0; -- 声明局部变量,完成标志位
DECLARE o INT; -- 局部变量
DECLARE ordernumbers CURSOR -- 声明游标
FOR
SELECT order_num FROM orders;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1; -- 在满足条件的时候(SQLSTATE = '02000'的时候,设置done为1)
OPEN ordernumbers; -- 开启游标
-- 开启循环
REPEAT
FETCH ordernumbers INTO o; -- 将数据保存在局部变量
UNTIL done END REPEAT; -- 关闭循环条件(done=1)
CLOSE ordernumbers; -- 关闭游标
END;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
这句话定义了一个CONTINUE HANDLER
,它在条件出现时被执行的语句。这里,它指出当SQLSTATE '02000'
出现时,SET done=1
INSERT
、UPDATE
、DELETE
,在事件发生之前、之后都可以设置触发器。INSERT
、UPDATE
、DELETE
)CREATE TRIGGER newproduct AFTER INSERT ON products -- 触发器名为'newproduct',在触发之后执行,关联的表为'products'
FOR EACH ROW SELECT 'Product added'; -- 在每一行发生改变的时候,输出'Product added'
DROP TRIGGER newproduct;
INSERT
触发器INSERT
触发器代码内,可引用一个名为NEW
的虚拟表,访问被插入的行BEFORE INSERT
触发器中,NEW
中的值也可以被更新(允许更新被插入的值,实现提前格式化插入数据)AUTO_INCREMENT
列,NEW
在INSERT
执行之前包含0,在INSERT
执行之后包含新的自动生成值。CREATE TRIGGER neworder AFTER INSERT ON orders
FOR EACH ROW SELECT NEW.order_num; -- 查询自动生成的值order_num,在insert之后
DELETE
触发器DELETE
触发器代码内,可以引用一个名为OLD
的虚拟表,访问被删除的行。OLD
中的值都是只读的,不能更新。-- 使用OLD保存将要被删除的行到一个存档表中
CREATE TRIGGER deleteorder BEFORE DELETE ON orders -- 在删除之前
FOR EACH ROW
BEGIN
INSERT INTO achieve_orders(order_num, order_datae, cust_id) -- 将旧值保存在一个叫做'achieve_orders'的表中
VALUES(OLD.order_num, OLD.order_date, OLD.cust_id);
END;
使用BEFORE DELETE
而不使用AFTER DELETE
的优点是,防止由于某种原因,造成订单不能存档。
UPDATE
触发器UPDATE
触发器代码中,可以引用NEW
访问新更新的值,也可以引用OLD
访问以前的值。BEFORE UPDATE
触发器中,NEW
中的值可能也被更新(允许更新将要用于UPDATE
的值)OLD
的值全都是只读的,不能更新CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors
FOR EACH ROW SET NEW.vend_state = UPPER(NEW.vend_state); -- 在更新之前,将插入的内容全部变成大写
ROLLBACK
SELECT * FROM ordertotals;
START TRANSACTION; -- 开始事务
DELETE FROM ordertotals; -- 清空表
SELECT * FROM ordertotals; -- 查询表为空
ROLLBACK; -- 回滚
SELECT * FROM ordertotals; -- 发现表并不为空
ROLLBACK
可以实现撤销的操作,回滚到事务开启之前的状态。INSERT
、DELETE
、UPDATE
(增删改)操作。不能回退CREATE
、DROP
操作。COMMIT
START TRANSACTION;
DELETE FROM orderitems WHERE order_num = 20010;
DELETE FROM orders WHERE order_num = 20010;
COMMIT; -- 提交事务。实现仅当事务内都不出错的时候,才提交事务
为了实现部分回退。通过保留点,可以回退到某个占位符。
SAVEPOINT delete1; -- 保留点
ROLLBACK TO delete1; -- 回滚到指定保留点
ROLLBACK
或COMMIT
)后自动释放。SET autocommit=0;
-- 开启事务
SET autocommit=0; -- 关闭自动提交
START TRANSACTION; -- 可省略
USE girls;
-- 事务
UPDATE boys
SET userCP=2000
WHERE id=7;
UPDATE boys
SET boyName='郭富城'
WHERE id=6;
COMMIT; -- 提交
-- rollback; -- 返回,则执行事务
SHOW CHARACTER SET;
SHOW COLLATION;
CREATE TABLE mytable
(
column1 INT,
column2 VARCHAR(10)
) DEFAULT CHARACTER SET hebrew, -- 设置字符集
COLLATE hebrew_general_ci; -- 设置校对
USE mysql;
SELECT user FROM user; -- 获得所有用户账号列表
CREATE USER ben IDENTIFIED BY 'ben'; -- 创建一个新用户,名为ben,密码为ben
IDENTIFIED BY
将对指定的口令进行加密
RENAME USER ben TO ben1;
DROP USER ben1;
在创建用户账号之后,必须接着分配访问权限。新创建的用户账号没有访问权限。它们能登录MySQL,但不能看到数据,不能执行任何数据库操作。
查看用户账号的权限
SHOW GRANTS FOR ben;
设置权限,至少给出以下信息:
– 要授予的权限
– 要授予访问权限的数据库或表
– 用户名
GRANT SELECT ON crashcourse.* TO ben; -- 将crashcourse的SELECT权赋予给ben用户。即ben用户具有crashcourse数据库所有表的只读权利
REVOKE SELECT ON crashcourse.* FROM ben; -- 将取消赋予ben用户对于crashcourse数据库的只读权利
GRANT ALL ON bookstore.* TO ben; -- 将赋予ben在bookstore数据库上所有权利
REVOKE ALL ON bookstore.* FROM ben;
GRANT SELECT, INSERT ON bookstore.* TO ben; -- 多个权限用逗号分隔
SET PASSWORD FOR ben = Password('ben'); -- 更改密码
SET PASSWORD = Password('ben'); -- 设置当前用户的登录口令
mysqldump
转储所有数据库内容到某个外部文件mysqlhotcopy
从一个数据库复制所有数据BACKUP TABLE
或SELECT INTO OUTFILE
转储所有数据到某个外部文件ANALYZE TABLE orders; -- 用来检查表键是否正确
CHECK TABLE orders, orderitems; -- 用来针对许多问题对表进行检查
--help # 显示帮助
--safe-mode # 装载减去某些最佳配置的服务器
--verbose # 显示全文本消息
--version # 显示版本信息然后退出
hostname.err
,位于data目录中,此日志名可用--log-error
命令行选项更改。hostname.log
,位于data目录中,此日志名可用--log
命令行选项更改。hostname-bin
,位于data目录中,此日志名可用--log-bin
命令行选项更改。hostname-slow.log
,位于data目录中。此名字可以通过--log-slow-queries
命令行选项更改。SELECT
语句,应该试验联结、并、子查询等,找出最佳方法。SELECT *
WHERE
子句返回结果所花的时间太长,则可以断定其中使用的列(或几个列)就是需要索引的对象。SELECT
若有大量的OR
条件。通过使用多条SELECT
语句和连接它们的UNION
语句,可以看到极大的性能改善。LIKE
很慢,一般来说,最好用FULLTEXT
而不是LIKE