我的问题是我有三张桌子;地区、国家、州.国家可以在区域内,国家可以在区域内.地区是食物链的顶端.

现在我添加了一个流行的带有两列的_areas表;region_id和popular_place_id.是否可以使popular_place_id成为任何一个国家OR个州的外键.我可能需要添加一个流行的_place _type列来确定id是否以任何方式描述一个国家或州.

推荐答案

你所描述的是多态关联.也就是说,"外键"列包含的id值必须存在于一组目标表中.通常,目标表以某种方式相互关联,例如作为某些公共超类数据的实例.在外键列旁边还需要另一列,以便在每一行上指定引用哪个目标表.

CREATE TABLE popular_places (
  user_id INT NOT NULL,
  place_id INT NOT NULL,
  place_type VARCHAR(10) -- either 'states' or 'countries'
  -- foreign key is not possible
);

无法使用SQL约束对多态关联进行建模.外键约束始终引用one个目标表.

Rails和Hibernate等框架支持多态关联.但他们明确表示,必须禁用SQL约束才能使用此功能.相反,应用程序或框架必须做同等的工作,以确保满足引用.也就是说,外键中的值出现在一个可能的目标表中.

多态关联在增强数据库一致性方面很弱.数据完整性取决于所有访问数据库的客户机都使用相同的引用完整性逻辑,而且执行必须没有bug.

以下是一些利用数据库强制引用完整性的替代解决方案:

Create one extra table per target.例如popular_statespopular_countries,其分别参考statescountries.这些"流行"表格中的每一个都引用了用户的个人资料.

CREATE TABLE popular_states (
  state_id INT NOT NULL,
  user_id  INT NOT NULL,
  PRIMARY KEY(state_id, user_id),
  FOREIGN KEY (state_id) REFERENCES states(state_id),
  FOREIGN KEY (user_id) REFERENCES users(user_id),
);

CREATE TABLE popular_countries (
  country_id INT NOT NULL,
  user_id    INT NOT NULL,
  PRIMARY KEY(country_id, user_id),
  FOREIGN KEY (country_id) REFERENCES countries(country_id),
  FOREIGN KEY (user_id) REFERENCES users(user_id),
);

这意味着,要获得用户最喜欢的所有位置,需要同时查询这两个表.但这意味着你可以依靠数据库来加强一致性.

Create a 100 table as a supertable.正如Abie提到的,第二种 Select 是,你的热门位置引用一个表,比如places,它是statescountries的父级.也就是说,州和国家都有一个外键places(你甚至可以让这个外键也成为statescountries的主键).

CREATE TABLE popular_areas (
  user_id INT NOT NULL,
  place_id INT NOT NULL,
  PRIMARY KEY (user_id, place_id),
  FOREIGN KEY (place_id) REFERENCES places(place_id)
);

CREATE TABLE states (
  state_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (state_id) REFERENCES places(place_id)
);

CREATE TABLE countries (
  country_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (country_id) REFERENCES places(place_id)
);

Use two columns.使用两列,而不是一列引用两个目标表中的任何一个.这两列可能是NULL;事实上,其中只有一个应该是非NULL.

CREATE TABLE popular_areas (
  place_id SERIAL PRIMARY KEY,
  user_id INT NOT NULL,
  state_id INT,
  country_id INT,
  CONSTRAINT UNIQUE (user_id, state_id, country_id), -- UNIQUE permits NULLs
  CONSTRAINT CHECK (state_id IS NOT NULL OR country_id IS NOT NULL),
  FOREIGN KEY (state_id) REFERENCES places(place_id),
  FOREIGN KEY (country_id) REFERENCES places(place_id)
);

根据关系理论,多态关联违反了First Normal Form,因为popular_place_id实际上是一个具有两种含义的列:它要么是一个州,要么是一个国家.你不会在一列中存储一个人的agephone_number,同样的原因你也不应该在一列中同时存储state_idcountry_id.这两个属性具有兼容的数据类型这一事实是巧合;它们仍然表示不同的逻辑实体.

多态关联也违反了Third Normal Form,因为列的含义取决于外键引用的表的额外列名称.在第三范式中,表中的属性必须仅依赖于该表的主键.


@SavasVedova回复:

我不确定在没有看到表定义或示例查询的情况下是否遵循了您的描述,但听起来好像您有多个Filters个表,每个表都包含一个引用中心Products表的外键.

CREATE TABLE Products (
  product_id INT PRIMARY KEY
);

CREATE TABLE FiltersType1 (
  filter_id INT PRIMARY KEY,
  product_id INT NOT NULL,
  FOREIGN KEY (product_id) REFERENCES Products(product_id)
);

CREATE TABLE FiltersType2 (
  filter_id INT  PRIMARY KEY,
  product_id INT NOT NULL,
  FOREIGN KEY (product_id) REFERENCES Products(product_id)
);

...and other filter tables...

如果您知道要加入哪种类型的过滤器,那么将产品加入特定类型的过滤器很容易:

SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)

如果希望过滤器类型是动态的,则必须编写应用程序代码来构造SQL查询.SQL要求在编写查询时指定并修复表.不能根据Products行中的值动态 Select 联接表.

唯一的另一个选项是使用外部联接连接到all个筛选表.那些没有匹配product_id的将作为一行空值返回.但是,您仍然必须对连接的表进行硬编码,如果添加新的筛选表,则必须更新代码.

SELECT * FROM Products
LEFT OUTER JOIN FiltersType1 USING (product_id)
LEFT OUTER JOIN FiltersType2 USING (product_id)
LEFT OUTER JOIN FiltersType3 USING (product_id)
...

连接到所有筛选表的另一种方法是按顺序进行:

SELECT * FROM Product
INNER JOIN FiltersType1 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType3 USING (product_id)
...

但这种格式仍然要求您写入对所有表的引用.这是绕不过go 的.

Mysql相关问答推荐

MySQL在带有特定属性值上的对象的数组的杨森对象中搜索

try 使用TypeORM创建新表时出现SQL语法错误

我可以指示MariaDB-Install-db和MariaDB忽略配置中的用户设置吗?

Python MySQL-无法从数据库中删除行

在时间戳上搜索大的MySQL表很慢

在两个日期之间生成每个月的1行数据.

java.lang.NullPointerException:无法调用com.proj.my.repository.OrderRepository.save(Object),因为this.orderRepository为空

0000-00-00 00:00:00 表中的日期设置为 NOT NULL

将 Go var 传递给 MySQL - try 在 Go lang 程序 (MySQL db) 中执行 db.Exec 时出错

使用数据表的直方图(SQL 查询)

由于长时间操作而断开连接后,我的 sql 查询是否继续执行?

估计行数 SQL

结果差异(MySQL 5.7 vs MySQL 8.0)

使用 mysql 处理非常大的数据

外键可以引用非唯一索引吗?

在 Yii2 中执行原始 SQL 查询?

MySQL获取两个值之间的随机值

MySQL:切换 int 字段值的简单方法

带有 LIKE 运算符的 Select 语句中的 MySQL case

为什么 GRANT 在 MySQL 中不起作用?