板凳-------Mysql cookbook学习 (十一--------5)

12.4 产生主从列表和摘要

sql 复制代码
mysql> select artist.name, painting.title
    -> from artist inner join painting on artist.a_id = painting.a_id
    -> order by name, title;
+----------+-------------------+
| name     | title             |
+----------+-------------------+
| Da Vinci | Mona Lisa         |
| Da Vinci | The Last Supper   |
| Renoir   | Les Deux Soeurs   |
| Van Gogh | Starry Night      |
| Van Gogh | The Potato Eaters |
| Van Gogh | The Rocks         |
+----------+-------------------+
6 rows in set (0.00 sec)
•	只返回两个表中匹配的记录(即artist.a_id等于painting.a_id的记录)。
•	结果:
o	没有作品的艺术家(如Monet)会被排除。
o	最终结果只有 6 条记录。

mysql> select artist.name, painting.title
    -> from artist left join painting on artist.a_id = painting.a_id
    -> order by name, title;
+----------+-------------------+
| name     | title             |
+----------+-------------------+
| Da Vinci | Mona Lisa         |
| Da Vinci | The Last Supper   |
| Monet    | NULL              |
| Renoir   | Les Deux Soeurs   |
| Van Gogh | Starry Night      |
| Van Gogh | The Potato Eaters |
| Van Gogh | The Rocks         |
+----------+-------------------+
7 rows in set (0.00 sec)
作用:返回左表(artist)的所有记录,以及右表(painting)中匹配的记录。如果右表中没有匹配项,则用NULL填充。
结果:
所有艺术家(包括没有作品的Monet)都会被保留。
Monet的title列为NULL,表示他没有对应的作品。
最终结果有 7 条记录。
INNER JOIN:只需要两个表中都存在关联数据的记录时。
LEFT JOIN:需要保留左表的所有数据,即使右表中没有匹配项(如统计每个艺术家的作品数,包括没有作品的艺术家)。

  mysql> select a_id, count(a_id) as count from painting group by a_id;
+------+-------+
| a_id | count |
+------+-------+
|    1 |     2 |
|    3 |     3 |
|    4 |     1 |
+------+-------+
3 rows in set (0.00 sec)

mysql> select artist.name as painter, count(painting.a_id) as count
    -> from artist inner join painting on artist.a_id = painting.a_id
    -> group by artist.name;
+----------+-------+
| painter  | count |
+----------+-------+
| Da Vinci |     2 |
| Van Gogh |     3 |
| Renoir   |     1 |
+----------+-------+
3 rows in set (0.00 sec)
•	连接类型:INNER JOIN 只保留两表中匹配的记录。
•	聚合逻辑:COUNT(painting.a_id) 统计每个艺术家的有效作品数(即 painting.a_id 不为 NULL 的记录)。
•	结果:
o	没有作品的艺术家(如 Monet)被排除。
o	每个画家的 count 等于其作品数量。

mysql> select artist.name as painter, count(painting.a_id) as count
    -> from artist left join painting on artist.a_id = painting.a_id
    -> group by artist.name;
+----------+-------+
| painter  | count |
+----------+-------+
| Da Vinci |     2 |
| Monet    |     0 |
| Renoir   |     1 |
| Van Gogh |     3 |
+----------+-------+
4 rows in set (0.00 sec)
•	连接类型:LEFT JOIN 保留左表(artist)的所有记录,右表无匹配时用 NULL 填充。
•	聚合逻辑:COUNT(painting.a_id) 统计每个艺术家的有效作品数(忽略 NULL)。
•	结果:
o	所有艺术家(包括 Monet)被保留。
o	Monet 的 count 为 0,因为他没有作品(painting.a_id 全为 NULL)。

mysql> select artist.name as painter, count(*) as count
    -> from artist left join painting on artist.a_id = painting.a_id
    -> group by artist.name;
