SQL - 具有3个所有者表的外键一个表

时间:2016-05-02 00:59:22

标签: sql postgresql

我有下表Widget,它将拥有与每行相关联的所有者。

所有者可以是来自UserCompanyDepartment表格的ID。我猜测如何设置它是为了制作一个像这样的链接表?

id       | user     | company  | department
---------|----------|----------|----------
1        | 4        | NULL     | NULL
2        | 6        | 3        | 6
3        | 10       | 3        | 8

然后让Widget表使用该ID作为owner提供的逻辑在应用中,如果company is not null则所有者是公司,否则所有者将是用户。

如果没有公司,那么部门就不存在。

1 个答案:

答案 0 :(得分:2)

如果要在WIDGET表上分别从三个表(USER,COMPANY,DEPARTMENT)添加三个外键(FK)列,则不会出现问题。您可以使用下面描述的JOIN操作区分真正的所有者;

CREATE TABLE WIDGET (
    WIDGET_NAME VARCHAR(20),
    OWNER_USER_ID INTEGER REFERENCES USER(ID),
    OWNER_COMPANY_ID INTEGER REFERENCES COMPANY(ID),
    OWNER_DEPART_ID INTEGER REFERENCES DEPARTMENT(ID),
);
-- retrieve OWNER_USER (you can JOIN with the USER table)
SELECT OWNER_USER_ID, WIDGET_NAME FROM WIDGET WHERE OWNER_COMPANY_ID IS NULL;
-- retrieve OWNER_COMPANY (plus OWNER_DEPART) (you can JOIN with the COMPANY and DEPARTMENT table)
SELECT OWNER_COMPANY_ID, OWNER_DEPART_ID, WIDGET_NAME FROM WIDGET WHERE OWNER_COMPANY_ID IS NOT NULL;

如果你想从三个表中只添加一个PK列,理论上它没有意义,但你可以在一些额外条件下完成。你说WIDGET表中一个小部件的所有者是公司,如果公司不是null。但如果公司为空,那么所有者就是用户。如果WIDGET表中的用户(或相应的标识符)列始终不为null,无论公司(或相应的标识符)列是否为空,那么您只需将USER表的主键(PK)列作为WIDGET的单个FK获取表。为什么?用户→公司和用户→部门依赖关系由此条件生成。这意味着,如果你选择一个用户A,那么就没有更多的公司与他或她有关,而且用户和部门之间也是如此。

-- Schema of USER, COMPANY, DEPARTMENT table
CREATE TABLE USER (
    ID INTEGER PRIMARY KEY,
    NAME VARCHAR(20),
    COMPANY_ID INTEGER REFERENCES COMPANY(ID),
    DEPART_ID INTEGER REFERENCES DEPARTMENT(ID)
);

CREATE TABLE COMPANY (
    ID INTEGER PRIMARY KEY,
    NAME VARCHAR(20)
);

CREATE TABLE DEPARTMENT (
    ID INTEGER PRIMARY KEY,
    NAME VARCHAR(20)
);

-- Schema of WIDGET table
CREATE TABLE WIDGET (
    WIDGET_NAME VARCHAR(20),
    OWNER_ID INTEGER REFERENCES USER(ID)
);

-- retrieve OWNER_USER
SELECT U.NAME AS OWNER_USER_NAME, W.WIDGET_NAME
FROM   WIDGET W, USER U
WHERE  U.ID = W.OWNER_ID AND U.COMPANY_ID IS NULL;

-- retrieve OWNER_COMPANY
SELECT C.NAME AS OWNER_COMPANY_NAME, W.WIDGET_NAME
FROM   WIDGET W, USER U, COMPANY C
WHERE  U.ID = W.OWNER_ID AND U.COMPANY_ID = C.ID;

-- retrieve OWNER_DEPARTMENT
SELECT D.NAME AS OWNER_DEPART_NAME, W.WIDGET_NAME
FROM   WIDGET W, USER U, DEPARTMENT D
WHERE  U.ID = W.OWNER_ID AND U.COMPANY_ID IS NOT NULL AND U.DEPART_ID IS NOT NULL AND U.DEPART_ID = D.ID;

但是,如果WIDGET表中的用户列即使公司列不为空也可以为null,那么您将构建另一个OWNER表来保存您的所有者信息(USER,COMPANY,DEPARTMENT)。当然,WIDGET的每个记录必须是唯一的,因此可能需要复合唯一索引。 (见http://www.postgresql.org/docs/current/static/indexes-unique.html

-- Schema of OWNER table
CREATE TABLE OWNER (
    ID INTEGER PRIMARY KEY.
    OWNER_USER_ID INTEGER REFERENCES USER(ID),
    OWNER_COMPANY_ID INTEGER REFERENCES COMPANY(ID),
    OWNER_DEPARTMENT_ID INTEGER REFERENCES DEPARTMENT(ID)
);

-- unique index on OWNER
CREATE UNIQUE INDEX OWNER_UIDX ON OWNER( OWNER_USER_ID, OWNER_COMPANY_ID, OWNER_DEPARTMENT_ID );

-- Schema of WIDGET table
CREATE TABLE WIDGET (
    WIDGET_NAME VARCHAR(20),
    OWNER_ID INTEGER REFERENCES OWNER(ID)
);