如何在sqlite中拥有多个表/字段的外键

问题描述 投票:0回答:1

我在 Sqlite 数据库中有一个表,当前它是另一个表中字段的外键。 但是,我需要更改该外键,以便它可以指向多个表。

在下面的例子中,最初我只处理来自地球上某个国家的起源的怪物。 现在,我想添加怪物来自不同星球的可能性。 有没有办法在不添加中间表或重命名表或向“Monster”表或任何其他表添加附加字段的情况下执行此操作?

我想通过修改外键来做到这一点。

CREATE TABLE Monster (
  Id INTEGER PRIMARY KEY,
  Name VARCHAR NOT NULL UNIQUE,
  Origin TEXT,
  FOREIGN KEY (Origin) REFERENCES Country(Name) ON UPDATE CASCADE
);

CREATE TABLE Country (
   Id INTEGER PRIMARY KEY,
   Name VARCHAR NOT NULL UNIQUE,
);

但是,我刚刚发现怪物也可能来自另一个星球,因此“Origin”列需要引用“Country”表或“Planet”表。

CREATE TABLE Planet (
   Id INTEGER PRIMARY KEY,
   Name VARCHAR NOT NULL UNIQUE,
);

请注意,我在一组不同的表中遇到了类似的情况,但通过使用虚拟列解决了。

CREATE MonsterElectricityPower (
    Id INTEGER PRIMARY KEY,
    Name VARCHAR NOT NULL UNIQUE
);

CREATE MonsterFreezePower (
    Id INTEGER PRIMARY KEY,
    Name VARCHAR NOT NULL UNIQUE
);

CREATE MonsterPowerParameter (
    Id INTEGER PRIMARY KEY,
    Level INTEGER,
    ParentCode TEXT,
    ParentName TEXT, 
    ParentCode$MonsterElectricityPower TEXT generated always as (case when ParentCode = 'MonsterElectricityPower' then ParentName else NULL end) virtual REFERENCES MonsterElectricityPower (Name) ON DELETE CASCADE, 
    ParentCode$MonsterFreezePower TEXT generated always as (case when ParentCode = 'MonsterFreezePower' then ParentName else NULL end) virtual REFERENCES MonsterFreezePower(Name) ON DELETE CASCADE
);

这里,ParentCode 指示需要引用哪些表/列,因此我可以相应地创建我的虚拟列。

但是,在上面的第一种情况下,我无法从 Monster 表中知道 Origin 列是否应该引用 Country 表或 Planet 表中的记录。

sqlite
1个回答
0
投票

我无法从 Monster 表中知道 Origin 列是否应该引用 Country 表或 Planet 表中的记录。

你会的。

考虑以下事项(根据您问题中的表格):-

INSERT INTO country (name) VALUES
    ('England'),('Australia'),('France'),('Spain'),('Germany'),('Pluto') /*<<<< PLUTO PURPOSEFULLY AMBIGUOUS */
;
INSERT INTO planet (name) VALUES
    ('Mars'),('Saturn'),('Jupiter'),('Venus'),('Pluto') /*<<<< AGAIN PLUTO PURPOSEFULLY AMBIGUOUS */
;
INSERT INTO Monster (name,origin) VALUES
    ('Monster 1','Australia')
    ,('Monster 2','Venus')
    ,('Monster 3','Pluto')
    , ('Monster from nowhere!!!!','Timbuktu')
;
SELECT 
    monster.*, 
    /* know whether planet, country,both (country and planet), or neither(nowhere) */
    CASE 
        WHEN origin IN (SELECT name FROM country) AND origin IN (SELECT name from planet) THEN 'country and planet'
        WHEN origin IN (SELECT name FROM country) THEN 'country' 
        WHEN origin IN (SELECT name FROM planet) THEN 'Planet' 
        ELSE 'nowhere' 
    END AS type
    /* get both together */
    ,coalesce((SELECT name FROM country AS c WHERE c.name=origin),'')||coalesce((SELECT name FROM planet AS p WHERE p.name=origin),'') AS combined
    /* get both individually */
    ,(SELECT name FROM country AS c WHERE c.name=origin) AS country_of_origin
    ,(SELECT name FROM planet AS p WHERE p.name=origin) AS planet_of_origin
FROM Monster;

这会导致:-

Result

因此可以确定是哪一个、两者都存在或都没有。

如果存在参考,也可以仅插入一个怪物,也许可以考虑以下尝试插入 4 个怪物的方法

  1. 龟背竹
  2. 怪物b 1.monsterx 跳过,因为原点 (
    not a country or planet
    ) 既不指向国家,也不指向行星
  3. 由于(可选)排除了不明确的起源(即冥王星)而跳过了怪物

按照:-

WITH 
    cte_insert_data(monster,origin) AS (
        SELECT 'monsterx','not a country or planet' 
        UNION SELECT 'monstera','England'
        UNION SELECT 'montserb','Venus'
        UNION SELECT 'monstery','Pluto'
    ),
    cte_to_insert_monster(monster,origin,in_country,in_planet) AS (
        SELECT 
            monster,
            origin,
            origin IN (SELECT name FROM country AS c WHERE c.name=origin), 
            origin IN (SELECT name FROM planet AS p WHERE p.name=origin)
        FROM cte_insert_data
    )
INSERT INTO monster (name,origin) 
    SELECT monster,origin 
    FROM cte_to_insert_monster 
    WHERE (in_country OR in_planet) 
        AND NOT (in_country AND in_planet) /* to exclude insertion of ambiguous (Pluto) if required */;
SELECT * FROM monster;

随后 SELECT 的结果是:-

enter image description here

  • CASCADE 是引用完整性内置处理的一部分,如果需要,必须予以满足。
  • 即由于排除标准,monsterx 和 monsterY 已被跳过。
    • monsterx 被排除,因为没有参考资料,
    • 由于冥王星既是一个国家又是一颗行星的模糊性,怪物被排除在外。
© www.soinside.com 2019 - 2024. All rights reserved.