+----------+-------+
| painter  | count |
+----------+-------+
| Da Vinci |     2 |
| Monet    |     1 |
| Renoir   |     1 |
| Van Gogh |     3 |
+----------+-------+
4 rows in set (0.00 sec)
连接类型:LEFT JOIN 保留左表的所有记录。
聚合逻辑:COUNT(*) 统计每个艺术家的总记录数(无论 painting.a_id 是否为 NULL)。
结果:
所有艺术家被保留。
Monet 的 count 为 1,因为 LEFT JOIN 为他生成了一条记录(painting 字段全为 NULL)。
核心区别总结
查询	连接类型	COUNT 统计对象	无作品艺术家的处理
1. INNER + COUNT(a_id)	INNER	有效作品数	排除(如 Monet 不显示)
2. LEFT + COUNT(a_id)	LEFT	有效作品数	保留,count=0
3. LEFT + COUNT(*)	LEFT	总记录数	保留,count=1(生成的 NULL 行)
示例对比(以 Monet 为例)
INNER JOIN:Monet 被完全排除,因为他没有匹配的作品。
LEFT JOIN + COUNT(painting.a_id):Monet 显示,但 COUNT 忽略 NULL,结果为 0。
LEFT JOIN + COUNT(*):Monet 显示,COUNT 包含生成的 NULL 行,结果为 1。
应用场景
统计作品数:用 LEFT JOIN + COUNT(painting.a_id)(包含无作品的艺术家)。
统计关联行数:用 LEFT JOIN + COUNT(*)(如计算每个艺术家在结果集中的行数)。
纯匹配数据:用 INNER JOIN(只关注有作品的艺术家)。
mysql> select artist.name as painter,
    -> count(painting.a_id) as 'number of paintings',
    -> sum(painting.price) as  'total price',
    -> avg(painting.price) as 'average price'
    -> from artist left join painting on artist.a_id = painting.a_id
    -> group by artist.name;
+----------+---------------------+-------------+---------------+
| painter  | number of paintings | total price | average price |
+----------+---------------------+-------------+---------------+
| Da Vinci |                   2 |         121 |       60.5000 |
| Monet    |                   0 |        NULL |          NULL |
| Renoir   |                   1 |          64 |       64.0000 |
| Van Gogh |                   3 |         148 |       49.3333 |
+----------+---------------------+-------------+---------------+
4 rows in set (0.01 sec)

mysql> select artist.name as painter,
    -> count(painting.a_id) as 'number of paintings',
    -> ifnull(sum(painting.price),0) as  'total price',
    -> ifnull(avg(painting.price), 0) as 'average price'
    -> from artist left join painting on artist.a_id = painting.a_id
    -> group by artist.name;
+----------+---------------------+-------------+---------------+
| painter  | number of paintings | total price | average price |
+----------+---------------------+-------------+---------------+
| Da Vinci |                   2 |         121 |       60.5000 |
| Monet    |                   0 |           0 |        0.0000 |
| Renoir   |                   1 |          64 |       64.0000 |
| Van Gogh |                   3 |         148 |       49.3333 |
+----------+---------------------+-------------+---------------+
4 rows in set (0.00 sec)

12.5 枚举多对多的关系

sql 复制代码
mysql> select name, abbrev from states;
+----------------+--------+
| name           | abbrev |
+----------------+--------+
| Alaska         | AK     |
| Alabama        | AL     |
| Arkansas       | AR     |
| Arizona        | AZ     |
| California     | CA     |
| Colorado       | CO     |
| Connecticut    | CT     |
| Delaware       | DE     |
| Florida        | FL     |
| Georgia        | GA     |
| Hawaii         | HI     |
| Iowa           | IA     |
| Idaho          | ID     |
| Illinois       | IL     |
| Indiana        | IN     |
| Kansas         | KS     |
| Kentucky       | KY     |
| Louisiana      | LA     |
| Massachusetts  | MA     |
| Maryland       | MD     |
| Maine          | ME     |
| Michigan       | MI     |
| Minnesota      | MN     |
| Missouri       | MO     |
| Mississippi    | MS     |
| Montana        | MT     |
| North Carolina | NC     |
| North Dakota   | ND     |
| Nebraska       | NE     |
| New Hampshire  | NH     |
| New Jersey     | NJ     |
| New Mexico     | NM     |
| Nevada         | NV     |
| New York       | NY     |
| Ohio           | OH     |
| Oklahoma       | OK     |
| Oregon         | OR     |
| Pennsylvania   | PA     |
| Rhode Island   | RI     |
| South Carolina | SC     |
| South Dakota   | SD     |
| Tennessee      | TN     |
| Texas          | TX     |
| Utah           | UT     |
| Virginia       | VA     |
| Vermont        | VT     |
| Washington     | WA     |
| Wisconsin      | WI     |
| West Virginia  | WV     |
| Wyoming        | WY     |
+----------------+--------+
50 rows in set (0.00 sec)

