Избегание декартового произведения в PostgreSQL с Python - PullRequest
0 голосов
/ 19 января 2020

Я пытаюсь создать обучающее приложение в python для работы с базой данных фильмов, добавляя детали mov ie через текстовое меню, запрашивая ввод данных пользователем для всех полей (mov ie name, актеры, компания, et c.).

Я использую PostgreSQL в качестве базы данных и импортирую psycopg2 в Python. В базе данных я использую отношение «многие ко многим» (поскольку один и тот же актер может сниматься в нескольких фильмах и т. Д.). Таким образом, у меня есть таблица фильмы ( идентификатор фильма, имя, компания, год ), таблица актеров ( имя_актера, фамилия, имя_имя, имя_актора ) и третья промежуточная таблица ctors_movies ( actor_id, movie_id ) внешних ключей для соединения двух, с помощью (actor_id, movie_id) сделанный первичным ключом .

Тогда у меня есть этот код в Python (это конечная стадия ввода данных пользователем через текстовый интерфейс):

def insert_movie(name, actors, company, year):
    connection = psycopg2.connect(user='postgres', password='postgres', database='movie')
    cursor = connection.cursor()

    query1 = "INSERT INTO movies (name, company, year) VALUES (%s, %s, %s);"
    cursor.execute(query1, (name, company, year))
    query2 = 'INSERT INTO actors (last_name, first_name, actor_ordinal) VALUES (%s, %s, %s);'
    cursor.executemany(query2, [tuple(actor) for actor in actors])
    query3 = 'INSERT INTO actors_movies (actor_id, movie_id) SELECT actor_id, movie_id FROM actors, movies ON CONFLICT DO NOTHING;'
    cursor.execute(query3)

    connection.commit()
    connection.close()

Он отлично работает для query1 и query2 , Но query3 (который нацелен на промежуточную таблицу ctors_movies ) дублирует все данные. Каждый раз, когда я добавляю новый mov ie, в промежуточной таблице присутствуют все актеры из других фильмов, например (после объединения):

movie_id    movie_name    actor_id
1           The Matrix    1
1           The Matrix    2
1           The Matrix    3
1           The Matrix    4
2           Titanic       1
2           Titanic       2
2           Titanic       3
2           Titanic       4
3           Patriot       1
3           Patriot       2
3           Patriot       3
3           Patriot       4

Итак, это совершенно неправильно, так как, согласно этому Стол, все четыре актера фигурируют во всех трех фильмах, что неверно. Почему так происходит и как можно устранить эту проблему? Я не могу найти какой-либо вариант решения этой проблемы.

Ответы [ 2 ]

0 голосов
/ 19 января 2020

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

Эта концепция проиллюстрирована с формальной (математической) точки зрения при на следующей странице википедии

https://en.wikipedia.org/wiki/Cartesian_product

Tutorialspoint также содержит хороший пример и объяснение, которые могут быть полезны для понимания вашей проблемы

https://www.tutorialspoint.com/sql/sql-cartesian-joins.htm

TL; DR

Чтобы избежать декартового объединения при вставке новых значений, вам нужно будет вставить их вручную или применить условие после объединения две таблицы (актеры и фильмы), как показано ниже

ВСТАВИТЬ INTOctors_movies (actor_id, movie_id) ВЫБРАТЬ actor_id, movie_id ИЗ актеров, фильмы ГДЕ актеры.id + movies.id <4; </p>

0 голосов
/ 19 января 2020

Вы можете поставить отдельное предложение select третьего запроса, который удалит дубликаты. Вставляя связанные данные в несколько таблиц, сохраните некоторые ключи, например: mov ie будет иметь movie_id, который может быть указан в таблице актеров. Затем используйте эту клавишу для соединения mov ie и таблицы актеров.

...