那么这里是我的问题,我有三个表;地区,国家,州。国家可区里,状态可以是区域内。区域是食物链的顶部。

现在我添加具有两列的表popular_areas; REGION_ID和popular_place_id。是否有可能使popular_place_id是一个外键无论是国家 的状态。我可能将不得不增加一个popular_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约束来模拟多态关联。外键约束总是引用一个目标表。

多态关联是由框架,例如Rails和休眠支持。但他们明确地说,你必须禁用SQL约束来使用此功能。相反,应用程序或框架必须做同等工作,以确保引用是满意的。即,在该外键的值是存在于可能的目标表中的一个。

多态关联是弱相对于执行数据库的一致性。数据完整性取决于访问与所述相同的参照完整性逻辑数据库执行的所有客户端,并且还执行必须是无缺陷。

下面是一些可替代的解决方案,做利用数据库强制参照完整性:

创建一个目标一个额外的表例如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),
);

这并不意味着让所有的用户流行最喜欢的地方,你需要查询这两个表。但它意味着你可以依赖数据库执行一致性。

创建places表作为超表如图艾比提到,第二备选方案是您的流行的地方引用表等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)
);

使用两列。可引用以下两种目标表,使用两列相反的一列。这两列可以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)
);

在关系理论而言,多态关联违反第一范式时,因为popular_place_id是在有两个方面的含义影响列:它或者是一个国家或一个国家。你不会存储个人的age及其phone_number在单个列中,出于同样的原因,你都应该state_idcountry_id不存储在单个列。这两个属性具有兼容的数据类型事实是巧合;他们仍然表示不同的逻辑实体。

多态关联也违反第三范式,因为列的含义取决于额外这名表到外键引用列。在第三范式,在一个表中的属性必须在表的主键仅依赖


这@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的各行中找到的值动态选择。

唯一的其他选择是加入到所有的过滤表使用外部联接。那些没有匹配的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)
...

但是,这种格式仍然要求你写的所有表引用。有没有得到解决的。

其他提示

下面是校正比尔Karwin的“超表”的方式,使用化合物键( place_type, place_id )解决感知正常形式侵犯:

CREATE TABLE places (
  place_id INT NOT NULL UNIQUE,
  place_type VARCHAR(10) NOT NULL
     CHECK ( place_type = 'state', 'country' ),
  UNIQUE ( place_type, place_id )
);

CREATE TABLE states (
  place_id INT NOT NULL UNIQUE,
  place_type VARCHAR(10) DEFAULT 'state' NOT NULL
     CHECK ( place_type = 'state' ),
  FOREIGN KEY ( place_type, place_id ) 
     REFERENCES places ( place_type, place_id )
  -- attributes specific to states go here
);

CREATE TABLE countries (
  place_id INT NOT NULL UNIQUE,
  place_type VARCHAR(10) DEFAULT 'country' NOT NULL
     CHECK ( place_type = 'country' ),
  FOREIGN KEY ( place_type, place_id ) 
     REFERENCES places ( place_type, place_id )
  -- attributes specific to country go here
);

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

什么这样的设计不能确保在places那里statescountries(但不能同时)存在行的每一行。这是SQL中的外键的限制。在一个完整的SQL-92符合标准的数据库管理系统,你可以缓征定义表间的约束,它会让你达到相同的,但它是笨重,涉及到交易,这样的数据库管理系统还没有做它对市场。

我知道这线程是老了,但我看到了这一点,一个解决方案来考虑,我想我会扔在那里。

区域,国家和国家是生活在一个层次地理位置。

您可以通过创建一个名为geographical_location_type域表,你将有三排(地区,国家,州)填充完全避免你的问题。

其次,代替三个位置的表,创建具有geographical_location_type_id的外键(所以你知道,如果该实例是一个地区,国家或国家)的单个geographical_location表。

模型的,这样一个国家实例中包含的Fkey其父国家实例,它反过来持有的Fkey其父地区的实例使该表自引用的层次结构。区域情况将在FKEY持有空。这不是比你会与三个表做不同的(你会1 - 区域和国家与国家和国家之间的多对多关系)。除了现在这一切都在一个表

在popular_user_location表将是用户和georgraphical_location之间的范围解析表(这么多用户可能喜欢的许多地方)。

... SOOOO

“在这里输入的图像描述”

CREATE TABLE [geographical_location_type] (
    [geographical_location_type_id] INTEGER NOT NULL,
    [name] VARCHAR(25) NOT NULL,
    CONSTRAINT [PK_geographical_location_type] PRIMARY KEY ([geographical_location_type_id])
)

-- Add 'Region', 'Country' and 'State' instances to the above table


CREATE TABLE [geographical_location] (
   [geographical_location_id] BIGINT IDENTITY(0,1) NOT NULL,
    [name] VARCHAR(1024) NOT NULL,
    [geographical_location_type_id] INTEGER NOT NULL,
    [geographical_location_parent] BIGINT,  -- self referencing; can be null for top-level instances
    CONSTRAINT [PK_geographical_location] PRIMARY KEY ([geographical_location_id])
)

CREATE TABLE [user] (
    [user_id] BIGINT NOT NULL,
    [login_id] VARCHAR(30) NOT NULL,
    [password] VARCHAR(512) NOT NULL,
    CONSTRAINT [PK_user] PRIMARY KEY ([user_id])
)


CREATE TABLE [popular_user_location] (
    [popular_user_location_id] BIGINT NOT NULL,
    [user_id] BIGINT NOT NULL,
    [geographical_location_id] BIGINT NOT NULL,
    CONSTRAINT [PK_popular_user_location] PRIMARY KEY ([popular_user_location_id])
)

ALTER TABLE [geographical_location] ADD CONSTRAINT [geographical_location_type_geographical_location] 
    FOREIGN KEY ([geographical_location_type_id]) REFERENCES [geographical_location_type] ([geographical_location_type_id])



ALTER TABLE [geographical_location] ADD CONSTRAINT [geographical_location_geographical_location] 
    FOREIGN KEY ([geographical_location_parent]) REFERENCES [geographical_location] ([geographical_location_id])



ALTER TABLE [popular_user_location] ADD CONSTRAINT [user_popular_user_location] 
    FOREIGN KEY ([user_id]) REFERENCES [user] ([user_id])



ALTER TABLE [popular_user_location] ADD CONSTRAINT [geographical_location_popular_user_location] 
    FOREIGN KEY ([geographical_location_id]) REFERENCES [geographical_location] ([geographical_location_id])

不知道什么目标DB是;以上是MS SQL服务器。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top