MySQL Cookbook Free Open Book

MySQL Cookbook

Previous Section Next Section

12.11 Enumerating a Many-to-Many Relationship

12.11.1 Problem

You want to display a relationship between tables when records in either table may be matched by multiple records in the other table.

12.11.2 Solution

This is a many-to-many relationship. It requires a third table for associating your two primary tables, and a three-way join to list the correspondences between them.

12.11.3 Discussion

The artist and painting tables used in earlier sections are related in a one-to-many relationship: A given artist may have produced many paintings, but each painting was created by only one artist. One-to-many relationships are relatively simple and the two tables in the relationship can be related by means of a key that is common to both tables.

Even simpler is the one-to-one relationship, which often is used for performing lookups that map one set of values to another. For example, the states table contains name and abbrev columns that list full state names and their corresponding abbreviations:

mysql> SELECT name, abbrev FROM states;
+----------------+--------+
| name           | abbrev |
+----------------+--------+
| Alabama        | AL     |
| Alaska         | AK     |
| Arizona        | AZ     |
| Arkansas       | AR     |
...

This is a one-to-one relationship. It can be used to map state name abbreviations in the painting table, which contains a state column indicating the state in which each painting was purchased. With no mapping, painting entries can be displayed like this:

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    |
| The Mona Lisa     | MI    |
| Les Deux Soeurs   | NE    |
+-------------------+-------+

If you want to see the full state names rather than abbreviations, it's possible to use the one-to-one relationship that exists between the two that is enumerated in the states table. Join that table to the painting table as follows, using the abbreviation values that are common to the two tables:

mysql> SELECT painting.title, states.name AS state
    -> FROM painting, states
    -> WHERE painting.state = states.abbrev
    -> ORDER BY state;
+-------------------+----------+
| title             | state    |
+-------------------+----------+
| The Last Supper   | Indiana  |
| The Rocks         | Iowa     |
| Starry Night      | Kentucky |
| The Potato Eaters | Kentucky |
| The Mona Lisa     | Michigan |
| Les Deux Soeurs   | Nebraska |
+-------------------+----------+

A more complex relationship between tables is the many-to-many relationship, which occurs when a record in one table may have many matches in the other, and vice versa. To illustrate such a relationship, this is the point at which database books typically devolve into the "parts and suppliers" problem. (A given part may be available through several suppliers; how can you produce a list showing which parts are available from which suppliers?) However, having seen that example far too many times, I prefer to use a different illustration. So, even though conceptually it's really the same idea, let's use the following scenario: You and a bunch of your friends are avid enthusiasts of euchre, a four-handed card game played with two teams of partners. Each year, you all get together, pair off, and run a friendly tournament. Naturally, to avoid controversy about the results of each tournament, you record the pairings and outcomes in a database. One way to store the results would be with a table that is set up as follows, where for each tournament year, you record the team names, win-loss records, players, and player cities of residence:

mysql> SELECT * FROM euchre ORDER BY year, wins DESC, player;
+----------+------+------+--------+----------+-------------+
| team     | year | wins | losses | player   | player_city |
+----------+------+------+--------+----------+-------------+
| Kings    | 2001 |   10 |      2 | Ben      | Cork        |
| Kings    | 2001 |   10 |      2 | Billy    | York        |
| Crowns   | 2001 |    7 |      5 | Melvin   | Dublin      |
| Crowns   | 2001 |    7 |      5 | Tony     | Derry       |
| Stars    | 2001 |    4 |      8 | Franklin | Bath        |
| Stars    | 2001 |    4 |      8 | Wallace  | Cardiff     |
| Sceptres | 2001 |    3 |      9 | Maurice  | Leeds       |
| Sceptres | 2001 |    3 |      9 | Nigel    | London      |
| Crowns   | 2002 |    9 |      3 | Ben      | Cork        |
| Crowns   | 2002 |    9 |      3 | Tony     | Derry       |
| Kings    | 2002 |    8 |      4 | Franklin | Bath        |
| Kings    | 2002 |    8 |      4 | Nigel    | London      |
| Stars    | 2002 |    5 |      7 | Maurice  | Leeds       |
| Stars    | 2002 |    5 |      7 | Melvin   | Dublin      |
| Sceptres | 2002 |    2 |     10 | Billy    | York        |
| Sceptres | 2002 |    2 |     10 | Wallace  | Cardiff     |
+----------+------+------+--------+----------+-------------+

As shown by the table, each team has multiple players, and each player has participated in multiple teams. The table captures the nature of this many-to-many relationship, but it's also in non-normal form, because each row unnecessarily stores quite a bit of repetitive information. (Information for each team is recorded multiple times, as is information about each player.) A better way to represent this many-to-many relationship is as follows:

  • Store each team name, year, and record once, in a table named euchre_team.

  • Store each player name and city of residence once, in a table named euchre_player.

  • Create a third table, euchre_link, that stores team-player associations and serves as a link, or bridge, between the two primary tables. To minimize the information stored in this table, assign unique IDs to each team and player within their respective tables, and store only those IDs in the euchre_link table.

