使用 Python 在 SQLite 中插入包含相同列的两个表的外键
Insert Foreign Key with two tables containing the same columns in SQLite with Python
我的电脑上有一个 SQLite 数据库,有两个 tables :
- table_post(包含有关推特 post 的信息:喜欢、作者、URL 等...)
- table_profile(包含有关 Twitter 个人资料的信息:用户名、描述、关注者等...)
我正在使用 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(减少数据的重复)那么另一种方法是
- 阅读个人资料 CSV 文件和insert/load个人资料,
- 然后读取 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).
我的电脑上有一个 SQLite 数据库,有两个 tables :
- table_post(包含有关推特 post 的信息:喜欢、作者、URL 等...)
- table_profile(包含有关 Twitter 个人资料的信息:用户名、描述、关注者等...)
我正在使用 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(减少数据的重复)那么另一种方法是
- 阅读个人资料 CSV 文件和insert/load个人资料,
- 然后读取 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).