2015-06-11 2 views
1

В настоящее время с помощью 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, [email protected] 
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 

Где я, Рон H, я из всех ID = 1 и если, скажем, я получил работу, которую вы могли бы добавить 1, WorkEmail, [email protected]

Так что, если я хочу, чтобы все атрибуты всех, кто 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 Рон

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 

На мой взгляд, я должен быть в состоянии сделать это в на утверждение, объединяя результаты от одной части того, где к результатам другой части.

Может ли кто-нибудь подумать о одном утверждении, которое может это сделать? Или более элегантный метод?

+1

Базы данных NoSQL в основном предназначены для этого.Возможно, вы захотите изучить один из них, если это общая проблема, с которой вы сталкиваетесь. SQL лучше всего работает с определенной схемой. – MartianCodeHound

+0

Я укажу, что это пример того, что я пытаюсь сделать. В моем реальном тесте у меня есть Лицо с базовым набором столбцов, таких как Имя и день рождения, и таблица personAttributes, где все остальные атрибуты, и я думаю, что у меня будет таблица AttributeLable для хранения всех уже определенных меток, чтобы я не например, глаз и глаз. –

+0

@MartianCodeHound Я бы хотел переключиться на один, я посмотрел на него, и вы на 100% прав, это путь, но у меня есть другие ограничения. Мне интересно узнать, как другие люди нарушили SQL, чтобы выполнить такую ​​работу. –

ответ

2

Классический SQL лучше работает со статической схемой.

Тем не менее, в вашем случае можно написать один запрос.

Например, вы хотите, чтобы найти все человек, которые имеют:

FavorateColor = red 
AND 
Fname = Ron 
AND 
FavorateIcream = Chocolate 

Есть три отдельных запросов для каждого атрибута и возвращать только те идентификаторы, которые соответствуют всем три фильтра:

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

Итак, это возможно, но я лично этого не сделаю. У меня были бы отдельные таблицы для людей, адресов, телефонов, писем, как описано в начале вашего вопроса.

+0

INTERSECT - это то, чего мне не хватало, спасибо. Может ли кто-нибудь говорить с [dis] преимуществами этого метода против hstore против нормального нормализации, как сказал Владимир, он предпочел бы? –

+0

Как выполняются различные параметры схемы? http://thebuild.com/presentations/pg-as-nosql-pgday-fosdem-2013.pdf –

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' 
; 
Смежные вопросы