使用 Python 在 SQLite 中插入包含相同列的两个表的外键

Insert Foreign Key with two tables containing the same columns in SQLite with Python

我的电脑上有一个 SQLite 数据库,有两个 tables :

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

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

我的 table_post 有这些列:post_ID(PK)、profile_ID(FK)、postUrl、postText , pubDate, commentCount, likeCount, 个人资料Url

我的 table 配置文件包含这些列:profile_ID(PK)、配置文件Url、subCount、用户名、profileDesc

两个 table 都有配置文件Url,我想使用配置文件Url 列将相应的 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 相关联吗?如果是,如何?

谢谢。

您需要的是 UPDATE 语句而不是 INSERT

将 CSV 文件传输到 SQLite 数据库后,您必须更新 table 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

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

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

但是,如果您想使用外键约束来强制执行参照完整性,AND/OR 如果您想规范化 profileurl(减少数据的重复)那么另一种方法是

  1. 阅读个人资料 CSV 文件和insert/load个人资料,
  2. 然后读取 post 的 CSV 文件和 insert/load posts 使用子查询解析 post 的 profile_ID插入。
  • inserts/loads 之后的更新将因外键约束异常而失败,除非外键处理已关闭(参见 PRAGMA Foreign Keys

    • 您可能需要打开外键处理,因为默认情况下它是关闭的。
  • 使用外键约束不仅可以强制执行参照完整性,还可以将配置文件的更新和删除级联到 post。例如。如果您要删除配置文件,那么与该配置文件相关的所有 post 都将被删除,而不是外键异常。

    • 在不使用外键约束的情况下删除配置文件可能会使 post 成为孤儿(使它们没有相关的配置文件)。
  • 第一个提出的方法的关键是使用子查询 (SELECT profile_id FROM table_profile WHERE profileurl = 'url2') 设置 table_post profile_id 列。这将增加所花费的时间(因此如果使用 UNIQUE 约束则减少,但随后以插入配置文件 table 花费更长的时间为代价,看到它们是 parents 插入配置文件将是更少的因素)。

  • 关于规范化;在您当前的模型中,您将配置文件url 存储在 parent(配置文件)和 children(posts) 中,但似乎不想将其用于关系,因此不需要将配置文件 url 存储在 post table 中。可以释放多余的存储 space。此外,您不必维护重复的事件。假设您的配置文件 X 的 url 为 x_is_here,并且出于某种原因需要将 url 更改为 x 不在此处,您将不得不更改所有 post与 X 相关(如果未归一化)。但是,如果 url 仅存储在配置文件中,则只需更改一次。

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

例子

这是一个基于您描述的架构但使用外键约束的示例。另外还有一个替代方案,它使用配置文件url 来建立关系。

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; 

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

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

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