如何使用多重继承检索存储在 SQL 中的属性
How to retrieve the properties stored in SQL with multiple inheritance
我将记录存储在 SQL 中,这些记录表示类似于 C++ 中的多重继承关系。像那样:
CREATE TABLE Classes
(
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE Inheritance
(
class_id INTEGER NOT NULL,
base_class_id INTEGER NOT NULL,
FOREIGN KEY (class_id) REFERENCES Classes(id),
FOREIGN KEY (base_class_id) REFERENCES Classes(id)
);
classes 具有两种类型的属性。 classes 继承了这些属性,但方式不同。为 class 定义的第一种类型 属性 会覆盖 在任何基础 class 中使用的相同 属性 的值es。另一种类型累加值:属性实际上是一组值,每个class继承它的基数class的所有值,加上可以向该集合添加一个额外的(单个)值:
CREATE TABLE OverridableValues
(
class_id INTEGER PRIMARY KEY,
value TEXT NOT NULL,
FOREIGN KEY (class_id) REFERENCES Classes(id)
);
CREATE TABLE AccumulableValues
(
class_id INTEGER PRIMARY KEY,
value TEXT NOT NULL,
FOREIGN KEY (class_id) REFERENCES Classes(id)
);
关于 OverridableValues
的警告:没有在多重继承的不同路径上覆盖相同 属性 的情况。
我正在尝试使用常见的 table 表达式来设计查询,对于给定的 属性 和 class。[=] return value/values 16=]
我尝试使用的方法是从根开始(为简单起见,假设只有一个根 class),然后构建从根到每个路径的树其他 class。问题是如何将有关属性的信息从 parents 传递到 children。例如下面是一个不正确的尝试:
WITH ParentProperty (id, value) AS
(
SELECT c.id, a.value
FROM Classes c
LEFT JOIN AccumulableValues a
ON a.class_id = c.id
WHERE c.id = 1 --This is the root
UNION ALL
SELECT i.class_id, IFNULL(a.value, ba.value)
FROM ParentProperty p
JOIN Inheritance i
ON i.base_class_id = p.id
LEFT JOIN AccumulableValues a
ON a.class_id = i.class_id
LEFT JOIN AccumulableValues ba
ON ba.class_id = i.base_class_id
)
SELECT id, value
FROM ParentProperty;
我觉得我在 CTE 里面还需要一个 UNION ALL
,这是不允许的。但如果没有它,我要么错过正确的价值观,要么错过继承的价值观。到目前为止,我未能为这两种类型的属性设计查询。
我正在使用 SQLite 作为我的数据库引擎。
我终于找到了解决办法。我在下面描述它,但仍然欢迎更有效的。
让我们从可累积 属性开始。我的问题是我试图将多个 UNION ALL
添加到单个 CTE 中。我通过添加额外的 CTE 解决了这个问题(参见 AcquiresFrom
)
WITH AcquiresFrom (class_id, from_class_id, value) AS
(
SELECT a.class_id, a.class_id, a.value
FROM AccumulatableValues a
UNION ALL
SELECT i.class_id, i.base_class_id, NULL
FROM Inheritance i
),
ClassProperty (class_id, value) AS
(
SELECT c.id, NULL
FROM Classes c
LEFT JOIN Inheritance i
ON i.class_id = c.id
WHERE i.base_class_id IS NULL
UNION ALL
SELECT a.class_id, IFNULL(a.value, p.value)
FROM ClassProperty p
JOIN AcquiresFrom a
ON (a.from_class_id = p.class_id AND a.from_class_id != a.class_id) OR
(a.class_id = p.class_id AND a.class_id = a.from_class_id AND p.value IS NULL)
)
SELECT DISTINCT class_id, value
FROM ClassProperty
WHERE value IS NOT NULL
ORDER BY class_id;
AcquiresFrom
表示获取值的方式:class要么引入一个新值(第一个子句),要么继承它(第二个子句)。 ClassProperty
将值从基础 class 逐渐传播到派生。唯一剩下要做的就是消除重复项和 NULL
值(最后一个子句 SELECT DISTINCT
/ WHERE value IS NOT NULL
)。
可重写的 属性 更复杂。
WITH Roots (id, value) AS
(
SELECT c.id, o.value
FROM Classes c
LEFT JOIN Inheritance i
ON i.class_id = c.id
LEFT JOIN OverridableValues o
ON o.class_id = c.id
WHERE i.base_class_id IS NULL
),
PossibleValues (id, acquired_from_id, value) AS
(
SELECT r.id, r.id, r.value
FROM Roots r
UNION ALL
SELECT i.class_id, CASE WHEN o.value IS NULL THEN p.acquired_from_id ELSE i.class_id END, IFNULL(o.value, p.value)
FROM PossibleValues p
JOIN Inheritance i
ON i.base_class_id = p.id
LEFT JOIN OverridableValues o
ON o.class_id = i.class_id
),
Split (class_id, base_class_id, direct) AS (
SELECT i.class_id, i.base_class_id, 1
FROM Inheritance i
UNION ALL
SELECT i.class_id, i.base_class_id, 0
FROM Inheritance i
),
Ancestors (id, ancestor_id) AS (
SELECT r.id, NULL
FROM Roots r
UNION ALL
SELECT s.class_id, CASE WHEN s.direct == 1 THEN a.id ELSE a.ancestor_id END
FROM Ancestors a
JOIN Split s
ON s.base_class_id = a.id
)
SELECT DISTINCT p.id, p.value
FROM PossibleValues p
WHERE p.acquired_from_id NOT IN
(
SELECT a.ancestor_id
FROM PossibleValues p1
JOIN PossibleValues p2
ON p2.id = p1.id
JOIN Ancestors a
ON a.id = p1.acquired_from_id AND a.ancestor_id = p2.acquired_from_id
WHERE p1.id = p.id
);
Roots
显然是没有parents的class列表。 PossibleValues
CTE propagates/overrides 从根到最终的值 classes,并打破了多个继承循环,使结构成为 tree-like。此查询的结果中存在所有有效的 id/value 对,但也存在一些无效值。这些无效值是那些在其中一个分支上被覆盖的值,但这一事实在另一个分支上是未知的。 acquired_from_id
允许我们重建第一个引入此值的 class 是谁(当两个不同的 class 引入相同的值时,这可能很有用)。
最后剩下的就是解决多重继承带来的歧义。知道 class 和两个可能的值,我们需要知道一个值是否覆盖另一个。这是用 Ancestors
表达式解决的。
我将记录存储在 SQL 中,这些记录表示类似于 C++ 中的多重继承关系。像那样:
CREATE TABLE Classes
(
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE Inheritance
(
class_id INTEGER NOT NULL,
base_class_id INTEGER NOT NULL,
FOREIGN KEY (class_id) REFERENCES Classes(id),
FOREIGN KEY (base_class_id) REFERENCES Classes(id)
);
classes 具有两种类型的属性。 classes 继承了这些属性,但方式不同。为 class 定义的第一种类型 属性 会覆盖 在任何基础 class 中使用的相同 属性 的值es。另一种类型累加值:属性实际上是一组值,每个class继承它的基数class的所有值,加上可以向该集合添加一个额外的(单个)值:
CREATE TABLE OverridableValues
(
class_id INTEGER PRIMARY KEY,
value TEXT NOT NULL,
FOREIGN KEY (class_id) REFERENCES Classes(id)
);
CREATE TABLE AccumulableValues
(
class_id INTEGER PRIMARY KEY,
value TEXT NOT NULL,
FOREIGN KEY (class_id) REFERENCES Classes(id)
);
关于 OverridableValues
的警告:没有在多重继承的不同路径上覆盖相同 属性 的情况。
我正在尝试使用常见的 table 表达式来设计查询,对于给定的 属性 和 class。[=] return value/values 16=]
我尝试使用的方法是从根开始(为简单起见,假设只有一个根 class),然后构建从根到每个路径的树其他 class。问题是如何将有关属性的信息从 parents 传递到 children。例如下面是一个不正确的尝试:
WITH ParentProperty (id, value) AS
(
SELECT c.id, a.value
FROM Classes c
LEFT JOIN AccumulableValues a
ON a.class_id = c.id
WHERE c.id = 1 --This is the root
UNION ALL
SELECT i.class_id, IFNULL(a.value, ba.value)
FROM ParentProperty p
JOIN Inheritance i
ON i.base_class_id = p.id
LEFT JOIN AccumulableValues a
ON a.class_id = i.class_id
LEFT JOIN AccumulableValues ba
ON ba.class_id = i.base_class_id
)
SELECT id, value
FROM ParentProperty;
我觉得我在 CTE 里面还需要一个 UNION ALL
,这是不允许的。但如果没有它,我要么错过正确的价值观,要么错过继承的价值观。到目前为止,我未能为这两种类型的属性设计查询。
我正在使用 SQLite 作为我的数据库引擎。
我终于找到了解决办法。我在下面描述它,但仍然欢迎更有效的。
让我们从可累积 属性开始。我的问题是我试图将多个 UNION ALL
添加到单个 CTE 中。我通过添加额外的 CTE 解决了这个问题(参见 AcquiresFrom
)
WITH AcquiresFrom (class_id, from_class_id, value) AS
(
SELECT a.class_id, a.class_id, a.value
FROM AccumulatableValues a
UNION ALL
SELECT i.class_id, i.base_class_id, NULL
FROM Inheritance i
),
ClassProperty (class_id, value) AS
(
SELECT c.id, NULL
FROM Classes c
LEFT JOIN Inheritance i
ON i.class_id = c.id
WHERE i.base_class_id IS NULL
UNION ALL
SELECT a.class_id, IFNULL(a.value, p.value)
FROM ClassProperty p
JOIN AcquiresFrom a
ON (a.from_class_id = p.class_id AND a.from_class_id != a.class_id) OR
(a.class_id = p.class_id AND a.class_id = a.from_class_id AND p.value IS NULL)
)
SELECT DISTINCT class_id, value
FROM ClassProperty
WHERE value IS NOT NULL
ORDER BY class_id;
AcquiresFrom
表示获取值的方式:class要么引入一个新值(第一个子句),要么继承它(第二个子句)。 ClassProperty
将值从基础 class 逐渐传播到派生。唯一剩下要做的就是消除重复项和 NULL
值(最后一个子句 SELECT DISTINCT
/ WHERE value IS NOT NULL
)。
可重写的 属性 更复杂。
WITH Roots (id, value) AS
(
SELECT c.id, o.value
FROM Classes c
LEFT JOIN Inheritance i
ON i.class_id = c.id
LEFT JOIN OverridableValues o
ON o.class_id = c.id
WHERE i.base_class_id IS NULL
),
PossibleValues (id, acquired_from_id, value) AS
(
SELECT r.id, r.id, r.value
FROM Roots r
UNION ALL
SELECT i.class_id, CASE WHEN o.value IS NULL THEN p.acquired_from_id ELSE i.class_id END, IFNULL(o.value, p.value)
FROM PossibleValues p
JOIN Inheritance i
ON i.base_class_id = p.id
LEFT JOIN OverridableValues o
ON o.class_id = i.class_id
),
Split (class_id, base_class_id, direct) AS (
SELECT i.class_id, i.base_class_id, 1
FROM Inheritance i
UNION ALL
SELECT i.class_id, i.base_class_id, 0
FROM Inheritance i
),
Ancestors (id, ancestor_id) AS (
SELECT r.id, NULL
FROM Roots r
UNION ALL
SELECT s.class_id, CASE WHEN s.direct == 1 THEN a.id ELSE a.ancestor_id END
FROM Ancestors a
JOIN Split s
ON s.base_class_id = a.id
)
SELECT DISTINCT p.id, p.value
FROM PossibleValues p
WHERE p.acquired_from_id NOT IN
(
SELECT a.ancestor_id
FROM PossibleValues p1
JOIN PossibleValues p2
ON p2.id = p1.id
JOIN Ancestors a
ON a.id = p1.acquired_from_id AND a.ancestor_id = p2.acquired_from_id
WHERE p1.id = p.id
);
Roots
显然是没有parents的class列表。 PossibleValues
CTE propagates/overrides 从根到最终的值 classes,并打破了多个继承循环,使结构成为 tree-like。此查询的结果中存在所有有效的 id/value 对,但也存在一些无效值。这些无效值是那些在其中一个分支上被覆盖的值,但这一事实在另一个分支上是未知的。 acquired_from_id
允许我们重建第一个引入此值的 class 是谁(当两个不同的 class 引入相同的值时,这可能很有用)。
最后剩下的就是解决多重继承带来的歧义。知道 class 和两个可能的值,我们需要知道一个值是否覆盖另一个。这是用 Ancestors
表达式解决的。