<font size =3> [toc]
基本语法
SELECT
检索一列
SELECT lastName
FROM employees;
检索多列
SELECT
lastname,
firstname,
jobtitle
FROM
employees;
检索整个表
SELECT *
FROM employees;
ORDER BY
基本用法
SELECT
select_list
FROM
table_name
ORDER BY
column1 [ASC|DESC],
column2 [ASC|DESC],
...;
ASC代表上升、DESC代表下降,默认使用ASC 先根据column1进行上升排序,再根据column2进行下降排序
根据一列排序(默认是升序.ASC)
SELECT
contactLastname,
contactFirstname
FROM
customers
ORDER BY
contactLastname;
根据一列指定降序排列
SELECT
contactLastname,
contactFirstname
FROM
customers
ORDER BY
contactLastname DESC;
按多列进行排序
SELECT
contactLastname,
contactFirstname
FROM
customers
ORDER BY
contactLastname DESC,
contactFirstname ASC;
对列进行运算后,按运算结果排序
SELECT
orderNumber,
orderlinenumber,
quantityOrdered * priceEach
FROM
orderdetails
ORDER BY
quantityOrdered * priceEach DESC;
将运算结果作为一个新的列别名
SELECT
orderNumber,
orderLineNumber,
quantityOrdered * priceEach AS subtotal
FROM
orderdetails
ORDER BY subtotal DESC;
SELECT语句在ORDER BY之前执行,所以ORDER BY可以访问subtotal
通过FILED()函数进行指定排序
SELECT
orderNumber,
status
FROM
orders
ORDER BY
FIELD(status,
'In Process',
'On Hold',
'Cancelled',
'Resolved',
'Disputed',
'Shipped');
WHERE
WHERE基本语法
SELECT
select_list
FROM
table_name
WHERE
search_condition;
除了SELECT外,DELETE和UPDATE也可以通过WHERE来指定更新和删除的行
在FROM语句之后,SELECT语句之前执行WHERE语句
WHERE子句中使用相等运算符
SELECT
lastname,
firstname,
jobtitle
FROM
employees
WHERE
jobtitle = 'Sales Rep';
WHERE子句中使用AND
SELECT
lastname,
firstname,
jobtitle,
officeCode
FROM
employees
WHERE
jobtitle = 'Sales Rep' AND
officeCode = 1;
WHERE语句中使用OR
SELECT
lastName,
firstName,
jobTitle,
officeCode
FROM
employees
WHERE
jobtitle = 'Sales Rep' OR
officeCode = 1
ORDER BY
officeCode ,
jobTitle;
WHERE语句中使用BETWEEN
BETWEEN基本语法
expression BETWEEN low AND high
SELECT
firstName,
lastName,
officeCode
FROM
employees
WHERE
officeCode BETWEEN 1 AND 3
ORDER BY officeCode;
在WHERE语句中使用LIKE
LIKE语句用法
//如果与LIKE指定的匹配,则为TRUE;
% 表示字符串前有0~多个字符;_表示指定字符串前有一个字符
mysql> SELECT
-> firstName,
-> lastName
-> FROM
-> employees
-> WHERE
-> lastName LIKE '%son'
-> ORDER BY firstName;
+-----------+-----------+
| firstName | lastName |
+-----------+-----------+
| Leslie | Thompson |
| Mary | Patterson |
| Steve | Patterson |
| William | Patterson |
+-----------+-----------+
4 rows in set (0.03 sec)
在WHERE语句中使用IN
IN语句的用法
value IN (value1,value2,...)
SELECT
firstName,
lastName,
officeCode
FROM
employees
WHERE
officeCode IN (1 , 2, 3)
ORDER BY
officeCode;
在WHERE语句中使用IS NULL
IS NULL的用法
判断一个value是否是NULL,不应该使用equeal语法,而是使用IS NULL. 在数据库世界中,NULL是标记,指示缺少或未知的信息,不等于数字0或者空字符串。
value IS NULL
WHERE语句与比较运算符一起使用
不等于
SELECT
lastname,
firstname,
jobtitle
FROM
employees
WHERE
jobtitle <> 'Sales Rep';
大于
SELECT
lastname,
firstname,
officeCode
FROM
employees
WHERE
officecode > 5;
小于
SELECT
lastname,
firstname,
officeCode
FROM
employees
WHERE
officecode <= 4;
DISTINCT
DISTINCT的用法
从表中查询数据时,会得到重复的行,当需要删除这些重复的行时,需要使用DISTINCT
SELECT DISTINCT
select_list
FROM
table_name;
不使用DISTINCT
mysql> SELECT
-> lastName
-> FROM
-> employees
-> ORDER BY
-> lastName;
+-----------+
| lastName |
+-----------+
| Bondur |
| Bondur |
| Bott |
| Bow |
| Castillo |
| Firrelli |
| Firrelli |
| Fixter |
| Gerard |
| Hernandez |
| Jennings |
| Jones |
| Kato |
| King |
| Marsh |
| Murphy |
| Nishi |
| Patterson |
| Patterson |
| Patterson |
| Thompson |
| Tseng |
| Vanauf |
+-----------+
不使用DISTINCT
mysql> SELECT
-> DISTINCT lastName
-> FROM
-> employees
-> ORDER BY
-> lastName;
+-----------+
| lastName |
+-----------+
| Bondur |
| Bott |
| Bow |
| Castillo |
| Firrelli |
| Fixter |
| Gerard |
| Hernandez |
| Jennings |
| Jones |
| Kato |
| King |
| Marsh |
| Murphy |
| Nishi |
| Patterson |
| Thompson |
| Tseng |
| Vanauf |
+-----------+
DISTINCT会把所有的NULL变成一个
mysql> SELECT DISTINCT state
-> FROM customers;
+---------------+
| state |
+---------------+
| NULL |
| NV |
| Victoria |
| CA |
| NY |
| PA |
| CT |
| MA |
| Osaka |
| BC |
| Québec |
| Isle of Wight |
| NSW |
| NJ |
| Queensland |
| Co. Cork |
| Pretoria |
| NH |
| Tokyo |
+---------------+
19 rows in set (0.03 sec)
DISTINCT和多个列一起使用
当两行的多个列的值都相同,就去掉。
SELECT DISTINCT
state, city
FROM
customers
WHERE
state IS NOT NULL
ORDER BY
state,
city;
DISTINCT与GROUP BY相互比较
相同的效果</br> 不同的效果在于GROUP BY会排序,DISTINCT不会
DISTINCT和集合函数
可以将DISTINCT与集合函数(SUM/AVG/COUNT),将集合函数应用于结果之前删除重复的行
mysql> SELECT
-> COUNT(DISTINCT state)
-> FROM
-> customers
-> WHERE
-> country='USA';
+-----------------------+
| COUNT(DISTINCT state) |
+-----------------------+
| 8 |
+-----------------------+
1 row in set (0.02 sec)
DISTINCT与LIMIT语句一起使用
当MySQL找到LIMIT子句中唯一指定的行数时,立即停止搜索。
mysql> SELECT DISTINCT
-> state
-> FROM
-> customers
-> WHERE
-> state IS NOT NULL
-> LIMIT 5;
+----------+
| state |
+----------+
| NV |
| Victoria |
| CA |
| NY |
| PA |
+----------+
5 rows in set (0.02 sec)
AND
AND语句组合多个布尔表达式来过滤数据
短路评估
就是当能够确定结果时,就不执行剩余部分
SELECT
customername,
country,
state
FROM
customers
WHERE
country = 'USA' AND state = 'CA';
AND联合多个布尔表达式
mysql> SELECT
-> customername,
-> country,
-> state,
-> creditlimit
-> FROM
-> customers
-> WHERE
-> country='USA' AND
-> state ='CA' AND
-> creditlimit > 100000;
+------------------------------+---------+-------+-------------+
| customername | country | state | creditlimit |
+------------------------------+---------+-------+-------------+
| Mini Gifts Distributors Ltd. | USA | CA | 210500 |
| Collectable Mini Designs Co. | USA | CA | 105000 |
| Corporate Gift Ideas Co. | USA | CA | 105000 |
+------------------------------+---------+-------+-------------+
3 rows in set (0.03 sec)
OR
短路评估
运算优先级
AND运算符的优先级 > OR运算符的优先级
使用SELECT()改变运算符的优先级
SELECT(true OR false) AND false;
SELECT
customername,
country
FROM
customers
WHERE country = 'USA' OR
country = 'France';
AND与OR一起使用
SELECT
customername,
country,
creditLimit
FROM
customers
WHERE(country = 'USA'
OR country = 'France')
AND creditlimit > 100000;
IN
IN的基本语法结构
SELECT
column1,column2,...
FROM
table_name
WHERE
(expr|column_1) IN ('value1','value2',...);
在WHERE子句中使用column或expr 列表中的值用逗号分隔
当column或者expr等于list中的值时,返回1,否则返回0.
当列表中的值为常量时
- 执行根据column_1的类型或者expr表达式的结果
- 对values进行排序
- 使用二叉搜索算法查找value
当IN与常量表达式算法一起执行时,速度非常快。
mysql> SELECT
-> officeCode,
-> city,
-> phone,
-> country
-> FROM
-> offices
-> WHERE
-> country IN('USA','France');
+------------+---------------+-----------------+---------+
| officeCode | city | phone | country |
+------------+---------------+-----------------+---------+
| 1 | San Francisco | +1 650 219 4782 | USA |
| 2 | Boston | +1 215 837 0825 | USA |
| 3 | NYC | +1 212 555 3000 | USA |
| 4 | Paris | +33 14 723 4404 | France |
+------------+---------------+-----------------+---------+
IN语句与多个OR相同,但是使用IN更简单
使用IN
SELECT
officeCode,
city,
phone
FROM
offices
WHERE
country NOT IN ('USA' , 'France');
使用OR
SELECT
officeCode,
city,
phone
FROM
offices
WHERE
country = 'USA' OR country = 'France';
IN和NOT一起使用
mysql> SELECT
-> officeCode,
-> city,
-> country,
-> phone
-> FROM
-> offices
-> WHERE
-> country NOT IN('USA','France');
+------------+--------+-----------+------------------+
| officeCode | city | country | phone |
+------------+--------+-----------+------------------+
| 5 | Tokyo | Japan | +81 33 224 5000 |
| 6 | Sydney | Australia | +61 2 9264 2451 |
| 7 | London | UK | +44 20 7877 2041 |
+------------+--------+-----------+------------------+
IN和subquery一起使用
mysql> SELECT
orderNumber,
customerNumber,
status,
shippedDate
FROM
orders
WHERE orderNumber IN
(
SELECT
orderNumber
FROM
orderdetails
GROUP BY
orderNumber
HAVING SUM(quantityOrdered * priceEach) > 60000
);
+-------------+----------------+---------+-------------+
| orderNumber | customerNumber | status | shippedDate |
+-------------+----------------+---------+-------------+
| 10165 | 148 | Shipped | 2003-12-26 |
| 10287 | 298 | Shipped | 2004-09-01 |
| 10310 | 259 | Shipped | 2004-10-18 |
+-------------+----------------+---------+-------------+
3 rows in set (0.03 sec)
是下述两个查询结果的组合:
mysql> SELECT
orderNumber
FROM
orderdetails
GROUP BY
orderNumber
HAVING
SUM(quantityOrdered * priceEach) > 60000;
+-------------+
| orderNumber |
+-------------+
| 10165 |
| 10287 |
| 10310 |
+-------------+
3 rows in set (0.03 sec)
mysql> SELECT
orderNumber,
customerNumber,
status,
shippedDate
FROM
orders
WHERE
orderNumber IN (10165,10287,10310);
+-------------+----------------+---------+-------------+
| orderNumber | customerNumber | status | shippedDate |
+-------------+----------------+---------+-------------+
| 10165 | 148 | Shipped | 2003-12-26 |
| 10287 | 298 | Shipped | 2004-09-01 |
| 10310 | 259 | Shipped | 2004-10-18 |
+-------------+----------------+---------+-------------+
3 rows in set (0.03 sec)
BETWEEN
判断values是否在a range of values
BETWEEN的用法
expr [NOT] BETWEEN begin_expr AND end_expr;
begin_expr和end_expr和expr必须有相同的类型
当expr属于[begin_expr,end_expr]时,返回1</br> 当expr为NULL时,BETWEEN返回NULL
mysql> SELECT
-> productCode,
-> productName,
-> buyPrice
-> FROM
-> products
-> WHERE
-> buyPrice BETWEEN 80 AND 100;
+-------------+--------------------------------------+----------+
| productCode | productName | buyPrice |
+-------------+--------------------------------------+----------+
| S10_1949 | 1952 Alpine Renault 1300 | 98.58 |
| S10_4698 | 2003 Harley-Davidson Eagle Drag Bike | 91.02 |
| S10_4757 | 1972 Alfa Romeo GTA | 85.68 |
| S12_1099 | 1968 Ford Mustang | 95.34 |
| S12_1108 | 2001 Ferrari Enzo | 95.59 |
| S12_3148 | 1969 Corvair Monza | 89.14 |
| S12_3891 | 1969 Ford Falcon | 83.05 |
| S18_1129 | 1993 Mazda RX-7 | 83.51 |
| S18_1749 | 1917 Grand Touring Sedan | 86.7 |
| S18_1984 | 1995 Honda Civic | 93.89 |
| S18_4027 | 1970 Triumph Spitfire | 91.92 |
| S18_4600 | 1940s Ford truck | 84.76 |
| S24_2011 | 18th century schooner | 82.34 |
| S24_3856 | 1956 Porsche 356A Coupe | 98.3 |
+-------------+--------------------------------------+----------+
14 rows in set (0.02 sec)
NOT BETWEEN
mysql> SELECT
productCode,
productName,
buyPrice
FROM
products
WHERE
buyPrice NOT BETWEEN 20 AND 100;
+-------------+-------------------------------------+----------+
| productCode | productName | buyPrice |
+-------------+-------------------------------------+----------+
| S10_4962 | 1962 LanciaA Delta 16V | 103.42 |
| S18_2238 | 1998 Chrysler Plymouth Prowler | 101.51 |
| S24_2840 | 1958 Chevy Corvette Limited Edition | 15.91 |
| S24_2972 | 1982 Lamborghini Diablo | 16.24 |
+-------------+-------------------------------------+----------+
4 rows in set (0.03 sec)
BETWEEN与日期一起使用
BETWEEN与日期一起使用,应该将expr类型显示转换为DATE类型
mysql> SELECT
orderNumber,
requiredDate,
status
FROM
orders
WHERE
requireddate BETWEEN
CAST('2003-01-01' AS DATE) AND
CAST('2003-01-31' AS DATE);
+-------------+--------------+---------+
| orderNumber | requiredDate | status |
+-------------+--------------+---------+
| 10100 | 2003-01-13 | Shipped |
| 10101 | 2003-01-18 | Shipped |
| 10102 | 2003-01-18 | Shipped |
+-------------+--------------+---------+
3 rows in set (0.01 sec)
LIKE
用于测试字符串是否包含指定的模式
expression LIKE pattern ESCAPE escape_character
使用%匹配多个字符
匹配前缀
mysql> SELECT
-> employeeNumber,
-> lastName,
-> firstName
-> FROM
-> employees
-> WHERE
-> firstName LIKE 'a%';
+----------------+----------+-----------+
| employeeNumber | lastName | firstName |
+----------------+----------+-----------+
| 1143 | Bow | Anthony |
| 1611 | Fixter | Andy |
+----------------+----------+-----------+
2 rows in set (0.02 sec)
匹配后缀
mysql> SELECT
employeeNumber,
lastName,
firstName
FROM
employees
WHERE
lastName LIKE '%on';
+----------------+-----------+-----------+
| employeeNumber | lastName | firstName |
+----------------+-----------+-----------+
| 1056 | Patterson | Mary |
| 1088 | Patterson | William |
| 1166 | Thompson | Leslie |
| 1216 | Patterson | Steve |
+----------------+-----------+-----------+
4 rows in set (0.03 sec)
匹配中间
mysql> SELECT
employeeNumber,
lastName,
firstName
FROM
employees
WHERE
lastname LIKE '%on%';
+----------------+-----------+-----------+
| employeeNumber | lastName | firstName |
+----------------+-----------+-----------+
| 1056 | Patterson | Mary |
| 1088 | Patterson | William |
| 1102 | Bondur | Gerard |
| 1166 | Thompson | Leslie |
| 1216 | Patterson | Steve |
| 1337 | Bondur | Loui |
| 1504 | Jones | Barry |
+----------------+-----------+-----------+
7 rows in set (0.03 sec)
使用_匹配单个字符
mysql> SELECT
employeeNumber,
lastName,
firstName
FROM
employees
WHERE
firstname LIKE 'T_m';
+----------------+----------+-----------+
| employeeNumber | lastName | firstName |
+----------------+----------+-----------+
| 1619 | King | Tom |
+----------------+----------+-----------+
1 row in set (0.01 sec)
NOT与LIKE一起使用
//匹配不是以B开头的,对大小写不敏感
mysql> SELECT
employeeNumber,
lastName,
firstName
FROM
employees
WHERE
lastName NOT LIKE 'B%';
LIKE与ESCAPE一起使用
使用ESCAPE子句指定转义符,以便MySQL将通配符解释为文字字符,默认\为转义字符
使用默认转义字符
mysql> SELECT
-> productCode,
-> productName
-> FROM
-> products
-> WHERE
-> productCode LIKE '%\_20%';
+-------------+-------------------------------------------+
| productCode | productName |
+-------------+-------------------------------------------+
| S10_2016 | 1996 Moto Guzzi 1100i |
| S24_2000 | 1960 BSA Gold Star DBD34 |
| S24_2011 | 18th century schooner |
| S24_2022 | 1938 Cadillac V-16 Presidential Limousine |
| S700_2047 | HMS Bounty |
+-------------+-------------------------------------------+
5 rows in set (0.01 sec)
自己指定转义字符
mysql> SELECT
-> productCode,
-> productName
-> FROM
-> products
-> WHERE
-> productCOde LIKE '%$_20%' ESCAPE '$';
+-------------+-------------------------------------------+
| productCode | productName |
+-------------+-------------------------------------------+
| S10_2016 | 1996 Moto Guzzi 1100i |
| S24_2000 | 1960 BSA Gold Star DBD34 |
| S24_2011 | 18th century schooner |
| S24_2022 | 1938 Cadillac V-16 Presidential Limousine |
| S700_2047 | HMS Bounty |
+-------------+-------------------------------------------+
5 rows in set (0.02 sec)
LIMIT
限制查询返回的行数
SELECT
select_list
FROM
table_name
LIMIT [offset,] row_count;
offset 指定要返回的第一行的偏移量,第一行的偏移量是0,而不是1 row_count返回的是最大行数 偏移量是3,往下数4个。
LIMIT语句和ORDER BY一起使用
SELECT select_list
FROM table_name
ORDER BY order_expression
LIMIT offset, row_count;
LIMIT举例
ORDER BY 按照creditLimit 降序排序 LIMIT返回5行
SELECT
customerNumber,
customerName,
creditLimit
FROM
customers
ORDER BY creditLimit DESC
LIMIT 5;
使用LIMIT进行分页
获取第一页
SELECT
customerNumber,
customerName
FROM
customers
ORDER BY customerName
LIMIT 10;
获取第二页
SELECT
customerNumber,
customerName
FROM
customers
ORDER BY customerName
LIMIT 10, 10;
使用LIMIT获取第n个最大或最小值
SELECT select_list
FROM table_name
ORDER BY sort_expression
LIMIT n-1, 1;
IS NULL
IS NULL基本用法
value IS NULL //如果为NULL,则返回true,否则返回false
SELECT
customerName,
country,
salesrepemployeenumber
FROM
customers
WHERE
salesrepemployeenumber IS NULL
ORDER BY
customerName;
IS NULL优化
SELECT
customerNumber,
salesRepEmployeeNumber
FROM
customers
WHERE
salesRepEmployeeNumber IS NULL;
alias
列别名和表别名
alias用法
SELECT
[column_1 | expression] AS descriptive_name
FROM table_name;
如果别名中有空格:
SELECT
[column_1 | expression] AS `descriptive name`
FROM
table_name;
alias示例
mysql> SELECT
CONCAT_WS(', ', lastName, firstname) AS `Full name`
FROM
employees;
+--------------------+
| Full name |
+--------------------+
| Murphy, Diane |
| Patterson, Mary |
| Firrelli, Jeff |
| Patterson, William |
| Bondur, Gerard |
| Bow, Anthony |
| Jennings, Leslie |
| Thompson, Leslie |
| Firrelli, Julie |
| Patterson, Steve |
| Tseng, Foon Yue |
| Vanauf, George |
| Bondur, Loui |
| Hernandez, Gerard |
| Castillo, Pamela |
| Bott, Larry |
| Jones, Barry |
| Fixter, Andy |
| Marsh, Peter |
| King, Tom |
| Nishi, Mami |
| Kato, Yoshimi |
| Gerard, Martin |
+--------------------+
23 rows in set (0.04 sec)
alias与其他clause使用
mysql> SELECT
orderNumber `Order no.`,
SUM(priceEach * quantityOrdered) total
FROM
orderdetails
GROUP BY
`Order no.`
HAVING
total > 60000;
+-----------+--------------------+
| Order no. | total |
+-----------+--------------------+
| 10165 | 67392.84999999999 |
| 10287 | 61402 |
| 10310 | 61234.669999999984 |
+-----------+--------------------+
3 rows in set (0.02 sec)
不能在WHERE子句中使用alias,因为在执行WHERE语句时,SELECT语句并未执行
alias用于表别名
SELECT
e.firstName,
e.lastName
FROM
employees e //表别名
ORDER BY e.firstName;
SELECT
customerName,
COUNT(o.orderNumber) total
FROM
customers c
INNER JOIN orders o ON c.customerNumber = o.customerNumber
GROUP BY
customerName
ORDER BY
total DESC;
join
通过外键连接多个表,因此从业务角度讲,每个表中的数据都不完整。
MySQL支持的连接
- Inner join
- Left join
- Right join
- Cross join
Inner join用法
SELECT column_list
FROM table_1
INNER JOIN table_2 ON join_condition;
如果联结条件使用运算符(=),并且两个表中用于匹配的列名称相同,则改用USING语句
SELECT column_list
FROM table_1
INNER JOIN table_2 USING (column_name);
inner join举例
mysql> SELECT
m.member_id,
m.name,
c.committee_id,
c.name
FROM
members m
INNER JOIN committees c
ON c.name = m.name;
+-----------+--------+--------------+--------+
| member_id | name | committee_id | name |
+-----------+--------+--------------+--------+
| 1 | John | 1 | John |
| 3 | Mary | 2 | Mary |
| 5 | Amelia | 3 | Amelia |
+-----------+--------+--------------+--------+
3 rows in set (0.03 sec)
当两个表有相同的列名时,使用USING替代join-predicate
mysql> SELECT
-> m.member_id,
-> m.name,
-> c.committee_id,
-> c.name
-> FROM
-> members m
-> INNER JOIN committees c USING(name);
+-----------+--------+--------------+--------+
| member_id | name | committee_id | name |
+-----------+--------+--------------+--------+
| 1 | John | 1 | John |
| 3 | Mary | 2 | Mary |
| 5 | Amelia | 3 | Amelia |
+-----------+--------+--------------+--------+
3 rows in set (0.01 sec)
LEFT JOIN
当左表和右表中有行相同时,将创建新的行,包含左表和右表的所有列;没有相同行,也要创建新的行,只包含左表,右表为空。
LEFT JOIN用法
SELECT column_list
FROM table_1
LEFT JOIN table_2 ON join_condition;
或者使用下面这种方式:
SELECT column_list
FROM table_1
LEFT JOIN table_2 USING (column_name);
举例
mysql> SELECT
m.member_id,
m.name,
c.committee_id,
c.name
FROM
members m
LEFT JOIN committees c USING(name);
+-----------+--------+--------------+--------+
| member_id | name | committee_id | name |
+-----------+--------+--------------+--------+
| 1 | John | 1 | John |
| 3 | Mary | 2 | Mary |
| 5 | Amelia | 3 | Amelia |
| 2 | Jane | NULL | NULL |
| 4 | David | NULL | NULL |
+-----------+--------+--------------+--------+
5 rows in set (0.03 sec)
左表全部存在,右表只有与左表name相同的才存在
LEFT JOIN与WHERE一起使用
mysql> SELECT
-> m.member_id,
-> m.name,
-> c.committee_id,
-> c.name
-> FROM
-> members m
-> LEFT JOIN committees c USING(name)
-> WHERE c.committee_id IS NULL;
+-----------+-------+--------------+------+
| member_id | name | committee_id | name |
+-----------+-------+--------------+------+
| 2 | Jane | NULL | NULL |
| 4 | David | NULL | NULL |
+-----------+-------+--------------+------+
2 rows in set (0.04 sec)
RIGHT JOIN
mysql> SELECT
-> m.member_id,
-> m.name,
-> c.committee_id,
-> c.name
-> FROM
-> members m
-> RIGHT JOIN committees c ON c.name = m.name;
+-----------+--------+--------------+--------+
| member_id | name | committee_id | name |
+-----------+--------+--------------+--------+
| 1 | John | 1 | John |
| 3 | Mary | 2 | Mary |
| 5 | Amelia | 3 | Amelia |
| NULL | NULL | 4 | Joe |
+-----------+--------+--------------+--------+
4 rows in set (0.01 sec)
CROSS JOIN
并没有join condition, n行的表1和m行的表2,通过交叉连接后悔称为nXm行的表
CROSS JOIN举例
SELECT select_list
FROM table_1
CROSS JOIN table_2;
mysql> SELECT
-> m.member_id,
-> m.name,
-> c.committee_id,
-> c.name
-> FROM
-> members m
-> CROSS JOIN committees c;
+-----------+--------+--------------+--------+
| member_id | name | committee_id | name |
+-----------+--------+--------------+--------+
| 1 | John | 4 | Joe |
| 1 | John | 3 | Amelia |
| 1 | John | 2 | Mary |
| 1 | John | 1 | John |
| 2 | Jane | 4 | Joe |
| 2 | Jane | 3 | Amelia |
| 2 | Jane | 2 | Mary |
| 2 | Jane | 1 | John |
| 3 | Mary | 4 | Joe |
| 3 | Mary | 3 | Amelia |
| 3 | Mary | 2 | Mary |
| 3 | Mary | 1 | John |
| 4 | David | 4 | Joe |
| 4 | David | 3 | Amelia |
| 4 | David | 2 | Mary |
| 4 | David | 1 | John |
| 5 | Amelia | 4 | Joe |
| 5 | Amelia | 3 | Amelia |
| 5 | Amelia | 2 | Mary |
| 5 | Amelia | 1 | John |
+-----------+--------+--------------+--------+
20 rows in set (0.03 sec)
交叉连接对于生成计划数据很有用、
self join
need to join a table to itself 查询层次数据结构,或者将表中的某一行与其他行进行比较。要执行self join,必须在表中使用别名,因为在单个查询中不能重复的使用两次表名。
self join举例
mysql> SELECT
-> CONCAT(m.lastName,',',m.firstName) AS Manager,
-> CONCAT(e.lastName,',',e.firstName)AS 'Direct report'
-> FROM
-> employees e
-> INNER JOIN employees m ON //表有两个别名
-> m.employeeNumber = e.reportsTo
-> ORDER BY
-> Manager;
使用MySQL自连接比较连续的行
mysql> SELECT
c1.city,
c1.customerName,
c2.customerName
FROM
customers c1
INNER JOIN customers c2 ON
c1.city = c2.city //确保两个顾客有相同的城市
AND c1.customername > c2.customerName //不是同一个人
ORDER BY
c1.city;
GROUP BY
group rows into subgroups based on values of columns or expressions.
GROUP BY用法
SELECT
c1, c2,..., cn, aggregate_function(ci)
FROM
table
WHERE
where_conditions
GROUP BY c1 , c2,...,cn;
在FROM/WHERE/SELECT后执行GROUP BY,在HAVING、ORDER BY、LIMIT前执行
GROUP BY举例
mysql> SELECT
-> status
-> FROM
-> orders
-> GROUP BY status;
+------------+
| status |
+------------+
| Shipped |
| Resolved |
| Cancelled |
| On Hold |
| Disputed |
| In Process |
+------------+
GROUP BY与集合函数一起用
mysql> SELECT
-> status,COUNT(*)
-> FROM
-> orders
-> GROUP BY status;
+------------+----------+
| status | COUNT(*) |
+------------+----------+
| Shipped | 303 |
| Resolved | 4 |
| Cancelled | 6 |
| On Hold | 4 |
| Disputed | 3 |
| In Process | 6 |
+------------+----------+
6 rows in set (0.01 sec)
GROUP BY和HAVING一起使用
mysql> SELECT
-> YEAR(orderDate) AS year,
-> SUM(quantityOrdered*priceEach) AS total
-> FROM
-> orders
-> INNER JOIN orderdetails
-> USING (orderNumber)
-> WHERE
-> status='Shipped'
-> GROUP BY
-> year
-> HAVING
-> year>2003;
+------+--------------------+
| year | total |
+------+--------------------+
| 2004 | 4300602.990000001 |
| 2005 | 1341395.8499999992 |
+------+--------------------+
2 rows in set (0.55 sec)
HAVING
指定一个过滤条件
SELECT
select_list
FROM
table_name
WHERE
search_condition
GROUP BY
group_by_expression
HAVING
group_condition;`
WHEREj将过滤条件应用于每行,而HAVING将过滤条件应用于每组行
mysql> SELECT
ordernumber,
SUM(quantityOrdered) AS itemsCount,
SUM(priceeach*quantityOrdered) AS total
FROM
orderdetails
GROUP BY
ordernumber
HAVING
total < 1000;
+-------------+------------+--------+
| ordernumber | itemsCount | total |
+-------------+------------+--------+
| 10408 | 15 | 615.45 |
+-------------+------------+--------+
1 row in set (0.03 sec)
HAVING总是和GROUP BY一起使用
SELECT
a.ordernumber,
status,
SUM(priceeach*quantityOrdered) total
FROM
orderdetails a
INNER JOIN orders b
ON b.ordernumber = a.ordernumber
GROUP BY
ordernumber,
status
HAVING
status = 'Shipped' AND
total > 1500;
ROLLUP
生成小计和总计 ROLLUP是GROUP BY子句的扩展,有以下语法:
SELECT
select_list
FROM
table_name
GROUP BY
c1, c2, c3 WITH ROLLUP;
Subquery(子查询)
括号内的称为子查询,子查询返回外部查询所需要
SELECT
lastName, firstName
FROM
employees
WHERE
officeCode IN (SELECT
officeCode
FROM
offices
WHERE
country = 'USA');
Derived 表(派生表)
派生表类似于临时表,但是不需要创建,十分方便。 派生表和子查询可以互换。派生表必须具有别名
SELECT
column_list
FROM
(SELECT
column_list
FROM
table_1) derived_table_name;
WHERE derived_table_name.c1 > 0;
top5products2003是一个派生表
mysql> SELECT
productName, sales
FROM
(SELECT
productCode,
ROUND(SUM(quantityOrdered * priceEach)) sales
FROM
orderdetails
INNER JOIN orders USING (orderNumber)
WHERE
YEAR(shippedDate) = 2003
GROUP BY productCode
ORDER BY sales DESC
LIMIT 5) top5products2003
INNER JOIN
products USING (productCode);
更复杂的派生表用法
SELECT
customerNumber,
ROUND(SUM(quantityOrdered * priceEach)) sales,
(CASE
WHEN SUM(quantityOrdered * priceEach) < 10000 THEN 'Silver'
WHEN SUM(quantityOrdered * priceEach) BETWEEN 10000 AND 100000 THEN 'Gold'
WHEN SUM(quantityOrdered * priceEach) > 100000 THEN 'Platinum'
END) customerGroup
FROM
orderdetails
INNER JOIN
orders USING (orderNumber)
WHERE
YEAR(shippedDate) = 2003
GROUP BY customerNumber;
SELECT
customerGroup,
COUNT(cg.customerGroup) AS groupCount
FROM
(SELECT
customerNumber,
ROUND(SUM(quantityOrdered * priceEach)) sales,
(CASE
WHEN SUM(quantityOrdered * priceEach) < 10000 THEN 'Silver'
WHEN SUM(quantityOrdered * priceEach) BETWEEN 10000 AND 100000 THEN 'Gold'
WHEN SUM(quantityOrdered * priceEach) > 100000 THEN 'Platinum'
END) customerGroup
FROM
orderdetails
INNER JOIN orders USING (orderNumber)
WHERE
YEAR(shippedDate) = 2003
GROUP BY customerNumber) cg
GROUP BY cg.customerGroup;
EXISTS
布尔表达式:返回true或者false 在找到匹配的行后,立即终止进一步的查询
SELECT
customerNumber,
customerName
FROM
customers
WHERE
EXISTS(
SELECT
1
FROM
orders
WHERE
orders.customernumber
= customers.customernumber);
在此示例中,对于customers表中的每一行,查询将检查orders表中的customerNumber。如果出现在customers表中的customerNumber存在于orders表中,则子查询将返回第一个匹配的行。结果,EXISTS运算符返回true,并停止检查订单表。否则,子查询不返回任何行,并且EXISTS运算符返回false。
MySQL CTE
common table expression 与派生表类似,CTE不存储为对象,仅在查询执行期间存储。与派生表不同,CTE可以是自引用(递归CTE),也可以在同一查询中多次引用。另外,与派生表相比,CTE提供更好的可读性和性能。
CTE的用法
WITH cte_name (column_list) AS (
query
)
SELECT * FROM cte_name;
CTE举例
WITH customers_in_usa AS (
SELECT
customerName, state --name of CTE
FROM
customers
WHERE
country = 'USA'
) SELECT
customerName
FROM
customers_in_usa
WHERE
state = 'CA'
ORDER BY customerName;
WITH topsales2003 AS (
SELECT
salesRepEmployeeNumber employeeNumber,
SUM(quantityOrdered * priceEach) sales
FROM
orders
INNER JOIN
orderdetails USING (orderNumber)
INNER JOIN
customers USING (customerNumber)
WHERE
YEAR(shippedDate) = 2003
AND status = 'Shipped'
GROUP BY salesRepEmployeeNumber
ORDER BY sales DESC
LIMIT 5
)
SELECT
employeeNumber,
firstName,
lastName,
sales
FROM
employees
JOIN
topsales2003 USING (employeeNumber);
两个CTE
WITH salesrep AS (
SELECT
employeeNumber,
CONCAT(firstName, ' ', lastName) AS salesrepName
FROM
employees
WHERE
jobTitle = 'Sales Rep'
),
customer_salesrep AS (
SELECT
customerName, salesrepName
FROM
customers
INNER JOIN
salesrep ON employeeNumber = salesrepEmployeeNumber
)
SELECT
*
FROM
customer_salesrep
ORDER BY customerName;
WITH
WITH可以与SELECT/UPDATE/DELETE一起用。
WITH ... SELECT ...
WITH ... UPDATE ...
WITH ... DELETE ...
在子查询或者派生查询的开头使用WITH子句。
SELECT ... WHERE id IN (WITH ... SELECT ...);
SELECT * FROM (WITH ... SELECT ...) AS derived_table;
在包含SELECT子句的SELECT前面使用WITH
CREATE TABLE ... WITH ... SELECT ...
CREATE VIEW ... WITH ... SELECT ...
INSERT ... WITH ... SELECT ...
REPLACE ... WITH ... SELECT ...
DECLARE CURSOR ... WITH ... SELECT ...
EXPLAIN ... WITH ... SELECT ...
Recursive CTE
遍历分层数据
Recursive CTE的用法
WITH RECURSIVE cte_name AS (
initial_query -- anchor member
UNION ALL
recursive_query -- recursive member that references to the CTE name
)
SELECT * FROM cte_name;
WITH RECURSIVE cte_count (n)
AS (
SELECT 1 --base result set
UNION ALL
SELECT n + 1
FROM cte_count
WHERE n < 3 --termination condition
)
SELECT n
FROM cte_count;
使用recursive CTE遍历层次数据
WITH RECURSIVE employee_paths AS
( SELECT employeeNumber,
reportsTo managerNumber,
officeCode,
1 lvl
FROM employees
WHERE reportsTo IS NULL
UNION ALL
SELECT e.employeeNumber,
e.reportsTo,
e.officeCode,
lvl+1
FROM employees e
INNER JOIN employee_paths ep ON ep.employeeNumber = e.reportsTo )
SELECT employeeNumber,
managerNumber,
lvl,
city
FROM employee_paths ep
INNER JOIN offices o USING (officeCode)
ORDER BY lvl, city;
UNION and UNION ALL
将多个查询的两个或多个结果合并成一个结果集
- SELECT中的列的数量和顺序一定是相同的
- 列的数据类型也一定是相同的或者兼容的
UNION的用法
SELECT column_list
UNION [DISTINCT | ALL]
SELECT column_list
UNION [DISTINCT | ALL]
SELECT column_list
JOIN和UNION的区别
JOIN是水平组合的,UNION是垂直组合的
UNION和ORDER BY一起使用
INTERSECT
模拟交叉运算符的方法 MySQL不支持INTERSECT运算,使用join子句在MySQL中模拟INTERSECT运算。
INTERSECT的用法
(SELECT column_list
FROM table_1)
INTERSECT
(SELECT column_list
FROM table_2);
使用DISTINCT和INNER JOIN模拟INTERSECT
mysql> CREATE TABLE t1 (
id INT PRIMARY KEY
);
CREATE TABLE t2 LIKE t1;
INSERT INTO t1(id) VALUES(1),(2),(3);
INSERT INTO t2(id) VALUES(2),(3),(4);
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.15 sec)
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT DISTINCT
id
FROM t1
INNER JOIN t2 USING(id);
+----+
| id |
+----+
| 2 |
| 3 |
+----+
使用IN来模拟INTERSECT
SELECT DISTINCT id
FROM t1
WHERE id IN (SELECT id FROM t2);
MINUS
解释MINUS运算符 MySQL不支持MINUS运算,比较两个查询结果并从第一个查询结果中返回不出现在第二个查询结果中的不同行。
MINUS的基本用法
SELECT select_list1
FROM table_name1
MINUS
SELECT select_list2
FROM table_name2;
使用LEFT JOIN来模拟MINUS
SELECT
select_list
FROM
table1
LEFT JOIN table2
ON join_predicate
WHERE
table2.column_name IS NULL;
[toc]
[toc]
INSERT
INSERT语法
INSERT INTO table(c1,c2,...)
VALUES
(v11,v12,...),
(v21,v22,...),
...
(vnn,vn2,...);
INSERT INTO SELECT
在从另一个表中拷贝数据时,非常的重要
使用SELECT的结果作为插入的源数据
INSERT INTO table_name(column_list)
SELECT
select_list
FROM
another_table
WHERE
condition;
INSERT INTO SELECT举例
mysql> SELECT
customerNumber,
customerName,
phone,
addressLine1,
addressLine2,
city,
state,
postalCode,
country
FROM
customers
WHERE
country = 'USA' AND
state = 'CA';
MySQL INSERT ON DUPLICATE KEY UPDATE
当您插入一行数据时,如果导致UNIQUE index或者PRIMARY KEY重复,MySQL将会报错。
INSERT ON DUPLICATE KEY UPDATE用法
基本上,该语句首先尝试在表中插入新行。如果发生重复错误,它将使用ON DUPLICATE KEY UPDATE子句中指定的值更新现有行。
INSERT INTO table (column_list)
VALUES (value_list)
ON DUPLICATE KEY UPDATE
c1 = v1,
c2 = v2,
...;
举例
mysql> CREATE TABLE devices (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO devices(name)
VALUES('Router F1'),('Switch 1'),('Switch 2');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> INSERT INTO
devices(name)
VALUES
('Printer')
ON DUPLICATE KEY UPDATE name = 'Printer';
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO devices(id,name)
VALUES
(4,'Printer')
ON DUPLICATE KEY UPDATE name = 'Central Printer';
Query OK, 2 rows affected (0.00 sec)
INSERT IGNORE
如果使用INSERT IGNORE语句,则会忽略包含导致错误的无效数据的行,并将具有有效数据的行插入表中。
INSERT IGNORE举例
CREATE TABLE subscribers (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(50) NOT NULL UNIQUE
);
//-- 当插入重复的值时,会有警告,但是仍然会插入正确的操作
mysql> INSERT INTO subscribers(email)
VALUES('john.doe@gmail.com'),
('jane.smith@ibm.com');
1062 - Duplicate entry 'john.doe@gmail.com' for key 'email'
mysql> INSERT IGNORE INTO subscribers(email)
VALUES('john.doe@gmail.com'),
('jane.smith@ibm.com');
Query OK, 1 row affected (0.01 sec)
Records: 2 Duplicates: 1 Warnings: 1
唯一约束保证email中没有重复的值
INSERT IGNORE 和SELECT同时执行
当我们在严格模式下插入无效数据时,会报错;不过当我们使用INSERT INGORE 后,MySQL会自动调整数据。
mysql> CREATE TABLE tokens(
-> s VARCHAR(6)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO tokens
-> VALUES('ABEDEFG');
1406 - Data too long for column 's' at row 1
mysql> INSERT IGNORE INTO tokens VALUES('abcdefg');
Query OK, 1 row affected (0.00 sec)
自动将插入字符串长度调整为6
UPDATE
更新表中的数据
UPDATE [LOW_PRIORITY] [IGNORE] table_name
SET
column_name1 = expr1,
column_name2 = expr2,
...
[WHERE
condition];
WHERE可选的用于更新某一行
两种更新的模式
LOW_PRIORITY
延迟更新直到没有连接读取表中的数据。 LOW_PRIORITY仅仅用于表级锁定的存储引擎。
IGNORE
UPDATE即使发生错误也可以继续执行,但是发生错误的行并不会得到更新
UPDATE举例
更新一列
mysql> SELECT
-> firstname,
-> lastname,
-> email
-> FROM
-> employees
-> WHERE
-> employeeNumber=1056;
+-----------+-----------+--------------------------------+
| firstname | lastname | email |
+-----------+-----------+--------------------------------+
| Mary | Patterson | mpatterso@classicmodelcars.com |
+-----------+-----------+--------------------------------+
1 row in set (0.02 sec)
mysql> UPDATE employees
SET
email = 'mary.patterson@classicmodelcars.com'
WHERE
employeeNumber = 1056;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT
firstname,
lastname,
email
FROM
employees
WHERE
employeeNumber = 1056;
+-----------+-----------+-------------------------------------+
| firstname | lastname | email |
+-----------+-----------+-------------------------------------+
| Mary | Patterson | mary.patterson@classicmodelcars.com |
+-----------+-----------+-------------------------------------+
1 row in set (0.02 sec)
更新多列
UPDATE employees
SET
lastname = 'Hill',
email = 'mary.hill@classicmodelcars.com'
WHERE
employeeNumber = 1056;
UPDATE employees
SET email = REPLACE(email,'@classicmodelcars.com','@mysqltutorial.org')
WHERE
jobTitle = 'Sales Rep' AND
officeCode = 6;
UPDATE JOIN
使用JOIN和UPDATE用于交叉表更新
UPDATE T1, T2,
[INNER JOIN | LEFT JOIN] T1 ON T1.C1 = T2. C1
SET T1.C2 = T2.C2,
T2.C3 = expr
WHERE condition
UPDATE T1, T2
SET T1.c2 = T2.c2,
T2.c3 = expr
WHERE T1.c1 = T2.c1 AND condition
UPDATE T1,T2
INNER JOIN T2 ON T1.C1 = T2.C1
SET T1.C2 = T2.C2,
T2.C3 = expr
WHERE condition
UPDATE JOIN举例
mysql> CREATE DATABASE IF NOT EXISTS empdb;
USE empdb;
-- create tables
CREATE TABLE merits (
performance INT(11) NOT NULL,
percentage FLOAT NOT NULL,
PRIMARY KEY (performance)
);
Query OK, 1 row affected (0.01 sec)
Database changed
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE employees (
emp_id INT(11) NOT NULL AUTO_INCREMENT,
emp_name VARCHAR(255) NOT NULL,
performance INT(11) DEFAULT NULL,
salary FLOAT DEFAULT NULL,
PRIMARY KEY (emp_id),
CONSTRAINT fk_performance FOREIGN KEY (performance)
REFERENCES merits (performance)
);
Query OK, 0 rows affected (0.02 sec)
mysql> -- insert data for merits table
INSERT INTO merits(performance,percentage)
VALUES(1,0),
(2,0.01),
(3,0.03),
(4,0.05),
(5,0.08);
-- insert data for employees table
INSERT INTO employees(emp_name,performance,salary)
VALUES('Mary Doe', 1, 50000),
('Cindy Smith', 3, 65000),
('Sue Greenspan', 4, 75000),
('Grace Dell', 5, 125000),
('Nancy Johnson', 3, 85000),
('John Doe', 2, 45000),
('Lily Bush', 3, 55000);
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
Query OK, 7 rows affected (0.00 sec)
Records: 7 Duplicates: 0 Warnings: 0
mysql> UPDATE employees
-> INNER JOIN
-> merits ON employees.performance=merits.performance
-> SET
-> salary=salary+salary*percentage
-> ;
Query OK, 6 rows affected (0.00 sec)
Rows matched: 7 Changed: 6 Warnings: 0
mysql> INSERT INTO employees(emp_name,performance,salary)
VALUES('Jack William',NULL,43000),
('Ricky Bond',NULL,52000);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> UPDATE employees
LEFT JOIN
merits ON employees.performance = merits.performance
SET
salary = salary + salary * 0.015
WHERE
merits.percentage IS NULL;
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
DELETE
从一个表中删除数据
DELETE FROM table_name
WHERE condition;
- 删除表中的所有数据使用TRUNCATE TABLE
- 有外键约束时,当删除父表中的数据时,使用ON DELETE CASCADE自动删除子表中的数据、
DELETE举例
mysql> DELETE FROM employees
-> WHERE
-> emp_id = 3;
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM employees
-> WHERE
-> salary=78750;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM employees;
Query OK, 8 rows affected (0.01 sec)
DELETE与LIMIT一起使用
-- 限制删除的行数
DELETE FROM table
LIMIT row_count;
-- 对要删除的数据进行排序
DELETE FROM table_name
ORDER BY c1, c2, ...
LIMIT row_count;
DELETE JOIN
使用DELETE JOIN从多个表中删除数据
DELETE JOIN和INNER JOIN一起使用
DELETE T1, T2
FROM T1
INNER JOIN T2 ON T1.key = T2.key
WHERE condition;
DELETE JOIN和INNER JOIN举例
mysql> DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1 (
id INT PRIMARY KEY AUTO_INCREMENT
);
CREATE TABLE t2 (
id VARCHAR(20) PRIMARY KEY,
ref INT NOT NULL
);
INSERT INTO t1 VALUES (1),(2),(3);
INSERT INTO t2(id,ref) VALUES('A',1),('B',2),('C',3);
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> DELETE t1,t2 FROM t1
INNER JOIN
t2 ON t2.ref = t1.id
WHERE
t1.id = 1;
Query OK, 2 rows affected (0.01 sec)
使用LEFT JOIN删除多个表的数据
DELETE T1
FROM T1
LEFT JOIN
T2 ON T1.key = T2.key
WHERE
T2.key IS NULL;
ON DELETE CASCADE
建筑表和房间表,一个建筑中有多个房间。 当你删除一个建筑时,rooms中的所有行业会被删除。
创建buildings表和rooms表
mysql> CREATE TABLE buildings (
building_no INT PRIMARY KEY AUTO_INCREMENT,
building_name VARCHAR(255) NOT NULL,
address VARCHAR(255) NOT NULL
);
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE rooms (
room_no INT PRIMARY KEY AUTO_INCREMENT,
room_name VARCHAR(255) NOT NULL,
building_no INT NOT NULL,
FOREIGN KEY (building_no)
REFERENCES buildings (building_no)
ON DELETE CASCADE
);
Query OK, 0 rows affected (0.02 sec)
插入数据行
mysql> INSERT INTO buildings(building_name,address)
VALUES('ACME Headquaters','3950 North 1st Street CA 95134'),
('ACME Sales','5000 North 1st Street CA 95134');
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql>
SELECT * FROM buildings;
+-------------+------------------+--------------------------------+
| building_no | building_name | address |
+-------------+------------------+--------------------------------+
| 1 | ACME Headquaters | 3950 North 1st Street CA 95134 |
| 2 | ACME Sales | 5000 North 1st Street CA 95134 |
+-------------+------------------+--------------------------------+
2 rows in set (0.02 sec)
mysql> INSERT INTO rooms(room_name,building_no)
VALUES('Amazon',1),
('War Room',1),
('Office of CEO',1),
('Marketing',2),
('Showroom',2);
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql>
SELECT * FROM rooms;
+---------+---------------+-------------+
| room_no | room_name | building_no |
+---------+---------------+-------------+
| 1 | Amazon | 1 |
| 2 | War Room | 1 |
| 3 | Office of CEO | 1 |
| 4 | Marketing | 2 |
| 5 | Showroom | 2 |
+---------+---------------+-------------+
5 rows in set (0.04 sec)
当从building中删除时,rooms中所有building_no=2的数据也将被清除
mysql> DELETE FROM buildings
WHERE building_no = 2;
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM rooms;
+---------+---------------+-------------+
| room_no | room_name | building_no |
+---------+---------------+-------------+
| 1 | Amazon | 1 |
| 2 | War Room | 1 |
| 3 | Office of CEO | 1 |
+---------+---------------+-------------+
3 rows in set (0.03 sec)
查询哪些数据会收到ON DELETE CASCADE的影响
USE information_schema;
SELECT
table_name
FROM
referential_constraints
WHERE
constraint_schema = 'database_name'
AND referenced_table_name = 'parent_table'
AND delete_rule = 'CASCADE'
REPLACE
REPLACE [INTO] table_name(column_list)
VALUES(value_list);
REPLACE用法
当插入发生冲突时,REPLACE会先删除原先的行,然后再插入新的行。
REPLACE举例
mysql> CREATE TABLE cities (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
population INT NOT NULL
);
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO cities(name,population)
VALUES('New York',8008278),
('Los Angeles',3694825),
('San Diego',1223405);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql>
mysql> SELECT * FROM cities;
+----+-------------+------------+
| id | name | population |
+----+-------------+------------+
| 1 | New York | 8008278 |
| 2 | Los Angeles | 3694825 |
| 3 | San Diego | 1223405 |
+----+-------------+------------+
3 rows in set (0.03 sec)
mysql> REPLACE INTO cities(id,population)
VALUES(2,3696820);
Query OK, 2 rows affected (0.01 sec)
mysql> REPLACE INTO cities(id,population)
VALUES(2,3696820);
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM cities;
+----+-----------+------------+
| id | name | population |
+----+-----------+------------+
| 1 | New York | 8008278 |
| 2 | NULL | 3696820 |
| 3 | San Diego | 1223405 |
+----+-----------+------------+
3 rows in set (0.04 sec)
REPLACE 和SELECT一起用
REPLACE INTO
cities(name,population)
SELECT
name,
population
FROM
cities
WHERE id = 1;
准备语句
来使你的查询执行的更快和更安全
- PREPARE:准备一个语句用于执行
- EXECUTE:执行一个准备好的语句
- DEALLOCATE PREPARE:释放一个准备好的语句
-- 准备一个语句
mysql> PREPARE stmt1 FROM
'SELECT
productCode,
productName
FROM products
WHERE productCode = ?';
Query OK, 0 rows affected (0.01 sec)
Statement prepared
mysql> SET @pc = 'S10_1678';
Query OK, 0 rows affected (0.00 sec)
-- 执行该语句
mysql> EXECUTE stmt1 USING @pc;
+-------------+---------------------------------------+
| productCode | productName |
+-------------+---------------------------------------+
| S10_1678 | 1969 Harley Davidson Ultimate Chopper |
+-------------+---------------------------------------+
1 row in set (0.01 sec)
mysql> SET @pc = 'S12_1099';
Query OK, 0 rows affected (0.00 sec)
mysql> EXECUTE stmt1 USING @pc;
+-------------+-------------------+
| productCode | productName |
+-------------+-------------------+
| S12_1099 | 1968 Ford Mustang |
+-------------+-------------------+
1 row in set (0.01 sec)
-- 释放该语句
mysql> DEALLOCATE PREPARE stmt1;
Query OK, 0 rows affected (0.00 sec)
Table Locking
Lock Table的语法
-- 锁定单个表
LOCK TABLES table_name [READ | WRITE]
-- 锁定多个表
LOCK TABLES table_name1 [READ | WRITE],
table_name2 [READ | WRITE],
... ;
读写锁的概念
- 表的读锁可以同时被多个会话获取,另外,其他的会话可以在不获取该锁的情况下读取数据
- 拥有读锁的会话只能从表中读取数据,而不能写。其他会话不能向表中写入数据,直到读锁被释放。其他会话的写操作在读锁释放前,被放入等待状态。
- 如果会话终止,无论是正常的还是非正常的,MySQL都会隐式的释放所有锁。
读锁举例
mysql> CREATE TABLE messages (
id INT NOT NULL AUTO_INCREMENT,
message VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
);
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT CONNECTION_ID();
+-----------------+
| CONNECTION_ID() |
+-----------------+
| 13 |
+-----------------+
1 row in set (0.01 sec)
mysql> INSERT INTO messages(message)
VALUES('Hello');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM messages;
+----+---------+
| id | message |
+----+---------+
| 1 | Hello |
+----+---------+
1 row in set (0.02 sec)
mysql>
LOCK TABLE messages READ;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO messages(message)
VALUES('Hi');
1099 - Table 'messages' was locked with a READ lock and can't be updated
mysql> SELECT CONNECTION_ID();
当第一个会话加入读锁后,其他会话仍然可以读该表,第一个会话不能插入,其他的会话要插入,会进入等待状态,当第一个会话释放读锁时,第二个会话的插入操作就会执行。
写锁举例
- 唯一拥有写锁的会话可以从表中读写数据
- 其他会话在写锁释放前,既不能读数据、也不能写数据。
mysql> LOCK TABLE messages WRITE;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO messages(message)
VALUES('Good Moring');
Query OK, 1 row affected (0.00 sec)
mysql>
SELECT * FROM messages;
+----+-------------+
| id | message |
+----+-------------+
| 1 | Hello |
| 2 | Bye |
| 3 | Good Moring |
+----+-------------+
3 rows in set (0.03 sec)
只有当第一个会话的写锁被释放后,第二个会话的读和写操作才会被执行,否则就等待。
</font>
来源:oschina
链接:https://my.oschina.net/u/4332580/blog/4121755