mysql> select title, state from painting order by state;
+-------------------+-------+
| title             | state |
+-------------------+-------+
| The Rocks         | IA    |
| The Last Supper   | IN    |
| Starry Night      | KY    |
| The Potato Eaters | KY    |
| Mona Lisa         | MI    |
| Les Deux Soeurs   | NE    |
+-------------------+-------+
6 rows in set (0.00 sec)

mysql> select painting.title, states.name as state
    -> from painting inner join states on painting.state = states.abbrev
    -> order by state;
+-------------------+----------+
| title             | state    |
+-------------------+----------+
| The Last Supper   | Indiana  |
| The Rocks         | Iowa     |
| Starry Night      | Kentucky |
| The Potato Eaters | Kentucky |
| Mona Lisa         | Michigan |
| Les Deux Soeurs   | Nebraska |
+-------------------+----------+
6 rows in set (0.00 sec)

-- 创建表(已正确创建)
CREATE TABLE euchre (
  team        CHAR(10) NOT NULL,      -- 队伍名称
  year        YEAR NOT NULL,          -- 比赛年份
  wins        INT UNSIGNED NOT NULL,  -- 胜场数
  losses      INT UNSIGNED NOT NULL,  -- 负场数
  player      CHAR(20) NOT NULL,      -- 队员姓名
  player_city CHAR(20) NOT NULL       -- 队员城市
);

-- 插入数据(修正表名和列)
INSERT INTO euchre (team, year, wins, losses, player, player_city)
VALUES
  ('Kings', 2005, 10, 2, 'Alice', 'New York'),
  ('Kings', 2005, 10, 2, 'Bob', 'New York'),
  ('Crowns', 2005, 7, 5, 'Charlie', 'Chicago'),
  ('Crowns', 2005, 7, 5, 'Diana', 'Chicago'),
  ('Stars', 2005, 4, 8, 'Eve', 'Los Angeles'),
  ('Stars', 2005, 4, 8, 'Frank', 'Los Angeles'),
  ('Sceptres', 2005, 3, 9, 'Grace', 'Houston'),
  ('Sceptres', 2005, 3, 9, 'Heidi', 'Houston'),
  ('Kings', 2006, 8, 4, 'Alice', 'New York'),
  ('Kings', 2006, 8, 4, 'Bob', 'New York'),
  ('Crowns', 2006, 9, 3, 'Ivan', 'Chicago'),
  ('Crowns', 2006, 9, 3, 'Judy', 'Chicago'),
  ('Stars', 2006, 5, 7, 'Ken', 'Los Angeles'),
  ('Stars', 2006, 5, 7, 'Lisa', 'Los Angeles'),
  ('Sceptres', 2006, 2, 10, 'Mike', 'Houston'),
  ('Sceptres', 2006, 2, 10, 'Nancy', 'Houston');

mysql> select * from euchre order by year, wins desc, player;
+----------+------+------+--------+---------+-------------+
| team     | year | wins | losses | player  | player_city |
+----------+------+------+--------+---------+-------------+
| Kings    | 2005 |   10 |      2 | Alice   | New York    |
| Kings    | 2005 |   10 |      2 | Bob     | New York    |
| Crowns   | 2005 |    7 |      5 | Charlie | Chicago     |
| Crowns   | 2005 |    7 |      5 | Diana   | Chicago     |
| Stars    | 2005 |    4 |      8 | Eve     | Los Angeles |
| Stars    | 2005 |    4 |      8 | Frank   | Los Angeles |
| Sceptres | 2005 |    3 |      9 | Grace   | Houston     |
| Sceptres | 2005 |    3 |      9 | Heidi   | Houston     |
| Crowns   | 2006 |    9 |      3 | Ivan    | Chicago     |
| Crowns   | 2006 |    9 |      3 | Judy    | Chicago     |
| Kings    | 2006 |    8 |      4 | Alice   | New York    |
| Kings    | 2006 |    8 |      4 | Bob     | New York    |
| Stars    | 2006 |    5 |      7 | Ken     | Los Angeles |
| Stars    | 2006 |    5 |      7 | Lisa    | Los Angeles |
| Sceptres | 2006 |    2 |     10 | Mike    | Houston     |
| Sceptres | 2006 |    2 |     10 | Nancy   | Houston     |
+----------+------+------+--------+---------+-------------+
16 rows in set (0.00 sec)