The resulting team and player tables look like this:

mysql> SELECT * FROM euchre_team;
+----+----------+------+------+--------+
| id | name     | year | wins | losses |
+----+----------+------+------+--------+
|  1 | Kings    | 2001 |   10 |      2 |
|  2 | Crowns   | 2001 |    7 |      5 |
|  3 | Stars    | 2001 |    4 |      8 |
|  4 | Sceptres | 2001 |    3 |      9 |
|  5 | Kings    | 2002 |    8 |      4 |
|  6 | Crowns   | 2002 |    9 |      3 |
|  7 | Stars    | 2002 |    5 |      7 |
|  8 | Sceptres | 2002 |    2 |     10 |
+----+----------+------+------+--------+
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   |
+----+----------+---------+

The euchre_link table associates teams and players as follows:

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

To answer questions about the teams or players using these tables, you need to perform a three-way join, using the link table to relate the two primary tables to each other. Here are some examples:

  • List all the pairings that show the teams and who played on them. This query enumerates all the correspondences between the euchre_team and euchre_player tables and reproduces the information that was originally in the non-normal euchre table:

    mysql> SELECT t.name, t.year, t.wins, t.losses, p.name, p.city
        -> FROM euchre_team AS t, euchre_link AS l, euchre_player AS p
        -> WHERE 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    | 2001 |   10 |      2 | Ben      | Cork    |
    | Kings    | 2001 |   10 |      2 | Billy    | York    |
    | Crowns   | 2001 |    7 |      5 | Melvin   | Dublin  |
    | Crowns   | 2001 |    7 |      5 | Tony     | Derry   |
    | Stars    | 2001 |    4 |      8 | Franklin | Bath    |
    | Stars    | 2001 |    4 |      8 | Wallace  | Cardiff |
    | Sceptres | 2001 |    3 |      9 | Maurice  | Leeds   |
    | Sceptres | 2001 |    3 |      9 | Nigel    | London  |
    | Crowns   | 2002 |    9 |      3 | Ben      | Cork    |
    | Crowns   | 2002 |    9 |      3 | Tony     | Derry   |
    | Kings    | 2002 |    8 |      4 | Franklin | Bath    |
    | Kings    | 2002 |    8 |      4 | Nigel    | London  |
    | Stars    | 2002 |    5 |      7 | Maurice  | Leeds   |
    | Stars    | 2002 |    5 |      7 | Melvin   | Dublin  |
    | Sceptres | 2002 |    2 |     10 | Billy    | York    |
    | Sceptres | 2002 |    2 |     10 | Wallace  | Cardiff |
    +----------+------+------+--------+----------+---------+
  • List the members for a particular team (the 2001 Crowns):

    mysql> SELECT p.name, p.city
        -> FROM euchre_team AS t, euchre_link AS l, euchre_player AS p
        -> WHERE t.id = l.team_id AND p.id = l.player_id
        -> AND t.name = 'Crowns' AND t.year = 2001;
    +--------+--------+
    | name   | city   |
    +--------+--------+
    | Tony   | Derry  |
    | Melvin | Dublin |
    +--------+--------+
  • List the teams that a given player (Billy) has been a member of:

    mysql> SELECT t.name, t.year, t.wins, t.losses
        -> FROM euchre_team AS t, euchre_link AS l, euchre_player AS p
        -> WHERE t.id = l.team_id AND p.id = l.player_id
        -> AND p.name = 'Billy';
    +----------+------+------+--------+
    | name     | year | wins | losses |
    +----------+------+------+--------+
    | Kings    | 2001 |   10 |      2 |
    | Sceptres | 2002 |    2 |     10 |
    +----------+------+------+--------+

Note that although questions about many-to-many relationships involve a three-way join, a three-way join in itself does not necessarily imply a many-to-many relationship. Earlier in this section, we joined the states table to the painting table to map state abbreviations to full names:

mysql> SELECT painting.title, states.name AS state
    -> FROM painting, states
    -> WHERE painting.state = states.abbrev
    -> ORDER BY state;
+-------------------+----------+
| title             | state    |
+-------------------+----------+
| The Last Supper   | Indiana  |
| The Rocks         | Iowa     |
| Starry Night      | Kentucky |
| The Potato Eaters | Kentucky |
| The Mona Lisa     | Michigan |
| Les Deux Soeurs   | Nebraska |
+-------------------+----------+

To display the artist who painted each painting, modify the query slightly by joining the results with the artist table:

mysql> SELECT artist.name, painting.title, states.name AS state
    -> FROM artist, painting, states
    -> WHERE artist.a_id = painting.a_id AND painting.state = states.abbrev;
