【问题标题】:Insert Foreign Key with two tables containing the same columns in SQLite with Python使用 Python 在 SQLite 中插入包含相同列的两个表的外键
【发布时间】:2021-04-13 12:32:27
【问题描述】:

我的计算机上有一个带有两个表的 SQLite 数据库:

  • table_post(包含有关 Twitter 帖子的信息:喜欢、作者、URL 等...)
  • table_profile(包含有关 Twitter 个人资料的信息:用户名、描述、关注者等...)

我正在使用 python 脚本创建两个单独的 CSV 文件,其中包含 table_post 和 table_profile 的行。

然后我使用另一个脚本将 CSV 文件传输到 SQLite 数据库。一切正常,直到我想用外键链接这两个表。

我的 table_post 有这些列:post_ID(PK)、profile_ID(FK)、postUrl、postText、pubDate、commentCount、likeCount、profileUrl

我的表配置文件有这些列:profile_ID(PK)、profileUrl、subCount、userName、profileDesc

两个表都有 profileUrl,我想使用 profileUrl 列将相应的 table_profile.profile_ID 插入 table_post.profile_ID。

我知道我们可以使用这个 SQLite 查询来连接行:

SELECT * FROM table_profile
JOIN table_post ON table_profile.profileUrl = table_post.profileUrl; 

我想使用 Python 和 sqlite3 在 post_ID 中插入相应的 profile_ID。 我能做什么 ? 写入 SQLite 数据库时是否需要写入 ID?如果是,如何?

我可以编写一个函数来检查 post_table 中的每一行并将其与 profile_ID 相关联吗?如果是,如何?

谢谢。

