【问题标题】:Avoiding Cartesian product in PostgreSQL with Python使用 Python 在 PostgreSQL 中避免笛卡尔积
【发布时间】:2020-01-19 16:51:19
【问题描述】:

我正在尝试在 python 中创建一个培训应用程序来处理电影数据库,通过文本菜单添加电影详细信息,提示用户输入所有字段(电影名称、演员、公司等)。

我使用 PostgreSQL 作为数据库并在 Python 中导入 psycopg2。在数据库中,我使用了多对多的关系(因为同一个演员可以出演多部电影,等等)。因此,我有一个 movies 表(movie_id, name, company, year),一个 actors 表(actor_id, last_name, first_name , actor_ordinal) 和第三个中间表 actors_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(以中间 actors_movies 表为目标)复制了所有数据。每次我添加一部新电影时,中间表中都会包含其他电影中的所有演员,例如(加入后):

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

所以,这是完全错误的,因为根据这张表,这三部电影中的所有四位演员都是错误的。 为什么会发生这种情况,如何消除这个问题?我找不到任何解决此问题的方法。

【问题讨论】:

    标签: python postgresql psycopg2


    【解决方案1】:

    您可以在第三个查询的 select 子句上放置一个 distinct,这将删除重复项。 在将相关数据插入多个表时,维护一些键,例如:movie会有movie_id,可以在actor表中引用然后使用该键连接movie和actor表。

    【讨论】:

      【解决方案2】:

      这就是关系数据库的工作方式。对于第三个查询,您正在对两个表(演员和电影)进行笛卡尔连接,因为您没有提供应用连接的约束。所以,数据库唯一能做的就是将表B中的所有记录分配给表A的每条记录。

      以下维基百科页面从形式(数学)的角度说明了这个概念

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

      Tutorialspoint 还有一个很好的示例和解释,可能有助于理解您的问题是什么

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

      TL;DR

      为了避免在插入新值时出现笛卡尔连接,您必须手动插入它们,或者在连接两个表(演员和电影)后应用条件,如下图所示

      INSERT INTO actor_movies (actor_id, movie_id) SELECT actor_id, movie_id FROM actor, movies WHERE actor.id + movies.id

      【讨论】:

      • 如果数字可以不同于 4,我该如何应用此条件?例如,用户可以添加 60 位演员的 6 部电影,然后再添加 20 部。
      • 我猜你只是一些用于填充和测试数据库的随机数据,对吧?在生产中,您需要参考电影,即电影的 id,以及参考演员,演员的 id。然后,您将在表中记录 。否则,您可以在 WHERE 子句中过滤电影名称和演员名称
      • 嗯,是的,它是一个填充了随机数据的数据库(来自我自己的输入,仅用于测试)。所有演员都有自己的身份,所有电影都有自己的身份。我创建了第三个中间表,用于连接两者并创建多对多关系。我仍然完全不明白 WHERE 子句在这方面有何帮助。
      • WHERE 子句可用于过滤数据,使其更接近真实案例场景的样子。此外,如果它只是随机数据,那么要么使用确切的值填充它,要么插入进行连接的数据,但在我看来它要复杂得多。
      • 你能告诉我如何插入精确的数据吗?我想这就是我的问题。书籍和作者的 id 都会自动填充为连续剧,所以我无法记住它们。应该有一些简单的编码方法,但我不知道。
      猜你喜欢
      • 2023-03-06
      • 1970-01-01
      • 2019-10-20
      • 2017-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-15
      • 2017-05-27
      相关资源
      最近更新 更多