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

时间:2015-06-11 20:09:59

标签: sql postgresql resultset

目前正在使用Postgres 9.3 我有一个表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

在我看来,我应该能够通过将其中一部分的结果与另一部分的结果相结合来进行声明。

有人能想到一个可以做到这一点的声明吗?还是更优雅的方法?

2 个答案:

答案 0 :(得分:2)

Classic SQL可以更好地使用静态模式。

仍然可以在你的情况下编写一个查询。

例如,您想要找到所有拥有以下内容的人:

FavorateColor = red
AND
Fname = Ron
AND
FavorateIcream = Chocolate

对每个属性执行三个单独的查询,并仅返回与所有三个过滤器匹配的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'
    )

所以,有可能,但是,我个人不会这样做。就像你在问题开头所描述的那样,我会为人,地址,电话,电子邮件分别设置表格。

答案 1 :(得分:1)

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

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'
;