Is it possible to name SQL result columns from rows in another table? (Postgres)

纵饮孤独 提交于 2019-12-25 07:01:47

问题


Basically I have users with a dynamic attributes table. Simplified:

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 
-----------+--------------+-------

What I'm looking to do is a SELECT that would return columns user_id, city, region where city & region are not empty.

The reason for the user_attributes table is one may want to store any number of custom fields about the user, and it's impossible to know beforehand what they will be to create them as columns of the user table.


回答1:


Use INNER JOIN for that:

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



回答2:


This is based on a principal misunderstanding of the inner workings of Postgres and EAV designs.

If you don't have hundreds of different fields or a dynamic set of attribute types, use a single table with all columns - except for database normalization. Columns without value are filled with NULL.
Null storage is very cheap.

  • 1 bit per column in the table for the null bitmap, typically allocated in units of 8 bytes which covers 64 columns.
  • A separate row for a single additional attribute occupies at least an additional 28 bytes.

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

    Typically more, due to padding and additional overhead.

There would have to be hundreds of different, sparsely populated columns before such an unwieldy EAV design could pay - and hstore or jsonb in Postgres 9.4 would be superior solutions for that. There is hardly any room in between for your design, and if there was, you'd probably be using an enum for the type.

At the same time, queries are more complicated and expensive. We're in a tight spot here.

Instead use a table layout like this:

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

Closely related answer with more advice:

  • SQL : Create a full record from 2 tables



回答3:


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'

This will show clients with a city or region value. If you want only clients with BOTH such attributes, in the having clause, change the OR to an AND.



来源:https://stackoverflow.com/questions/25704086/is-it-possible-to-name-sql-result-columns-from-rows-in-another-table-postgres

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!