如果一列中的值相同,则将三行连接起来

时间:2018-11-23 09:59:48

标签: sql database postgresql

有一个Postgres数据库,该表有三列。数据结构在外部系统中,所以我无法对其进行修改。

每个对象都由三行表示(由element_id列标识-此列中具有相同值的行表示同一对象),例如:

key     value            element_id
-----------------------------------
status  active           1
name    exampleNameAAA   1
city    exampleCityAAA   1
status  inactive         2
name    exampleNameBBB   2
city    exampleCityBBB   2
status  inactive         3
name    exampleNameCCC   3
city    exampleCityCCC   3

我想获取描述每个对象(名称,状态和城市)的所有值。

对于此示例,输出应类似于:

exampleNameAAA   | active    | exampleCityAAA
exampleNameBBB   | inactive  | exampleCityBBB
exampleNameCCC   | inactive  | exampleCityCCC   

我知道如何合并两行:

select a.value as name,
       b.value as status
from the_table a 
  join the_table b 
    on a.element_id = b.element_id 
   and b."key" = 'status'
where a."key" = 'name';

如何连接三列?

3 个答案:

答案 0 :(得分:0)

您可以在下面尝试

DEMO

select a.value as name,
       b.value as status,c.value as city
from t1 a 
  join t1 b 
    on a.element_id = b.element_id and b."keys" = 'status'
    join t1 c on a.element_id = c.element_id and c."keys" = 'city'
where a."keys" = 'name';

输出

name            status      city
exampleNameAAA  active      exampleCityAAA
exampleNameBBB  inactive    exampleCityBBB
exampleNameCCC  inactive    exampleCityCCC

答案 1 :(得分:0)

一种选择是简单地为所需的每个值添加另一个联接(这是您使用的EAV (anti) pattern的主要缺点之一:

select a.value as name,
       b.value as status, 
       c.value as city
from the_table a 
  join the_table b on a.element_id = b.element_id and b."key" = 'status'
  join the_table c on a.element_id = c.element_id and c."key" = 'city'
where a."key" = 'name';

另一种选择是将元素的所有键/值对聚合为JSON,然后您可以轻松访问每个键/值对,而无需其他联接:

select t.element_id, 
       t.obj ->> 'city' as city, 
       t.obj ->> 'status' as status,
       t.obj ->> 'name' as name
from (
  select e.element_id, jsonb_object_agg("key", value) as obj
  from element e
  group by e.element_id
) t;

如果表真的很大,由于聚合步骤,这可能会比联接版本慢很多。如果您将查询限制为仅某些元素(例如,通过添加where element_id = 1where element_id in (1,2,3)),那么查询应该很快。

它的优点是,无论您做什么,都始终为每个element_id提供所有键/值对。内部选择可以放在视图中,以使事情变得容易。

在线示例:https://rextester.com/MSZOWU37182

答案 2 :(得分:0)

好像想要PIVOT

一种方法是通过条件聚合。

select 
 -- t.element_id,
 max(case when t.key = 'name' then t.value end) as name,
 max(case when t.key = 'status' then t.value end) as status,
 max(case when t.key = 'city' then t.value end) as city
from the_table t
group by t.element_id;

db <>提琴here

或使用交叉表:

select 
-- element_id,
name, 
status, 
city
from crosstab (
   'select t.element_id, t.key, t.value 
   from the_table t'
) as ct (element_id int, name varchar(30), status varchar(30), city varchar(30));

但是,如果您喜欢这些联接,这是一种方法

select 
-- el.element_id,
nm.value as name,
st.value as status,
ci.value as city
from 
(
  select distinct t.element_id 
  from the_table t
  where t.key in ('name','status','city')
) as el
left join the_table as nm on (nm.element_id = el.element_id and nm.key = 'name')
left join the_table as st on (st.element_id = el.element_id and st.key = 'status')
left join the_table as ci on (ci.element_id = el.element_id and ci.key = 'city');