Выбор данных из базы данных со связями многие ко многим.

Nerian

Новичок
Выбор данных из базы данных со связями многие ко многим.

Всем привет. Помогите пожалуйсто оптимально выбрать из базы данных данные связанные связью многие ко многим.
Имееться три остонвых таблицы:
Фильмы, Жарны, Актёры. Связаны двумя дополнительными таблицами ФильмыЖанры, ФильмыАктёры.

Требуеться на выходе для всех фильмов получить информацию вида:
Имя Фильма1 / Жанр1, Жанр2, ... / Актёр1, Акрёт2, Актёр3 ... /
Имя Фильма2 / Жанр1, Жанр2, ... / Актёр1, Акрёт2, Актёр3 ... /

Как я понимаю связал я всё правильно, т.к. у фильма может быть много жанров, и один и тот же жанр может быть у многих фильмов, тоже самое и с актёрами.

Только вот как выбрать это более оптимально?

Для моего примера легко получить таблицу вида:
Имя фильма / Жарн / Имя актёра
Имя Фильма 1 / Жанр1 / Актёр1
Имя фильма 1 / Жанр1 / Актёр2
Имя фильма 1 / Жанр2 / Актёр1
Имя фильма 1 / Жанр2 / Актёр2
Имя фильма 2 / Жанр1 / Актёр1
и тд...
И ещё.. Возмножно ли получить на выходе требуемый результат одинм запросом или двумя тремя? Так как если список будет состоять из 100 или даже 1000 фильмов, чтобы извлечь такую информацию потребуеться 100х2+1 или 1000х2+1 запросов.

Пока я временно делаю это тремя запросами: выбираю всех актёров, выбираю все жанры, выбираю все фильмы. И уже на php пишу код который сопостовляет (работает быстрее чем 1000х2+1 запрос).

Вопщем кто что думает по этому поводу, очень важно ваще мнение, примеры тоже будут кстати..

-~{}~ 18.10.05 00:59:

Пока вот что смог придумать: (Кто что может по этому поводу сказать? )

Код:
<?

$result=mysql_query("SELECT f.id as film_id, f.name as film_name, 
                            a.id as actor_id, a.name as actor_name,
                            g.id as genre_id, g.name as genre_name 
                     FROM films f, actors a, genres g, 
                          films_genres fg, films_actors fa 
                     WHERE fa.f_id=f.id AND fa.a_id=a.id AND
                           fg.f_id=f.id AND fg.g_id=g.id
                     ORDER BY a.name, g.name;");

while($rows=mysql_fetch_array($result)) {
	// При смени фильма
	($film[name]<>$rows[film_name]) { 
		// Если фильм уже прошёл фазу сбора инофрмации,
		// значит мы собарли всю информацию о нём
		if($film[name]<>'') { 
			// А потому выводим.. тут может быть много различных методов вывода
			// Хотя иногда выгоднее это делать не тут
			print "Film name: ".$film[name];
			print_r "Genres: ".$film[genres];
			print_r "Actors: ".$film[autors];
		}
		// При смене фильма очистим старые данные и забьём новые
		$film[name]=$rows[film_name];
		$film[genres]=array();
		$film[actors]=array();
	};
	// Колекционируем жарны текущего фильма
	if(!in_array($rows[$genre_name],$film[genres])) {
		$film[genres][]=$rows[$genre_name];
	};
	// Колекционируем аткрёров текущего фильма
	if(!in_array($rows[$autor_name],$film[actors])) {
		$film[actors][]=$rows[$autor_name];
	};
}
?>
 
1) Получи все фильмы с их жанрами
2) Получи все фильмы с их актерами

При выводе на печать бери данные из двух массивов.
Можно с помощью двух одновременных соединений к базе обойтись и без временных массивов.

-~{}~ 18.10.05 01:29:

Дело в том, что жанры напрямую не связаны с актерами, получать их вместе - бессмысленно.
 

Kirs

Fireman
Nerian

Я бы посоветовал изменить структуру хранения данных на такую:

Таблица фильма:
id|film_name|genres(типа SET)

Таблица актеров:
id|actors_name|...

Связная таблица актер-фильм
id(актера)|id(фильма)
 

Nerian

Новичок
Спосибо. Тока вот как быть если нужно ограничить вывод, допустим показать 10 из 100? Ведь мы можем для 10 фильмов получить и 30 и 40 строчек из базы...
 

baev

‹°°¬•
Команда форума

MuXa247

Новичок
Автор оригинала: Kirs
Nerian

Я бы посоветовал изменить структуру хранения данных на такую:

Таблица фильма:
id|film_name|genres(типа SET)
Вопрос: Можно ли при выборке по полю SET сделать так, что бы использовался какой либо индекс?
например:
[sql]SELECT *
FROM `offers`
WHERE FIND_IN_SET( '2', test ) >0[/sql] здесь индекс не используется!
[sql]SELECT *
FROM `offers`
WHERE test like = '2%'[/sql] здесь индекс работает. Но в этом случае нужно знать все начальные биты(значения), что не приемлемо в данном случае.
Можно разрешить данную проблему или лучше использовать дополнительную таблицу?
 