+----------+-------------------+----------+
| name     | title             | state    |
+----------+-------------------+----------+
| Da Vinci | The Last Supper   | Indiana  |
| Da Vinci | The Mona Lisa     | Michigan |
| Van Gogh | Starry Night      | Kentucky |
| Van Gogh | The Potato Eaters | Kentucky |
| Van Gogh | The Rocks         | Iowa     |
| Renoir   | Les Deux Soeurs   | Nebraska |
+----------+-------------------+----------+

The query now involves a three-way join, but the nature of the relationship between artists and paintings remains the same. It's still one-to-many, not many-to-many.

    Previous Section Next Section
    Index: [SYMBOL][A][B][C][D][E][F][G][H][I][J][K][L][M][N][O][P][Q][R][S][T][U][V][W][X][Y][Z]


         Main Menu
    Main Page
    Table of content
    Copyright
    Preface
    Chapter 1. Using the mysql Client Program
    Chapter 2. Writing MySQL-Based Programs
    Chapter 3. Record Selection Techniques
    Chapter 4. Working with Strings
    Chapter 5. Working with Dates and Times
    Chapter 6. Sorting Query Results
    Chapter 7. Generating Summaries
    Chapter 8. Modifying Tables with ALTER TABLE
    Chapter 9. Obtaining and Using Metadata
    Chapter 10. Importing and Exporting Data
    Chapter 11. Generating and Using Sequences
    Chapter 12. Using Multiple Tables
    12.1 Introduction
    12.2 Combining Rows in One Table with Rows in Another
    12.3 Performing a Join Between Tables in Different Databases
    12.4 Referring to Join Output Column Names in Programs
    12.5 Finding Rows in One Table That Match Rows in Another
    12.6 Finding Rows with No Match in Another Table
    12.7 Finding Rows Containing Per-Group Minimum or Maximum Values
    12.8 Computing Team Standings
    12.9 Producing Master-Detail Lists and Summaries
    12.10 Using a Join to Fill in Holes in a List
    12.11 Enumerating a Many-to-Many Relationship
    12.12 Comparing a Table to Itself
    12.13 Calculating Differences Between Successive Rows
    12.14 Finding Cumulative Sums and Running Averages
    12.15 Using a Join to Control Query Output Order
    12.16 Converting Subselects to Join Operations
    12.17 Selecting Records in Parallel from Multiple Tables
    12.18 Inserting Records in One Table That Include Values from Another
    12.19 Updating One Table Based on Values in Another
    12.20 Using a Join to Create a Lookup Table from Descriptive Labels
    12.21 Deleting Related Rows in Multiple Tables
    12.22 Identifying and Removing Unattached Records
    12.23 Using Different MySQL Servers Simultaneously
    Chapter 13. Statistical Techniques
    Chapter 14. Handling Duplicates
    Chapter 15. Performing Transactions
    Chapter 16. Introduction to MySQL on the Web
    Chapter 17. Incorporating Query Resultsinto Web Pages
    Chapter 18. Processing Web Input with MySQL
    Chapter 19. Using MySQL-Based Web Session Management
    Appendix A. Obtaining MySQL Software
    Appendix B. JSP and Tomcat Primer
    Appendix C. References
    Colophone
    Index


    More Books
    PHP Hacks
    Processing Xml With Java - A Guide To Sax, Dom, Jdom, Jaxp, And Trax
    The Koran (Holy Qur'an)
    Macromedia Flash 8 Bible
    Search Engine Optimization for Dummies
    YouTube Traffic
    PHP 5 for Dummies
    Harry Potter and The Chamber of Secrets
    Harry Potter and the Sorcerer's Stone
    The Pilgrim's Progress
    Wireless Hacks
    Flash Hacks. 100 Industrial-Strength Tips & Tools
    PayPal Hacks. 100 Industrial-Strength Tips and Tools
    Amazon Hacks
    Pdf Hacks
    The Da Vinci Code
    Google Hacks
    The Holy Bible
    Windows XP For Dummies
    Harry Potter and the Half-Blood Prince
    Seo Book
    Upgrading and Repairing Networks
    Macromedia Dreamweaver 8 UNLEASHED
    Windows XP Annoyances
    Windows XP Hacks
    Microsoft Windows XP Power Toolkit
    Teach Yourself MS Office In 24Hours
    iPod & iTunes Missing Manual
    PC Hacks 100 Industrial-Strength Tips and Tools
    PC Overclocking, Optimization, and Tuning - 2th Edition
    PC Hardware In A Nutshell 3rd Edition
    PC Hardware in a Nutshell, 2nd Edition
    Upgrading and Repairing PCs
    Google for Dummies
    MySQL Cookbook
    Teach Yourself Macromedia Flash 8 In 24 Hours
    PHP CookBook
    Sams Teach Yourself JavaScript in 24 Hours
    PHP5 Manual
    Free Games Paper Airplanes
    500 Juegos Gratis 500 Giochi Gratis 500 Jeux Gratuits 500 Jogos Gratis 500 Kostenlose Spiele