mysql> -- 创建 euchre_team 表
mysql> CREATE TABLE IF NOT EXISTS euchre_team (
    ->   id        INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,  -- 自增主键
    ->   name      CHAR(10) NOT NULL,                        -- 队伍名称
    ->   year      YEAR NOT NULL,                            -- 比赛年份
    ->   wins      INT UNSIGNED NOT NULL,                    -- 胜场数
    ->   losses    INT UNSIGNED NOT NULL,                    -- 负场数
    ->   UNIQUE KEY team_year_idx (name, year)               -- 确保每个队伍在同一年份只能有一条记录
    -> );
Query OK, 0 rows affected (0.10 sec)

mysql>
mysql> -- 插入数据
mysql> INSERT INTO euchre_team (name, year, wins, losses)
    -> VALUES
    ->   ('Kings', 2005, 10, 2),
    ->   ('Crowns', 2005, 7, 5),
    ->   ('Stars', 2005, 4, 8),
    ->   ('Sceptres', 2005, 3, 9),
    ->   ('Kings', 2006, 8, 4),
    ->   ('Crowns', 2006, 9, 3),
    ->   ('Stars', 2006, 5, 7),
    ->   ('Sceptres', 2006, 2, 10);
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql>
mysql> -- 查询所有数据
mysql> SELECT * FROM euchre_team;
+----+----------+------+------+--------+
| id | name     | year | wins | losses |
+----+----------+------+------+--------+
|  1 | Kings    | 2005 |   10 |      2 |
|  2 | Crowns   | 2005 |    7 |      5 |
|  3 | Stars    | 2005 |    4 |      8 |
|  4 | Sceptres | 2005 |    3 |      9 |
|  5 | Kings    | 2006 |    8 |      4 |
|  6 | Crowns   | 2006 |    9 |      3 |
|  7 | Stars    | 2006 |    5 |      7 |
|  8 | Sceptres | 2006 |    2 |     10 |
+----+----------+------+------+--------+
8 rows in set (0.00 sec)
mysql> DROP TABLE IF EXISTS euchre_player;
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> #@ _CREATE_EUCHRE_PLAYER_TABLE_
mysql> CREATE TABLE euchre_player
    -> (
    ->   id    INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   name  CHAR(20) NOT NULL,    # player name and city
    ->   city  CHAR(20) NOT NULL,
    ->   PRIMARY KEY (id)
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> #@ _CREATE_EUCHRE_PLAYER_TABLE_
mysql>
mysql> INSERT INTO euchre_player (name,city)
    ->   VALUES
    ->     ('Ben','Cork'),
    ->     ('Billy','York'),
    ->     ('Tony','Derry'),
    ->     ('Melvin','Dublin'),
    ->     ('Franklin','Bath'),
    ->     ('Wallace','Cardiff'),
    ->     ('Nigel','London'),
    ->     ('Maurice','Leeds')
    -> ;
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql>
mysql> SELECT * FROM euchre_player;
+----+----------+---------+
| id | name     | city    |
+----+----------+---------+
|  1 | Ben      | Cork    |
|  2 | Billy    | York    |
|  3 | Tony     | Derry   |
|  4 | Melvin   | Dublin  |
|  5 | Franklin | Bath    |
|  6 | Wallace  | Cardiff |
|  7 | Nigel    | London  |
|  8 | Maurice  | Leeds   |
+----+----------+---------+
8 rows in set (0.00 sec)

mysql> DROP TABLE IF EXISTS euchre_link;
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> #@ _CREATE_EUCHRE_LINK_TABLE_
mysql> CREATE TABLE euchre_link
    -> (
    ->   team_id   INT UNSIGNED NOT NULL,
    ->   player_id INT UNSIGNED NOT NULL,
    ->   UNIQUE (team_id, player_id)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> #@ _CREATE_EUCHRE_LINK_TABLE_
mysql>
mysql> INSERT INTO euchre_link (team_id,player_id)
    ->   VALUES
    ->     (1,1),
    ->     (1,2),
    ->     (2,3),
    ->     (2,4),
    ->     (3,5),
    ->     (3,6),
    ->     (4,7),
    ->     (4,8),
    ->     (5,5),
    ->     (5,7),
    ->     (6,1),
    ->     (6,3),
    ->     (7,4),
    ->     (7,8),
    ->     (8,2),
    ->     (8,6)
    -> ;
Query OK, 16 rows affected (0.01 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql>
mysql> SELECT * FROM euchre_link;
+---------+-----------+
| team_id | player_id |
+---------+-----------+
|       1 |         1 |
|       1 |         2 |
|       2 |         3 |
|       2 |         4 |
|       3 |         5 |
|       3 |         6 |
|       4 |         7 |
|       4 |         8 |
|       5 |         5 |
|       5 |         7 |
|       6 |         1 |
|       6 |         3 |
|       7 |         4 |
|       7 |         8 |
|       8 |         2 |
|       8 |         6 |
+---------+-----------+
16 rows in set (0.00 sec)

mysql> select t.name, t.year, t.wins, t.losses, p.name, p.city
    -> from euchre_team as t inner join euchre_link as l
    -> inner join euchre_player as p
    -> on t.id = l.team_id and p.id = l.player_id
    -> order by t.year, t.wins desc, p.name;
+----------+------+------+--------+----------+---------+
| name     | year | wins | losses | name     | city    |
+----------+------+------+--------+----------+---------+
| Kings    | 2005 |   10 |      2 | Ben      | Cork    |
| Kings    | 2005 |   10 |      2 | Billy    | York    |
| Crowns   | 2005 |    7 |      5 | Melvin   | Dublin  |
| Crowns   | 2005 |    7 |      5 | Tony     | Derry   |
| Stars    | 2005 |    4 |      8 | Franklin | Bath    |
| Stars    | 2005 |    4 |      8 | Wallace  | Cardiff |
| Sceptres | 2005 |    3 |      9 | Maurice  | Leeds   |
| Sceptres | 2005 |    3 |      9 | Nigel    | London  |
| Crowns   | 2006 |    9 |      3 | Ben      | Cork    |
| Crowns   | 2006 |    9 |      3 | Tony     | Derry   |
| Kings    | 2006 |    8 |      4 | Franklin | Bath    |
| Kings    | 2006 |    8 |      4 | Nigel    | London  |
| Stars    | 2006 |    5 |      7 | Maurice  | Leeds   |
| Stars    | 2006 |    5 |      7 | Melvin   | Dublin  |
| Sceptres | 2006 |    2 |     10 | Billy    | York    |
| Sceptres | 2006 |    2 |     10 | Wallace  | Cardiff |
+----------+------+------+--------+----------+---------+
16 rows in set (0.00 sec)

mysql> select p.name, p.city
    -> from euchre_team as t inner join euchre_link as l
    -> inner join euchre_player as p
    -> on t.id = l.team_id and p.id = l.player_id
    -> and t.name = 'Crowns' and t.year = 2005;
+--------+--------+
| name   | city   |
+--------+--------+
| Tony   | Derry  |
| Melvin | Dublin |
+--------+--------+
2 rows in set (0.00 sec)

mysql> select t.name, t.year, t.wins, t.losses
    -> from euchre_team as t inner join euchre_link as l
    -> inner join euchre_player as p
    -> on t.id = l.team_id and p.id = l.player_id
    -> where p.name = 'Billy';
+----------+------+------+--------+
| name     | year | wins | losses |
+----------+------+------+--------+
| Kings    | 2005 |   10 |      2 |
| Sceptres | 2006 |    2 |     10 |
+----------+------+------+--------+
2 rows in set (0.00 sec)