Фанат

oncle terrible
Команда форума
Только вот как выбрать это более оптимально?
ЭТО - это что?
10 из 100 ЧЕГО?
фильможанороактёров?

ты просишь подсказать, как тебе сделать выборку, но не говоришь, выборку ЧЕГО
 

Nerian

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

Что надо для решения этой задачи?
Чтобы можно было хранить данные о жарнах, актёрах, фильмов. а так же выводить список, причём ограничевать его в N фильмов? Как лучше и правильнее спроектировать такую БД. (Пример www.sharereactor.ru) там что то сделано на подобие. Хочу узнать как это делаеться. Если кто может подсказать какой движок буду признателен, так как дальше я уже сам смогу разобраться как там всё реализовано.

Спасибо.
 

Rammstein

PHPClub::News
Так, автор, ты хоть для приличия структуру БД скинь.
Предлагаю так сделать (фильм с жанрами связь)
Делаем три таблицы:
films: id | title
genres: id | name
Везде индексы по ID
и f2g: film_id | genre_id
Тут индексы по всем полям (т.е. один индекс на два поля)
Теперь всё сводится к (выбор по ID фильма):
[sql]SELECT * FROM films[/sql]
и
[sql]SELECT genres.name FROM f2g LEFT JOIN genres ON f2g.film_id='id_фильма' GROUP BY f2g.genre_id[/sql]
 

Steamroller

Новичок
Поставим конкретную задачу:
вывод списка фильмов, напротив фильмов жанры этих фильмов (одному фильму много жарнов, одну жанру много фильмов), а так же актёры (одноми фульму много актёров, один актёр во многих фильмах).
так же продумать возможность хранения информации об актёрах и об жарнах.
Так как сейчас хранишь (две таблицы-связки и три с данными) -это уже правильно.
Выбирать - так, как во втором посте этой темы уже написал Алексей Пешков. То есть два селекта - для жанров и для актеров, каждый с сортировкой по фильму, и параллельно все нужное из этих рекордсетов фетчишь и выводишь.
В один запрос можно конечно это запихать, но тормозить шибко будет.
 

Nerian

Новичок
Спасибо
Steamroller, Алексей Пешков.
Непонятен до сих пор один момент. Что ограничивать можно LIMIT'ом я знал конечно, но вот как быть в случае с несколькими результами, да ещё при условии что результаты для одного фильма с многими жанрами или актёрами будут дублироваться?
К примеру:
фильм1:жанр1
фильм1:жанр2
фильм2:жанр1
Если сделаю LIMIT 0,2 то получю не два фильма, а если
LIMIT 0,3 не три, тоесть в зависимости от количества сопоставленых жанров/актёров. Можно ли как нибудь задать лимит на количество уникальных фильмов? И ещё на сколько это выгоднее чем делать всё без лемита при 1000 записях о фильмах? Ведь на php я сам потом добираю эти элементы, помере надобности.

Rammstein: структуру я описал в краце (какие таблицы использую чем связываю) и также сказал что подойдёт любая, главная максимально грамотно решить проблему. В твоём примере одна связь многие ко многим, что сделать не составляет сложности. У меня была провлема когда таких связей появляеться несколько.
 

Steamroller

Новичок
Непонятен до сих пор один момент. Что ограничивать можно LIMIT'ом я знал конечно, но вот как быть в случае с несколькими результами, да ещё при условии что результаты для одного фильма с многими жанрами или актёрами будут дублироваться?
Вообще можно сначала сделать список нужных id фильма (ты же не от балды первые 10 попавшихся выводишь), и уже ставить условие не limit, а where id in(n1,n2...).
 

Nerian

Новичок
>> условие не limit, а where id in(n1,n2...).
Хм спасибо незнал.

Вопщем приду домой забью рандомом 10000 фильмов, и 100 жанров 20000 актёров ибуду тестить какой алгоритм подходит. Хотя цифры приувелицены, ибо в реальной жизни фильмов будет ни как ни 10000 )
Всем Спасибо. Думаю тема закрыта. Потом добавлю результаты.
 

Rammstein

PHPClub::News
Если продолжать Steamroller'а, то можешь посмотрть ещё "подзапросы".
У меня раньше была такая идея - "всё одним запросом", но это гемор. Проще реализовать кэширование готового результата и не парить мозги.
 

texrdcom

Новичок
А еще проще проэктировать базу до создания таблиц )
1) Какие данные будут храниться
2) Какие данные и в каком порядке должны выводиться - с базы
3) Создаем таблици - стараемся делать так чтоб данные не дублировались с разных таблиц.
4) Делаем таблици в которых будут записанны id записей с других таблиц - и с помощью данных промежуточных таблиц мы и будем вытаскивать фильмы ! но не по текстовым значениям а по id
Есть программы которые помагают проэктировать базы ищи по форуму.
Можно и без них карандаш и лист....
 
Сверху