存储未知数量属性的更好方法

A better way to store unknown number of attributes

目前正在使用 Postgres 9.3 我有一个 Table Person(Id, FName, Lname, Address1, Adress2, phone1, phone1,....)

我可以Person(id, FName, Lname) 然后 Address(PersonID, AddressName, Address)Pone(PersonID, PhoneName, Number)

但是当我需要添加新属性时,比如电子邮件,我需要更改架构并添加 Email(PersonID, EmailName, Address)

我想做的是Person(ID, AtrbLbl, AtribVal)

1, Fname, Ron
1, Lname, H
1, HomeEmal, rh@home.ca
1, HomeAddress, 123 st edmonton
2, LName, Smith
3, Fname, Bob
2, Fname, Sam
3, Lnaem, Marly
3, HomeAdress, Heven
2, HomeAddress, abc St.
1, FavorateColor, red
2, FavorateColor, red
3, FavorateColor, red
1, FavorateIcream, Chocolate 
2, FavorateIcream, Vanila
3, FavorateIcream, Mint
4, FName, tom
4, FavorateColor, blue

我 Ron H 由所有 id = 1 组成,如果说我找到了工作,你可以添加 1, WorkEmail, rh@Work.ca

所以如果我想要 FavorateColor 为红色的每个人的所有属性

Select * from person where id in (Select ID from person where  AtrbLbl = FavorateColor and AtribVal = red)`

我的问题是搜索多个属性。 在 sudo sql 我想要的是

Select * from person where id in (Select id from person where (AtrbLbl = FavorateColor and AtribVal = red) AND (AtrbLbl = Fname and AtribVal = Ron)

显然那行不通。

我想做的是

insert into temptbl
Select Count(id) cnt, ID from person where (AtrbLbl = FavorateColor and AtribVal = red) OR (AtrbLbl = Fname and AtribVal = Ron) 

Select * From person where id in (select id from temtbl where cnt = 2)  order by id
where 2 is the number of searched attributes.

所以如果我想要那些喜欢红色、巧克力和 FName Ron 的人

insert into temptbl
Select Count(id) cnt, ID from person where (AtrbLbl = FavorateColor and AtribVal = red) OR (AtrbLbl = Fname and AtribVal = Ron) OR (AtrbLbl = FavorateIcream and AtribVal = Chocolate) 

Select * From person where id in (select id from temtbl where cnt = 3)  order by id

在我看来,我应该能够通过将 where 的一部分的结果与另一部分的结果相结合来在 on 语句中做到这一点。

谁能想出一条语句来做到这一点?或者更优雅的方法?

实体-属性-值方法可能适用于这种情况。更多信息在这里:

http://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model

这是一个简化的例子。

drop schema example;

create schema example;

use example;

create table attribute_type (
    type_code varchar(16) primary key
);

create table person (
    person_id int primary key,
    person_name varchar(64)
);

create table person_attribute_value (
    person_id int references person(person_id),
    attribute_type varchar(16) references attribute_type(type_code),
    string_value varchar(64)
);

insert into attribute_type values ('phone');
insert into attribute_type values ('email');
insert into attribute_type values ('snail_mail_addr1');
insert into attribute_type values ('snail_mail_addr2');
insert into attribute_type values ('snail_mail_city');
insert into attribute_type values ('snail_mail_state');
insert into attribute_type values ('snail_mail_zip');

insert into person values (1, 'Larry');
insert into person values (2, 'Moe');
insert into person values (3, 'Curly');

insert into person_attribute_value values(1, 'phone', '(860)555-1234');
insert into person_attribute_value values(2, 'phone', '(860)555-1234');
insert into person_attribute_value values(3, 'phone', '(860)555-1234');
insert into person_attribute_value values(2, 'snail_mail_addr1', '123 Evergreen Terrace');
insert into person_attribute_value values(2, 'snail_mail_city', 'Springfield');
insert into person_attribute_value values(2, 'snail_mail_state', 'MA');

select
    person.*,
    phone.string_value phone,
    addr1.string_value addr1,
    addr2.string_value addr2,
    city.string_value city,
    state.string_value state
from
    person
    left outer join person_attribute_value phone on person.person_id = phone.person_id and phone.attribute_type = 'phone'
    left outer join person_attribute_value addr1 on person.person_id = addr1.person_id and addr1.attribute_type = 'snail_mail_addr1'
    left outer join person_attribute_value addr2 on person.person_id = addr2.person_id and addr2.attribute_type = 'snail_mail_addr2'
    left outer join person_attribute_value city on person.person_id = city.person_id and city.attribute_type = 'snail_mail_city'
    left outer join person_attribute_value state on person.person_id = state.person_id and state.attribute_type = 'snail_mail_state'
;

经典 SQL 使用静态模式效果更好。

不过,您可以编写单个查询。

例如,您要查找所有具有以下特征的人:

FavorateColor = red
AND
Fname = Ron
AND
FavorateIcream = Chocolate

对每个属性执行三个单独的查询,并且 return 仅匹配所有三个过滤器的 ID:

SELECT *
FROM PersonDetails
WHERE PersonID IN
    (
        SELECT ID
        FROM person
        WHERE AtrbLbl = 'FavorateColor' AND AtribVal = 'red'

        INTERSECT

        SELECT ID
        FROM person
        WHERE AtrbLbl = 'Fname' AND AtribVal = 'Ron'

        INTERSECT

        SELECT ID
        FROM person
        WHERE AtrbLbl = 'FavorateIcream' AND AtribVal = 'Chocolate'
    )

所以,这是可能的,但我个人不会这样做。正如您在问题开头所描述的那样,我会为人员、地址、电话、电子邮件设置单独的表格。