多个外来的SQL问题

时间:2017-08-04 04:19:08

标签: mysql postgresql postgresql-9.6

问题:

我正在使用PostgreSQL数据库。

我有一个表(相册)要链接到另外两个表(客户端)。因此,如果您是客户 域,则可以拥有相册。但是在Albums表中,所有者只能处理单个外键。我该如何解决这个问题?

梦想:单个相册只能拥有(1)客户 域。需要修复外键问题。专辑:id |所有者(多个外国人 - >客户:id或域名:id) - >不能这样做名称。我只需要一些聪明的返工。

表格(现在可以只拥有相册域名):

  1. 相册
  2. 客户
  3. 专辑(具有外键的表格):

    id |所有者(外键 - >域名:id)|名称

    客户:

    id | first_name |姓氏

    域:

    id |老板|名称

2 个答案:

答案 0 :(得分:0)

添加2个FK列和CHECK约束,只强制其中一个是NOT NULL ...
像这样:

CREATE TABLE albums (
  id        serial PRIMARY KEY,
  client_id integer,
  domain_id integer,
  name      varchar(255) NOT NULL,
  FOREIGN KEY (client_id) REFERENCES clients(id),
  FOREIGN KEY (domain_id) REFERENCES domains(id),
  CHECK ((client_id IS NULL) <> (domain_id IS NULL))
);

要查询,您可以使用以下内容:

SELECT a.id, COALESCE(c.id, d.id) AS owner_id, COALESCE(c.name, d.name) AS owner_name,
       a.name AS title
FROM albums a
LEFT JOIN clients c ON a.client_id = c.id
LEFT JOIN domains d ON a.domain_id = d.id

@ e_i_pi的版本

CREATE TABLE entities (
  id        serial PRIMARY KEY,
  type      integer, -- could be any other type
  -- any other "common" values
);

CREATE TABLE client_entities (
  id        integer PRIMARY KEY, -- at INSERT this comes from table `entities`
  name      varchar(255) NOT NULL,
);

CREATE TABLE domain_entities (
  id        integer PRIMARY KEY, -- at INSERT this comes from table `entities`
  name      varchar(255) NOT NULL,
);

CREATE TABLE albums (
  id        serial PRIMARY KEY,
  owner_id  integer FOREIGN KEY REFERENCES entities(id), -- maybe NOT NULL?
  name      varchar(255) NOT NULL,
);

查询:

SELECT a.id, owner_id, COALESCE(c.name, d.name) AS owner_name, a.name AS title
FROM albums a
LEFT JOIN entities e ON a.owner_id = e.id
LEFT JOIN client_entities c ON e.id = c.id AND e.type = 1 -- depending on the type of `type`
LEFT JOIN domain_entities d ON e.id = d.id AND e.type = 2

答案 1 :(得分:0)

Righto,正如@UsagiMiyamoto对答案的评论所建议的那样,有一种方法可以通过级联来声明实体类型。请注意,此解决方案不支持无限制的实体类型,因为我们需要维护具体的FK约束。 是一种使用无限实体类型执行此操作的方法,但涉及触发器和相当多的肮脏。

以下是易于理解的解决方案:

-- Start with a test schema
DROP SCHEMA IF EXISTS "entityExample" CASCADE;
CREATE SCHEMA IF NOT EXISTS "entityExample";
SET SEARCH_PATH TO "entityExample";

-- We'll need this to enforce constraints
CREATE OR REPLACE FUNCTION is_entity_type(text, text) returns boolean as $$
SELECT TRUE WHERE $1 = $2
;
$$ language sql;

-- Unique entity types
CREATE TABLE "entityTypes" (
  name          TEXT NOT NULL,
  CONSTRAINT    "entityTypes_ukey" UNIQUE ("name")
);

-- Our client entities
CREATE TABLE clients (
  id                integer PRIMARY KEY,
  name              TEXT NOT NULL
);

-- Our domain entities
CREATE TABLE domains (
  id                integer PRIMARY KEY,
  name              TEXT NOT NULL
);

-- Our overaching entities table, which maintains FK constraints against clients and domains
CREATE TABLE entities (
  id                  serial PRIMARY KEY,
  "entityType"    TEXT NOT NULL,
  "clientID"      INTEGER CHECK (is_entity_type("entityType", 'client')),
  "domainID"      INTEGER CHECK (is_entity_type("entityType", 'domain')),
  CONSTRAINT      "entities_entityType" FOREIGN KEY ("entityType") REFERENCES "entityTypes" (name) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT      "entities_clientID" FOREIGN KEY ("clientID") REFERENCES "clients" (id) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT      "entities_domainID" FOREIGN KEY ("domainID") REFERENCES "domains" (id) ON DELETE CASCADE ON UPDATE CASCADE
);

-- Our albums table, which now can have one owner, but of a dynam ic entity type
CREATE TABLE albums (
  id                    serial PRIMARY KEY,
  "ownerEntityID"       integer,
  name                  TEXT NOT NULL,
  CONSTRAINT "albums_ownerEntityID" FOREIGN KEY ("ownerEntityID") REFERENCES "entities"("id")
);

-- Put the entity type in
INSERT INTO "entityTypes" ("name") VALUES ('client'), ('domain');

-- Enter our clients and domains
INSERT INTO clients VALUES (1, 'clientA'), (2, 'clientB');
INSERT INTO domains VALUES (50, 'domainA');

-- Make sure the clients and domains are registered as entities
INSERT INTO entities ("entityType", "clientID")
  SELECT
    'client',
    "clients".id
  FROM "clients"
ON CONFLICT DO NOTHING
;
INSERT INTO entities ("entityType", "domainID")
  SELECT
    'domain',
    "domains".id
  FROM "domains"
ON CONFLICT DO NOTHING
;

如果您不喜欢插入两次的想法(例如,一次在客户端,一次在entites中),您可以在clients表中插入一个触发器,或者替换地创建插入两者的插入函数桌子一下子。