是否可以从另一个表中的行命名SQL结果列? (Postgres的)

时间:2014-09-06 19:42:43

标签: sql postgresql database-design entity-attribute-value

基本上我有用户使用动态属性表。简化为:

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表的原因可能是想要存储关于用户的任意数量的自定义字段,并且事先不可能知道将它们创建为用户表的列。

3 个答案:

答案 0 :(得分:2)

这是基于对Postgres和EAV designs的内部运作的主要误解

如果您没有数百个不同的字段或动态的属性类型集,请使用包含所有列的单个表 - database normalization除外。没有值的列填充NULL Null storage is very cheap.

    表中每列的
  • 1位为空位图,通常以8个字节为单位分配,覆盖64列。
  • 单个附加属性的单独行占用至少额外 28个字节

    4  bytes item pointer
    23 bytes heap tuple header
    1  byte  padding
    

    通常更多,由于填充和额外的开销。

在这种笨重的EAV设计可以支付之前,必须有数百个不同的,人口稀少的列 - 而Postgres 9.4中的hstorejsonb将是的优秀解决方案。您的设计之间几乎没有任何空间,如果,您可能会使用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