postgresql 如何建立多层次的关系?(一列中有多个表关系)

sigwle7e  于 2023-08-04  发布在  PostgreSQL
关注(0)|答案(2)|浏览(146)

我有一个包含四个表的PostgreSQL数据库:用户、国家、城市和街道。用户表包含有关用户的信息,包括他们的姓名、级别(可以是“国家”、“城市”或“街道”)以及与他们所属的“国家”、“城市”和“街道”的ID相对应的id。
表格结构:

CREATE TABLE "user" (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  level VARCHAR(10) NOT NULL, --ENUM('Country', 'City', 'Street')
  id_country INT NULL,
  id_city INT NULL,
  id_street INT NULL,
  FOREIGN KEY (id_country) REFERENCES Country(id) ON UPDATE CASCADE ON DELETE CASCADE,
  FOREIGN KEY (id_city) REFERENCES City(id) ON UPDATE CASCADE ON DELETE CASCADE,
  FOREIGN KEY (id_street) REFERENCES Street(id) ON UPDATE CASCADE ON DELETE CASCADE
);

x

CREATE TABLE Country (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  code INT NOT NULL
);
CREATE TABLE City (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  code INT NOT NULL,
  id_country INT NOT NULL,
  FOREIGN KEY (id_country) REFERENCES Country(id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE Street (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  code INT NOT NULL,
  id_city INT NOT NULL,
  FOREIGN KEY (id_city) REFERENCES City(id) ON UPDATE CASCADE ON DELETE CASCADE
);

的数据
我想在User表和其他三个表之间建立适当的关系,这样我就可以根据用户的级别直接访问相应的“Country”、“City”或“Street”。我目前的方法包括为每种情况添加三个单独的列(id_country、id_city、id_street)和三个连接操作,这似乎是重复的,不是理想的设计。
我的用例的一个例子是:

switch (user.level) {
  case "Country":
    query = ' SELECT * from "user" AS u
              INNER JOIN country c ON u.id_country = c.id '
    break;

  case "City":
    query = ' SELECT * from "user" AS u
              INNER JOIN city c ON u.id_city = c.id '
    break;

  case "Street":
    query = ' SELECT * from "user" AS u
              INNER JOIN street s ON u.id_street = s.id '
    break;
}


是否有一种更有效、更简洁的方法来在User表中表示这些关系,同时保持数据完整性并遵守标准数据库设计原则?
我想到了使用这样的复合外键

CREATE TABLE "user" (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  location_id INT NOT NULL,
  location_type VARCHAR(10) NOT NULL, --ENUM('Country', 'City', 'Street')
  FOREIGN KEY (location_id, location_type) REFERENCES Country(id, 'Country' )
    ON UPDATE CASCADE
    ON DELETE CASCADE,
  FOREIGN KEY (location_id, location_type) REFERENCES City(id, 'City' )
    ON UPDATE CASCADE
    ON DELETE CASCADE,
  FOREIGN KEY (location_id, location_type) REFERENCES Street(id, 'Street' )
    ON UPDATE CASCADE
    ON DELETE CASCADE
);


但是,SQL不接受静态值作为引用。我没有SQL的高级知识。
如有任何改进数据库设计的建议或替代办法,将不胜感激。谢谢你,谢谢

xtupzzrd

xtupzzrd1#

你可以选择这样做:

CREATE TABLE "user" (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  geo_entity_id INT NOT NULL,
  FOREIGN KEY (geo_entity_id) REFERENCES geo_entity(id) ON UPDATE CASCADE ON DELETE RESTRICT
);

CREATE TABLE geo_entity (
  id INT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  code INT NOT NULL,
  level VARCHAR(10) NOT NULL, --ENUM('Country', 'City', 'Street')
  parent_id INT NULL,
  FOREIGN KEY (parent_id) REFERENCES geo_entity(id) ON UPDATE CASCADE ON DELETE CASCADE
);

字符串
因此,user有一个简单的外键,指向一个名为geo_entity的东西-它适用于任何类型的实体:国家、城市或街道,视情况而定。geo_entity行可以有另一个geo_entity行作为其父行。
如果将user加入geo_entity,并查看geo_entity中的“level”设置,则会告诉您这是用户的级别。
如果有帮助,您还可以基于geo_entity创建国家、城市和街道视图。

06odsfpq

06odsfpq2#

您的3个表(国家,城市,街道)设计正确。但是要小心FK中的CASCADE模式,并在PK中添加IDENTITY...
您的主表设计不正确,应按以下方式创建:

CREATE TABLE "user" (
  id INT PRIMARY KEY IDENTITY,
  name VARCHAR(255) NOT NULL,
  level VARCHAR(10) NOT NULL, 
  id_street INT NULL,
  FOREIGN KEY (id_street) REFERENCES Street(id) 
);

字符串
然后添加一个视图以完成:

CREATE VIEW user_address
AS
SELECT u.id AS user_id,
       u.name AS user_name,   
       u.level,
       s.name AS street_name,
       ct.name AS city_name,
       cr.name AS country_name
FROM   "user" as u
       LEFT OUTER JOIN Street AS s ON u.street_id = s.id
          LEFT OUTER JOIN City AS ct ON s.city_id = ct.id
             LEFT OUTER JOIN Country AS cr ON ct.country_id = cr.id;

相关问题