mysql_tutorial

岁酱吖の 提交于 2021-01-05 12:03:12

<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>

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!