有没有一种方法可以从另外两个表中引用另一个属性?

时间:2015-05-22 09:35:08

标签: sql database database-design

对不起这个标题,英语不是我的第一语言。 我想知道是否有办法构造我的表/基数,以便在执行某个作业时,job_location(JOB表)将是来自HOME表的home_address或来自WORK表的work_address而不仅仅是VARCHAR就像现在一样吗?

我不确定这是否有意义,我是新手,我试图看看我是否可以在不使用触发器之类的情况下完成它。

enter image description here

例如,考虑上面的图片,其中job_location可以是home_address或work_address,而不是两者而​​不是none。

1 个答案:

答案 0 :(得分:1)

如评论中所述,您应该有一个地址表。这就是原因。

许多数据建模人员在完成原始设计时,将对正在建模的真实世界对象进行人类语言描述,并制作名词和形容词列表。名词通常是实体,形容词通常是属性。所以,当你遇到"家庭住址"和#34;工作地址",你应该放置"地址"在名词列表和" home"和"工作"在形容词列表中。这意味着您应该有一个名为Addresses的表,其中一个字段的名称类似于" AddressType"将地址指定为家庭或工作。

因为同一个用户(或员工或其他)可以拥有一个家庭住址和/或一个工作地址,所以地址表如下所示:

create table Addresses(
    EmpID    int not null references Employees,
    AddrType char( 1 ) check( AddrType in( 'H', 'W' ),
    ...,     -- other address data
    constraint PK_Addresses primary key( EmpID, AddrType )
);

所以Jobs表看起来像这样:

create table Jobs(
    ID       int identity primary key,
    LocOwner int not null,
    LocType  char( 1 ),
    ...,     -- other job data
    constraint FK_Jobs_Location foreign key( LocOwner, LocType )
        references Addresses( EmpID, AddrType )
);

我已将地址表的PK显示为员工ID和类型(H或W)的组合。这允许同一个员工拥有两个地址,每种地址都有一个。这极大地简化了对与员工关联的所有地址的搜索。请注意,这是一个封闭的解决方案:作业元组中的地址数据必须引用地址表中具有正确类型名称的现有地址。

但是,您可能会遇到自己的设计。这很不幸,但仍然有一个解决方案,虽然不像首先设计得那么简单和直接。以下是它的外观和制作方法。

首先,创建一个地址类型表,就像上面地址表的前两列一样。但是,由于我的设计包含了员工ID,但是您的设计具有代理地址密钥,因此请坚持使用您的设计。

create table AddresseTypes(
    Addr_ID   int not null,
    Addr_Type char( 1 ) check( Addr_Type in( 'H', 'W' ),
    constraint PK_AddresseTypes primary key( Addr_ID, Addr_Type )
};

其次,每个地址表必须有一个单独的表,其类型如上所示。

create table HomeAddresses(
    Home_ID    int not null references Home( Home_ID ),
    Home_Type  char( 1 ) check( HomeType = 'H' )
);
create table WorkAddresses(
    Work_ID    int not null references Work( Work_ID ),
    Work_Type  char( 1 ) check( WorkType = 'W' )
);

这些表包含各自地址表中列出的所有地址,或者只包含与作业关联的地址。将这些表中的条目复制到AddresseTypes表中。然后:

alter table HomeAddresses add constraint FK_HomeAddressType
    foreign key( Home_ID, Home_Type )
    references AddressTypes( Addr_ID, AddrType );
alter table WorkAddresses add constraint FK_WorkAddressType
    foreign key( WorkID, WorkType )
    references AddressTypes( Addr_ID, AddrType );
alter table Jobs add constraint FK_JobAddress
    foreign key( Loc_ID, Loc_Type )
    references AddressTypes( Addr_ID, AddrType );

HomeAddresses或WorkAddresses中不存在任何条目,分别在HomeType或WorkTypes中分别为。 No Job条目可以引用AddressTypes中未列出的地址。

不幸的是,这不是一个封闭的解决方案:AddressTypes可能包含HomeAddress或WorkAddresses中找不到的虚假条目。这需要额外的维护工作,但这并不困难。

要查询作业并从包含它的表中获取地址,查询必须与Home表和Work表进行外连接。

select  j.*, 
        case t.Addr_Type  -- repeated for each address field
            when 'W' then w.Street
            else h.Street end as Street, 
        case t.Addr_Type
            when 'W' then w.City
            else h.City end as City,
        case <etc.>
from    Jobs j
join    AddressTypes t
    on  t.Addr_ID = j.Loc_ID
    and t.Addr_Type = j.Loc_Type
left join HomeAddresses ha
    on  ha.Home_ID = t.Addr_ID
    and ha.Home_Type = t.Addr_Type
left join WorkAddresses wa
    on  wa.Work_ID = t.Addr_ID
    and wa.Work_Type = t.Addr_Type
left join Home h
    on  h.Home_ID    = t.Addr_ID
left join Work w
    on  w.Work_ID    = t.Addr_ID;

如果地址数据显示为一系列NULL s,则表示AddressTypes中的条目是虚假的。轻松修复。由于Home和Work中都存在相同的地址id,因此case语句选择了正确的源。

显然,如果你可以自由改变设计,你会好得多。