基本上我有用户使用动态属性表。简化为:
SELECT * FROM users;
id | email
-----+------------------------------------------
1 | example@example.com
SELECT * FROM user_attributes;
id | name
----+----------------
1 | Salutation
2 | Given Name
3 | Surname
4 | Alias
5 | Address
6 | Address 2
7 | Address 3
8 | City
9 | Region
....
SELECT * FROM user_attribute_values;
client_id | attribute_id | value
-----------+--------------+-------
我要做的是一个SELECT,它会返回列user_id,city,region,city&地区不是空的。
user_attributes表的原因可能是想要存储关于用户的任意数量的自定义字段,并且事先不可能知道将它们创建为用户表的列。
答案 0 :(得分:2)
这是基于对Postgres和EAV designs的内部运作的主要误解。
如果您没有数百个不同的字段或动态的属性类型集,请使用包含所有列的单个表 - database normalization除外。没有值的列填充NULL
Null storage is very cheap.
单个附加属性的单独行占用至少额外 28个字节。
4 bytes item pointer
23 bytes heap tuple header
1 byte padding
通常更多,由于填充和额外的开销。
在这种笨重的EAV设计可以支付之前,必须有数百个不同的,人口稀少的列 - 而Postgres 9.4中的hstore
或jsonb
将是的优秀解决方案。您的设计之间几乎没有任何空间,如果,您可能会使用enum
作为类型。
同时,查询更复杂,更昂贵。我们在这里很紧张。
而是使用像这样的表格布局:
CREATE TABLE users (
users_id serial PRIMARY KEY
, salutation text
, given_name text
, surname text
, alias text
... (many) more columns
);
CREATE TABLE address (
address_id serial PRIMARY KEY
, users_id int REFERENCES users
, city text -- or separate TABLE city incl region_id etc. ...
, region_id int REFERENCES region
, address text
... (many) more columns
);
与更多建议密切相关的答案:
答案 1 :(得分:0)
select client_id,
min(case when attribute_id = 8 then value else '0' end) as city,
min(case when attribute_id = 9 then value else '0' end) as region
from user_attribute_values
group by clientid
having min(case when attribute_id = 8 then value else '0' end) <> '0'
or min(case when attribute_id = 9 then value else '0' end) <> '0'
这将向客户显示城市或地区价值。如果只希望客户端具有这样的属性,请在having子句中将OR更改为AND。
答案 2 :(得分:0)
使用INNER JOIN
:
SELECT u.id, a_city.value AS city, a_region.value AS region
FROM users u
INNER JOIN user_attribute_values a_city ON a_city.client_id = u.id AND a_city.attribute_id = 8
INNER JOIN user_attribute_values a_region ON a_region.client_id = u.id AND a_region.attribute_id = 9
WHERE LENGTH(a_city.value) > 0
AND LENGTH(a_region.value) > 0