【问题讨论】:

    标签: python sqlite sql-update foreign-keys primary-key


    【解决方案1】:

    您需要的是UPDATE 声明,而不是INSERT

    在将 CSV 文件传输到 SQLite 数据库后,您必须更新表 table_post

    UPDATE table_post
    SET profile_ID = (SELECT profile.profile_ID FROM profile WHERE profile.profileUrl = table_post.profileUrl)
    

    如果您的 SQLite 版本是 3.33.0+,您可以使用 UPDATE...FROM 语法:

    UPDATE table_post AS t
    SET profile_ID = p.profile_ID 
    FROM profile AS p
    WHERE p.profileUrl = t.profileUrl
    

    【讨论】:

    • 谢谢!这正是我所需要的,它现在可以工作了!
    【解决方案2】:

    如果您已经从 CSV 加载了两个表,并且您没有使用或不希望使用外键约束,那么您可以简单地运行 UPDATE,例如

    UPDATE table_post 
        SET profile_ID = (SELECT table_profile.profile 
        FROM table_profile 
        WHERE table_profile.profileurl = table_post.profileurl)
    ;
    

    但是,如果您想使用外键约束来强制引用完整性,并且/或者如果您想规范化 profileurl(减少数据的重复),那么另一种方法是到

    1. 读取配置文件 CSV 文件并插入/加载配置文件,
    2. 然后读取帖子的 CSV 文件并使用子查询插入/加载帖子,以在 INSERT 期间解析帖子的 profile_ID。
    • 除非关闭外键处理,否则插入/加载后的 UPDATE 会因外键约束异常而失败(请参阅 PRAGMA Foreign Keys

      • 您可能必须打开外键处理,因为默认情况下它已关闭。
    • 使用外键约束不仅可以强制引用完整性,还可以将配置文件中的更新和删除 CASCADE 到帖子。例如。如果您要删除个人资料,则与该个人资料相关的所有帖子都将被删除,而不是外键异常。

      • 不使用外键约束删除配置文件可能会孤立帖子(让它们没有相关的配置文件)。
    • 第一种方法的关键是使用子查询(SELECT profile_id FROM table_profile WHERE profileurl = 'url2') 设置 table_post profile_id 列。这将增加所花费的时间(因此,如果使用 UNIQUE 约束,则会减少时间,但代价是插入配置文件表需要更长的时间,看到它们是插入配置文件的父母将不那么重要)。

    • 关于标准化;在您当前的模型中,您将 profileurl 存储在父级(配置文件)和子级(帖子)中,但似乎不想将其用于关系,因此不需要将 profileurl 存储在 post 表中。可以释放多余的存储空间。此外,您不必维护重复的事件。假设您的个人资料 X 的 url 为 x_is_here 并且由于某种原因需要更改为 x 的 url 不在此处,您将不得不更改与 X 相关的所有帖子(如果未标准化)。但是,如果 url 只存储在配置文件中,那么它只需要更改一次。

    另一种方法是利用已经存在的关系,即使用 profileurl。但是,如果你想强制引用完整性,那么 table_profile 和 table_post 需要稍微修改一下,插入/加载就可以了。

    示例

    这是一个基于您描述的架构但使用外键约束的示例。此外,还有一种利用 profileurl 建立关系的替代方案。

    DROP TABLE IF EXISTS table_post;
    DROP TABLE IF EXISTS table_post_alt;
    DROP TABLE IF EXISTS table_profile;
    CREATE TABLE IF NOT EXISTS table_profile (
        profile_id INTEGER PRIMARY KEY, 
        profileurl TEXT UNIQUE, 
        subCount INTEGER, 
        userName, 
        profileDesc TEXT
    );
    CREATE TABLE IF NOT EXISTS table_post (
        post_id INTEGER PRIMARY KEY, 
        profile_id INTEGER REFERENCES table_profile(profile_id), 
        post_url TEXT, 
        postText TEXT, 
        pubDate TEXT, 
        commentCount INTEGER, 
        likeCount INTEGER, 
        profileurl
    );
    CREATE TABLE IF NOT EXISTS table_post_alt (
        post_id INTEGER PRIMARY KEY, 
        profile_id INTEGER, 
        post_url TEXT, 
        postText TEXT, 
        pubDate TEXT, 
        commentCount INTEGER, likeCount INTEGER, profileurl REFERENCES table_profile(profileurl));
    
    INSERT OR IGNORE INTO table_profile VALUES
        (null,'url1',10,'user1','blah1')
        ,(null,'url2',100,'user2','blah2')
        ,(null,'url3',10,'user3','blah3')
        /* etc....  */
        ,(null,'url2',100,'user2','blah2') /* purposeful duplicate (ignored) */
    ;
    INSERT OR IGNORE INTO table_post VALUES 
        (null,(SELECT profile_id FROM table_profile WHERE profileurl = 'url2'),'post_url1','post text 1st post','2020-04-01',5,7,'url2')
        ,(null,(SELECT profile_id FROM table_profile WHERE profileurl = 'url1'),'post_url2','post text 2nd post','2020-04-01',5,7,'url1')
        ,(null,(SELECT profile_id FROM table_profile WHERE profileurl = 'url1'),'post_url3','post text 3rd post','2020-04-01',5,7,'url1')
        ,(null,(SELECT profile_id FROM table_profile WHERE profileurl = 'url3'),'post_url4','post text 4th post','2020-04-01',5,7,'url3')
        ,(null,(SELECT profile_id FROM table_profile WHERE profileurl = 'url2'),'post_url5','post text 5th post','2020-04-01',5,7,'url2')
        ,(null,(SELECT profile_id FROM table_profile WHERE profileurl = 'url1'),'post_url6','post text 6th post','2020-04-01',5,7,'url1')
        /* etc .... */
    ;
    INSERT OR IGNORE INTO table_post_alt VALUES
        (null,'does not matter','post_url1','post text 1st post','2020-04-01',5,7,'url2')
        ,(null,'does not matter','post_url2','post text 2nd post','2020-04-01',5,7,'url1')
        ,(null,'does not matter','post_url3','post text 3rd post','2020-04-01',5,7,'url1')
        ,(null,'does not matter','post_url4','post text 4th post','2020-04-01',5,7,'url3')
        ,(null,'does not matter','post_url5','post text 5th post','2020-04-01',5,7,'url2')
        ,(null,'does not matter','post_url6','post text 6th post','2020-04-01',5,7,'url1')
    ;
    
    SELECT * FROM table_profile
    JOIN table_post ON table_profile.profileUrl = table_post.profileUrl; 
    SELECT * FROM table_profile
    JOIN table_post_alt ON table_profile.profileUrl = table_post_alt.profileUrl; 
    

    运行上述结果(使用您的查询):-

    并且(使用仅针对替代表名修改的查询):-

    • 注意值does not matter 已用于表明由于 FK 是 profileurl,profile_Id 不匹配并不重要 (您可以使用与第一个选项相同的方法来获得正确的值但是然后数据没有完全标准化,它不是原始的,因为你有两次出现的profileurl)

    【讨论】:

    • 感谢您的辛勤工作,确实我没有想过profileUrl更改并在post表中留下孤儿以及我的数据库中的数据重复问题。我打算再次考虑规范化我的数据,谢谢!
    猜你喜欢
    • 2014-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-27
    • 1970-01-01
    • 2022-12-12
    • 1970-01-01
    相关资源
    最近更新 更多