2012-03-23 4 views
1

Я использую Mysql, так как довольно долгое время и действительно смущен результатом простой LEFT JOIN на трех таблицах.Mysql LEFT JOIN из трех таблиц возвращается ко многим строкам

я следующие три таблицы (я создал пример, чтобы сузить)

а) человек

+----------+-------------+------+-----+---------+----------------+ 
| Field | Type  | Null | Key | Default | Extra   | 
+----------+-------------+------+-----+---------+----------------+ 
| PersonID | int(11)  | NO | PRI | NULL | auto_increment | 
| Name  | varchar(50) | YES |  | NULL |    | 
| Age  | int(11)  | YES |  | NULL |    | 
+----------+-------------+------+-----+---------+----------------+ 

б) person_fav_artists

+----------------+--------------+------+-----+---------+----------------+ 
| Field   | Type   | Null | Key | Default | Extra   | 
+----------------+--------------+------+-----+---------+----------------+ 
| FavInterpretID | int(10)  | NO | PRI | NULL | auto_increment | 
| PersonID  | int(10)  | NO |  | 0  |    | 
| Interpret  | varchar(100) | YES |  | NULL |    | 
+----------------+--------------+------+-----+---------+----------------+ 

гр) person_fav_movies

+------------+--------------+------+-----+---------+----------------+ 
| Field  | Type   | Null | Key | Default | Extra   | 
+------------+--------------+------+-----+---------+----------------+ 
| FavMovieID | int(10)  | NO | PRI | NULL | auto_increment | 
| PersonID | int(10)  | NO |  | 0  |    | 
| Movie  | varchar(100) | YES |  | NULL |    | 
+------------+--------------+------+-----+---------+----------------+ 

Мои таблицы примеров используются для хранения любого количества художников и фильмов для одного человека. Погодные условия это не имеет значения, так как это просто простой пример.

Теперь у меня есть следующие данные в таблицах:

mysql> SELECT * FROM persons; 
+----------+------+------+ 
| PersonID | Name | Age | 
+----------+------+------+ 
|  1 | Jeff | 22 | 
|  2 | Lisa | 15 | 
|  3 | Jon | 30 | 
+----------+------+------+ 

mysql> SELECT * FROM person_fav_artists; 
+----------------+----------+----------------+ 
| FavInterpretID | PersonID | Interpret  | 
+----------------+----------+----------------+ 
|    1 |  1 | Linkin Park | 
|    2 |  1 | Muse   | 
|    3 |  2 | Madonna  | 
|    4 |  2 | Katy Perry  | 
|    5 |  2 | Britney Spears | 
|    6 |  1 | Fort Minor  | 
|    7 |  1 | Jay Z   | 
+----------------+----------+----------------+ 

mysql> SELECT * FROM person_fav_movies; 
+------------+----------+-------------------+ 
| FavMovieID | PersonID | Movie    | 
+------------+----------+-------------------+ 
|   1 |  1 | American Pie 1 | 
|   2 |  1 | American Pie 2 | 
|   3 |  1 | American Pie 3 | 
|   4 |  3 | A Game of Thrones | 
|   5 |  3 | Eragon   | 
+------------+----------+-------------------+ 

Теперь i'm просто соединения таблиц с помощью следующего запроса:

Select * FROM persons 
LEFT JOIN person_fav_artists USING (PersonID) 
LEFT JOIN person_fav_movies USING (PersonID); 

, который возвращает следующий результат:

+----------+------+------+----------------+----------------+------------+-------------------+ 
| PersonID | Name | Age | FavInterpretID | Interpret  | FavMovieID | Movie    | 
+----------+------+------+----------------+----------------+------------+-------------------+ 
|  1 | Jeff | 22 |    1 | Linkin Park |   1 | American Pie 1 | 
|  1 | Jeff | 22 |    1 | Linkin Park |   2 | American Pie 2 | 
|  1 | Jeff | 22 |    1 | Linkin Park |   3 | American Pie 3 | 
|  1 | Jeff | 22 |    2 | Muse   |   1 | American Pie 1 | 
|  1 | Jeff | 22 |    2 | Muse   |   2 | American Pie 2 | 
|  1 | Jeff | 22 |    2 | Muse   |   3 | American Pie 3 | 
|  1 | Jeff | 22 |    6 | Fort Minor  |   1 | American Pie 1 | 
|  1 | Jeff | 22 |    6 | Fort Minor  |   2 | American Pie 2 | 
|  1 | Jeff | 22 |    6 | Fort Minor  |   3 | American Pie 3 | 
|  1 | Jeff | 22 |    7 | Jay Z   |   1 | American Pie 1 | 
|  1 | Jeff | 22 |    7 | Jay Z   |   2 | American Pie 2 | 
|  1 | Jeff | 22 |    7 | Jay Z   |   3 | American Pie 3 | 
|  2 | Lisa | 15 |    3 | Madonna  |  NULL | NULL    | 
|  2 | Lisa | 15 |    4 | Katy Perry  |  NULL | NULL    | 
|  2 | Lisa | 15 |    5 | Britney Spears |  NULL | NULL    | 
|  3 | Jon | 30 |   NULL | NULL   |   4 | A Game of Thrones | 
|  3 | Jon | 30 |   NULL | NULL   |   5 | Eragon   | 
+----------+------+------+----------------+----------------+------------+-------------------+ 
17 rows in set (0.00 sec) 

