如何在 SQLite3 中获取与给定 table 具有一对一关系的 table 列表?
How to get a list of tables that have one-on-one relationship to a given table in SQLite3?
有没有办法获取与 SQLite3 中的给定 table 具有一对一关系的 table 列表?
例如这里的tableab
与tableabc
和abd
都是一对一的关系。 return abc
和 abd
是否有针对给定 table 名称 ab
的一个或多个查询?
-- By default foreign key is diabled in SQLite3
PRAGMA foreign_keys = ON;
CREATE TABLE a (
aid INTEGER PRIMARY KEY
);
CREATE TABLE b (
bid INTEGER PRIMARY KEY
);
CREATE TABLE ab (
aid INTEGER,
bid INTEGER,
PRIMARY KEY (aid, bid)
FOREIGN KEY (aid) REFERENCES a(aid)
FOREIGN KEY (bid) REFERENCES b(bid)
);
-- tables 'ab' and 'abc' have a one-on-one relationship
CREATE TABLE abc (
aid INTEGER,
bid INTEGER,
name TEXT NOT NULL,
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab(aid, bid)
);
-- tables 'ab' and 'abd' have a one-on-one relationship
CREATE TABLE abd (
aid INTEGER,
bid INTEGER,
value INTEGER CHECK( value > 0 ),
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab(aid, bid)
);
CREATE TABLE w (
id INTEGER PRIMARY KEY
);
以下繁琐的过程可能会得到我想要的 table 列表:
获取 table ab
的主键:
SELECT l.name FROM pragma_table_info('ab') as l WHERE l.pk > 0;
获取其他 table 的外键(这种情况适用于 table abd
):
SELECT * from pragma_foreign_key_list('abd');
进行解析以获取一对一关系的 table 列表。
不过,我希望一定有更优雅的方法。
对于 SQL 服务器,有 sys.foreign_keys
和 referenced_object_id
可用(参见 post)。也许在 SQLite?
中有类似的东西
编辑:再添加两个 table 用于测试
-- tables 'ab' and 'abe' have a one-on-one relationship
CREATE TABLE abe (
aid INTEGER,
bid INTEGER,
value INTEGER CHECK( value < 0 ),
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab
);
-- tables 'ab' and 'abf' have a one-on-one relationship
CREATE TABLE abf (
aidQ INTEGER,
bidQ INTEGER,
value INTEGER,
PRIMARY KEY (aidQ, bidQ) FOREIGN KEY (aidQ, bidQ) REFERENCES ab(aid, bid)
);
编辑:验证 table abe
的 FK
sqlite> PRAGMA foreign_keys;
1
sqlite> .schema abe
CREATE TABLE abe (
aid INTEGER,
bid INTEGER,
value INTEGER CHECK( value < 0 ),
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab
);
sqlite> DELETE FROM abe;
sqlite> INSERT INTO abe (aid, bid, value) VALUES (2, 1, -21);
sqlite> INSERT INTO abe (aid, bid, value) VALUES (-2, 1, -21);
Error: FOREIGN KEY constraint failed
sqlite> SELECT * FROM ab;
1|1
1|2
2|1
Is there a way to get a list of tables that have one-on-one relationship to a given table in SQLite3?
不确定,因为编写外键约束不定义关系(而是支持关系),即关系可以在没有 FK 约束的情况下存在。
外键约束定义:-
- a) 强制参照完整性的规则
- b) 可选地 maintains/alters 当引用的列发生更改时的引用完整性(
ON DELETE
和 ON UPDATE
)
因此查看外键列表只会告诉您 where/if 已对 FK 约束进行编码。
表示以下将获得具有约束的 tables 和引用的 tables.
更优雅见仁见智,所以由你决定:-
WITH cte_part(name,reqd,rest) AS (
SELECT name,'',substr(sql,instr(sql,' REFERENCES ') + 12)||' REFERENCES '
FROM sqlite_master
WHERE sql LIKE '% REFERENCES %(%'
UNION ALL
SELECT
name,
substr(rest,0,instr(rest,' REFERENCES ')),
substr(rest,instr(rest,' REFERENCES ') + 12)
FROM cte_part
WHERE length(rest) > 12
)
SELECT DISTINCT
CASE
WHEN length(reqd) < 1 THEN name
ELSE
CASE substr(reqd,1,1)
WHEN '''' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
WHEN '[' THEN substr(replace(replace(reqd,'[',''),']',''),1,instr(reqd,'(')-3)
WHEN '`' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
ELSE substr(reqd,1,instr(reqd,'(')-1)
END
END AS tablename
FROM cte_part
;
例如 use/results :-
- Navicat 截图
这是对上述的改编,其中包括在适当情况下引用父项的子项table:-
WITH cte_part(name,reqd,rest) AS (
SELECT name,'',substr(sql,instr(sql,' REFERENCES ') + 12)||' REFERENCES '
FROM sqlite_master
WHERE sql LIKE '% REFERENCES %(%'
UNION ALL
SELECT
name,
substr(rest,0,instr(rest,' REFERENCES ')),
substr(rest,instr(rest,' REFERENCES ') + 12)
FROM cte_part
WHERE length(rest) > 12
)
SELECT DISTINCT
CASE
WHEN length(reqd) < 1 THEN name
ELSE
CASE substr(reqd,1,1)
WHEN '''' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
WHEN '[' THEN substr(replace(replace(reqd,'[',''),']',''),1,instr(reqd,'(')-3)
WHEN '`' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
ELSE substr(reqd,1,instr(reqd,'(')-1)
END
END AS tablename,
CASE WHEN length(reqd) < 1 THEN '' ELSE name END AS referrer
FROM cte_part
;
结果示例:-
artists table 被 albums 引用为 SQL 用于创建 相册 table 是 CREATE TABLE 'albums'([AlbumId] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,[Title] TEXT NOT NULL ,[ArtistId] INTEGER NOT NULL , FOREIGN KEY ([ArtistId]) REFERENCES 'artists'([ArtistId]))
即FOREIGN KEY ([ArtistId]) REFERENCES
'artists'
([ArtistId]))
员工table根据CREATE TABLE 'employees'(.... REFERENCES 'employees'([EmployeeId]))
自我引用
补充评论:-
(I am still trying to understand your code...)
该代码基于从 sqlite_master 中选择行,其中该行用于 table(类型 = 'table'),而不是索引、触发器或视图,并且其中sql 列包含单词 REFERENCES,前后有一个 space,后面还有一个左括号。
- 最后一个条件用来剔除
CREATE TABLE oops (`REFERENCES` TEXT, `x REFERENCES Y`);
对于每个选定的行,输出 3 列:-
- name 这是从 sqlite_master、
的名称列中提取的 table 的名称
- reqd最初是一个空字符串(即initial)
- rest 后缀为 REFERENCES 的 sql 的其余部分 table。
UNION ALL 添加的行基于新添加到 CTE 的内容,即三列是按照 :-
提取的
- 名字是名字
- reqd 是从其余列到第一个 REFERENCES 项(即 table 和引用列)的 sql
- rest 是参考术语
之后的 sql
与任何递归一样,需要检测结束,这是当整个 sql 语句已减少到小于 12(即“ REFERENCES ”的长度,用于拆分sql声明)。
这就是所谓的 RECURSIVE CTE
最后查询得到的CTE。如果 reqd 字段为空,则 tablename 列是名称列,否则(即 reqd 列包含数据(sql 的一部分))提取 table 名称(部分向上如果未包含在左括号中(`,' 或 [ with ]))或从括号之间提取。
如果包含所有 CTE 列(部分数据已被截断),则最终查询结果如下:-
可以清楚地看到提取的 sql 逐渐减少
答案是原则上的,没有经过广泛测试以考虑所有情况,可能需要裁剪。
备选
虽然不是单个查询解决方案,但以下仅需要 submission/execution 一系列查询,因此与平台无关。
它围绕使用两个 tables:-
- sqlite_master
的工作副本
- 一个工作 table 来存储
SELECT pragma_foreign_key_list(?)
的输出
两个 table 都是通过 CREATE-SELECT 创建的,尽管它们都没有复制任何行,因此 table 是空的。
触发器应用于 sqlite_master 的工作副本以插入存储 SELECT 结果的 table pragma_foreign_key_list(table_name_from_insert) ;
相关行是通过 SELECT INSERT 从 sqlite_master 复制的,因此触发会填充存储 table。
以下是测试代码:-
DROP TABLE IF EXISTS fklist;
DROP TABLE IF EXISTS master_copy;
DROP TRIGGER IF EXISTS load_fklist;
/* Working version of foreign_key_list to store ALL results of SELECT pragma_foreign_key_list invocation */
CREATE TABLE IF NOT EXISTS fklist AS SELECT '' AS child,*
FROM pragma_foreign_key_list((SELECT name FROM sqlite_master WHERE type = 'not a type' LIMIT 1));
/* Working version of sqlite master */
CREATE TABLE IF NOT EXISTS master_copy AS SELECT * FROM sqlite_master WHERE type = 'not a type';
/* Add an after insert trigger for master copy to add to fklist */
CREATE TRIGGER IF NOT EXISTS load_fklist
AFTER INSERT ON master_copy
BEGIN
INSERT INTO fklist SELECT new.name,* FROM pragma_foreign_key_list(new.name);
END
;
/* Populate master_copy from sqlite_master (relevant rows)
and thus build the fklist
*/
INSERT INTO master_copy SELECT *
FROM sqlite_master
WHERE type = 'table'
AND instr(sql,' REFERENCES ') > 0
;
SELECT * FROM fklist;
DROP TABLE IF EXISTS fklist;
DROP TABLE IF EXISTS master_copy;
DROP TRIGGER IF EXISTS load_fklist;
根据之前的答案使用类似的测试基础,上述结果为:-
有没有办法获取与 SQLite3 中的给定 table 具有一对一关系的 table 列表?
例如这里的tableab
与tableabc
和abd
都是一对一的关系。 return abc
和 abd
是否有针对给定 table 名称 ab
的一个或多个查询?
-- By default foreign key is diabled in SQLite3
PRAGMA foreign_keys = ON;
CREATE TABLE a (
aid INTEGER PRIMARY KEY
);
CREATE TABLE b (
bid INTEGER PRIMARY KEY
);
CREATE TABLE ab (
aid INTEGER,
bid INTEGER,
PRIMARY KEY (aid, bid)
FOREIGN KEY (aid) REFERENCES a(aid)
FOREIGN KEY (bid) REFERENCES b(bid)
);
-- tables 'ab' and 'abc' have a one-on-one relationship
CREATE TABLE abc (
aid INTEGER,
bid INTEGER,
name TEXT NOT NULL,
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab(aid, bid)
);
-- tables 'ab' and 'abd' have a one-on-one relationship
CREATE TABLE abd (
aid INTEGER,
bid INTEGER,
value INTEGER CHECK( value > 0 ),
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab(aid, bid)
);
CREATE TABLE w (
id INTEGER PRIMARY KEY
);
以下繁琐的过程可能会得到我想要的 table 列表:
获取 table
ab
的主键:SELECT l.name FROM pragma_table_info('ab') as l WHERE l.pk > 0;
获取其他 table 的外键(这种情况适用于 table
abd
):SELECT * from pragma_foreign_key_list('abd');
进行解析以获取一对一关系的 table 列表。
不过,我希望一定有更优雅的方法。
对于 SQL 服务器,有 sys.foreign_keys
和 referenced_object_id
可用(参见 post)。也许在 SQLite?
编辑:再添加两个 table 用于测试
-- tables 'ab' and 'abe' have a one-on-one relationship
CREATE TABLE abe (
aid INTEGER,
bid INTEGER,
value INTEGER CHECK( value < 0 ),
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab
);
-- tables 'ab' and 'abf' have a one-on-one relationship
CREATE TABLE abf (
aidQ INTEGER,
bidQ INTEGER,
value INTEGER,
PRIMARY KEY (aidQ, bidQ) FOREIGN KEY (aidQ, bidQ) REFERENCES ab(aid, bid)
);
编辑:验证 table abe
的 FKsqlite> PRAGMA foreign_keys;
1
sqlite> .schema abe
CREATE TABLE abe (
aid INTEGER,
bid INTEGER,
value INTEGER CHECK( value < 0 ),
PRIMARY KEY (aid, bid) FOREIGN KEY (aid, bid) REFERENCES ab
);
sqlite> DELETE FROM abe;
sqlite> INSERT INTO abe (aid, bid, value) VALUES (2, 1, -21);
sqlite> INSERT INTO abe (aid, bid, value) VALUES (-2, 1, -21);
Error: FOREIGN KEY constraint failed
sqlite> SELECT * FROM ab;
1|1
1|2
2|1
Is there a way to get a list of tables that have one-on-one relationship to a given table in SQLite3?
不确定,因为编写外键约束不定义关系(而是支持关系),即关系可以在没有 FK 约束的情况下存在。
外键约束定义:-
- a) 强制参照完整性的规则
- b) 可选地 maintains/alters 当引用的列发生更改时的引用完整性(
ON DELETE
和ON UPDATE
)
因此查看外键列表只会告诉您 where/if 已对 FK 约束进行编码。
表示以下将获得具有约束的 tables 和引用的 tables.
更优雅见仁见智,所以由你决定:-
WITH cte_part(name,reqd,rest) AS (
SELECT name,'',substr(sql,instr(sql,' REFERENCES ') + 12)||' REFERENCES '
FROM sqlite_master
WHERE sql LIKE '% REFERENCES %(%'
UNION ALL
SELECT
name,
substr(rest,0,instr(rest,' REFERENCES ')),
substr(rest,instr(rest,' REFERENCES ') + 12)
FROM cte_part
WHERE length(rest) > 12
)
SELECT DISTINCT
CASE
WHEN length(reqd) < 1 THEN name
ELSE
CASE substr(reqd,1,1)
WHEN '''' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
WHEN '[' THEN substr(replace(replace(reqd,'[',''),']',''),1,instr(reqd,'(')-3)
WHEN '`' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
ELSE substr(reqd,1,instr(reqd,'(')-1)
END
END AS tablename
FROM cte_part
;
例如 use/results :-
- Navicat 截图
这是对上述的改编,其中包括在适当情况下引用父项的子项table:-
WITH cte_part(name,reqd,rest) AS (
SELECT name,'',substr(sql,instr(sql,' REFERENCES ') + 12)||' REFERENCES '
FROM sqlite_master
WHERE sql LIKE '% REFERENCES %(%'
UNION ALL
SELECT
name,
substr(rest,0,instr(rest,' REFERENCES ')),
substr(rest,instr(rest,' REFERENCES ') + 12)
FROM cte_part
WHERE length(rest) > 12
)
SELECT DISTINCT
CASE
WHEN length(reqd) < 1 THEN name
ELSE
CASE substr(reqd,1,1)
WHEN '''' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
WHEN '[' THEN substr(replace(replace(reqd,'[',''),']',''),1,instr(reqd,'(')-3)
WHEN '`' THEN substr(replace(reqd,substr(reqd,1,1),''),1,instr(reqd,'(')-3)
ELSE substr(reqd,1,instr(reqd,'(')-1)
END
END AS tablename,
CASE WHEN length(reqd) < 1 THEN '' ELSE name END AS referrer
FROM cte_part
;
结果示例:-
artists table 被 albums 引用为 SQL 用于创建 相册 table 是
CREATE TABLE 'albums'([AlbumId] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,[Title] TEXT NOT NULL ,[ArtistId] INTEGER NOT NULL , FOREIGN KEY ([ArtistId]) REFERENCES 'artists'([ArtistId]))
即
FOREIGN KEY ([ArtistId]) REFERENCES
'artists'
([ArtistId]))
员工table根据
自我引用CREATE TABLE 'employees'(.... REFERENCES 'employees'([EmployeeId]))
补充评论:-
(I am still trying to understand your code...)
该代码基于从 sqlite_master 中选择行,其中该行用于 table(类型 = 'table'),而不是索引、触发器或视图,并且其中sql 列包含单词 REFERENCES,前后有一个 space,后面还有一个左括号。
- 最后一个条件用来剔除
CREATE TABLE oops (`REFERENCES` TEXT, `x REFERENCES Y`);
对于每个选定的行,输出 3 列:-
- name 这是从 sqlite_master、 的名称列中提取的 table 的名称
- reqd最初是一个空字符串(即initial)
- rest 后缀为 REFERENCES 的 sql 的其余部分 table。
UNION ALL 添加的行基于新添加到 CTE 的内容,即三列是按照 :-
提取的- 名字是名字
- reqd 是从其余列到第一个 REFERENCES 项(即 table 和引用列)的 sql
- rest 是参考术语 之后的 sql
与任何递归一样,需要检测结束,这是当整个 sql 语句已减少到小于 12(即“ REFERENCES ”的长度,用于拆分sql声明)。
这就是所谓的 RECURSIVE CTE
最后查询得到的CTE。如果 reqd 字段为空,则 tablename 列是名称列,否则(即 reqd 列包含数据(sql 的一部分))提取 table 名称(部分向上如果未包含在左括号中(`,' 或 [ with ]))或从括号之间提取。
如果包含所有 CTE 列(部分数据已被截断),则最终查询结果如下:-
可以清楚地看到提取的 sql 逐渐减少
答案是原则上的,没有经过广泛测试以考虑所有情况,可能需要裁剪。
备选
虽然不是单个查询解决方案,但以下仅需要 submission/execution 一系列查询,因此与平台无关。
它围绕使用两个 tables:-
- sqlite_master 的工作副本
- 一个工作 table 来存储
SELECT pragma_foreign_key_list(?)
的输出
两个 table 都是通过 CREATE-SELECT 创建的,尽管它们都没有复制任何行,因此 table 是空的。
触发器应用于 sqlite_master 的工作副本以插入存储 SELECT 结果的 table pragma_foreign_key_list(table_name_from_insert) ;
相关行是通过 SELECT INSERT 从 sqlite_master 复制的,因此触发会填充存储 table。
以下是测试代码:-
DROP TABLE IF EXISTS fklist;
DROP TABLE IF EXISTS master_copy;
DROP TRIGGER IF EXISTS load_fklist;
/* Working version of foreign_key_list to store ALL results of SELECT pragma_foreign_key_list invocation */
CREATE TABLE IF NOT EXISTS fklist AS SELECT '' AS child,*
FROM pragma_foreign_key_list((SELECT name FROM sqlite_master WHERE type = 'not a type' LIMIT 1));
/* Working version of sqlite master */
CREATE TABLE IF NOT EXISTS master_copy AS SELECT * FROM sqlite_master WHERE type = 'not a type';
/* Add an after insert trigger for master copy to add to fklist */
CREATE TRIGGER IF NOT EXISTS load_fklist
AFTER INSERT ON master_copy
BEGIN
INSERT INTO fklist SELECT new.name,* FROM pragma_foreign_key_list(new.name);
END
;
/* Populate master_copy from sqlite_master (relevant rows)
and thus build the fklist
*/
INSERT INTO master_copy SELECT *
FROM sqlite_master
WHERE type = 'table'
AND instr(sql,' REFERENCES ') > 0
;
SELECT * FROM fklist;
DROP TABLE IF EXISTS fklist;
DROP TABLE IF EXISTS master_copy;
DROP TRIGGER IF EXISTS load_fklist;
根据之前的答案使用类似的测试基础,上述结果为:-