Пока все хорошо. Мой вопрос теперь, если он «нормальный», что «12» Строки возвращаются для человека «Джефф», несмотря на то, что у него только четыре «артиста» и три «фильма», назначенные ему. Я думаю, что могу понять, почему результат такой, какой есть, но я думаю, что очень глупо возвращать так много строк для столь менее реальных данных.

Так что с моим запросом что-то не так, или это намерение?

Результат Я бы хотел, чтобы было бы, как следующие (только для Джеффа):

+----------+------+------+----------------+----------------+------------+-------------------+ 
| PersonID | Name | Age | FavInterpretID | Interpret  | FavMovieID | Movie    | 
+----------+------+------+----------------+----------------+------------+-------------------+ 
|  1 | Jeff | 22 |    1 | Linkin Park |   1 | American Pie 1 | 
|  1 | Jeff | 22 |    2 | Muse   |   2 | American Pie 2 | 
|  1 | Jeff | 22 |    3 | Fort Minor  |   3 | American Pie 3 | 
|  1 | Jeff | 22 |    4 | Jay Z   |   1 | NULL    | <- 'American Pie 1/2/3' would be OK as well. 
+----------+------+------+----------------+----------------+------------+-------------------+ 

Спасибо за вашу помощь!

ответ

0

Вы получаете правильный результат с 12 записями, потому что это правильный кортеж с тем, как вы запрашиваете данные. Я не уверен, почему вы объединяете эти 3 таблицы вместе, потому что по существу, 2 связанные таблицы не являются одним и тем же типом данных.Я бы предположил, что вы выбираете персону &, а затем вы можете объединить людей & художников, потому что ваш союз захочет, чтобы колонки были одинаковыми, я бы предложил добавить тип, чтобы отличать от художников и фильмов, а затем приятное имя должно just be AS string_value

+0

Спасибо за ваш ответ! Я уже понимаю, что это «правильный» результат, поскольку он возвращает все возможные комбинации. О твоем вопрос: Я присоединяюсь к таблицам, чтобы создать объект «Человек» на C#, который имеет списки, содержащие «Фильмы» и «Художники». Я использую этот запрос для создания всего объекта, поэтому я соединяю все вместе. Возможно, было бы разумнее сделать это отдельно, но тогда у меня будет намного больше запросов ... – user1288392

+0

Если вы используете объект C#, который имеет возможность «фильтровать» записи, вы, конечно, можете использовать фильтр данные и применить это к вашему объекту/представлению, а затем повторно фильтровать и применять его к другому объекту/представлению. Это заставило бы ваш SQL тянуть одно нажатие, хотя я бы предложил не. Время, затрачиваемое на объединение данных из третьей таблицы, будет таким же, как и для второго оператора, потому что ваша первая таблица кэшируется. –

+0

Да, вы правы, мой объект типа C# «фильтрует» результат из Mysql-Query. Это означает, что дублированные строки просто игнорируются. Я не понимаю, что я понял вторую часть вашего ответа. Вы имеете в виду, что я должен использовать больше запросов вместо одного большого, как я сделал в моем примере? Это означало бы, что я должен создать отдельный запрос для фильмов и один для художников. – user1288392

0

Это поведение предназначено.

  1. В вашей первой таблице есть 1 столбец для Джеффа.
  2. Вторая таблица имеет 4 столбца для Джеффа, поэтому объединенный стол дает 1x4.
  3. Третий стол имеет 3 столбца для Джеффа, поэтому объединенный стол дает 1x4x3.

Теперь у вас есть все возможные комбинации.

0

Я думаю, что это нормально, так как он принимает все сочетания fav-фильмов и fav-исполнителей. Я думаю, что так работает объединение.

1

Ничего плохого в запросе или результатах, он просто возвращает все возможные комбинации. Одним из вариантов было бы разделение на два отдельных запроса, если объем данных будет большим.

+0

Спасибо за ваш ответ. Я просто надеялся, что будет такой способ без расщепления запросов, но тогда я собираюсь дать попытку расщепления. – user1288392

0

Try заменой LEFT JOIN с INNER JOIN как:

 SELECT * 
    FROM persons 
    INNER JOIN person_fav_artists USING (PersonID) 
    INNER JOIN person_fav_movies USING (PersonID); 
+1

Использование «INNER JOIN» не будет работать, и оно не уменьшит полученные строки для лица «Джефф». Это приведет только к результату без «Лизы» и «Джона». – user1288392

Смежные вопросы