Oracle:如何对具有某些密钥的动态基数的EAV表进行透视?

时间:2019-07-24 06:46:58

标签: sql oracle pivot entity-attribute-value

我在Oracle中具有以下实体-属性-值(EAV)表:

| ID |     Key     |    Value     |
|----|-------------|--------------|
|  1 | phone_num_1 | 111-111-1111 |
|  1 | phone_num_2 | 222-222-2222 |
|  1 | contact_1   | friend       |
|  1 | contact_2   | family       |
|  1 | first_name  | mike         |
|  1 | last_name   | smith        |
|  2 | phone_num_1 | 333-333-3333 |
|  2 | phone_num_2 | 444-444-4444 |
|  2 | contact_1   | family       |
|  2 | contact_2   | friend       |
|  2 | first_name  | john         |
|  2 | last_name   | adams        |
|  3 | phone_num_1 | 555-555-5555 |
|  3 | phone_num_2 | 666-666-6666 |
|  3 | phone_num_3 | 777-777-7777 |
|  3 | contact_1   | work         |
|  3 | contact_2   | family       |
|  3 | contact_3   | friend       |
|  3 | first_name  | mona         |
|  3 | last_name   | lisa         |

请注意,某些键已索引,因此与其他索引键有关联。例如,phone_num_1将与contact_1关联。

注意:索引数量没有硬性限制。可以有10、20甚至50个phone_num_ *,但是可以保证每个phone_num_N都有一个对应的contact_N

这是我想要的结果:

| ID |  Phone_Num   | Contact | First_Name | Last_Name |
|----|--------------|---------|------------|-----------|
|  1 | 111-111-1111 | friend  | mike       | smith     |
|  1 | 222-222-2222 | family  | mike       | smith     |
|  2 | 333-333-3333 | family  | john       | adams     |
|  2 | 444-444-4444 | friend  | john       | adams     |
|  3 | 555-555-5555 | work    | mona       | lisa      |
|  3 | 666-666-6666 | family  | mona       | lisa      |
|  3 | 777-777-7777 | friend  | mona       | lisa      |

我尝试过/看过什么:

我研究了Oracle的枢纽功能;但是,我不相信这可以解决我的问题,因为我没有固定数量的属性要使用。 我看了这些帖子: SQL Query to return multiple key value pairs from a single table in one row

Pivot rows to columns without aggregate

问题:

我想用SQL完全完成的任务是什么?如果是这样,怎么办?如果没有,请解释原因。

非常感谢您的帮助,以下是附表,可帮助您入门:

with
    table_1 ( id, key, value ) as (
        select 1,'phone_num_1','111-111-1111' from dual union all
        select 1,'phone_num_2','222-222-2222' from dual union all
        select 1,'contact_1','friend' from dual union all
        select 1,'contact_2','family' from dual union all
        select 1,'first_name','mike' from dual union all
        select 1,'last_name','smith' from dual union all
        select 2,'phone_num_1','333-333-3333' from dual union all
        select 2,'phone_num_2','444-444-4444' from dual union all
        select 2,'contact_1','family' from dual union all
        select 2,'contact_2','friend' from dual union all
        select 2,'first_name','john' from dual union all
        select 2,'last_name','adams' from dual union all
        select 3,'phone_num_1','555-555-5555' from dual union all
        select 3,'phone_num_2','666-666-6666' from dual union all
        select 3,'phone_num_3','777-777-7777' from dual union all
        select 3,'contact_1','work' from dual union all
        select 3,'contact_2','family' from dual union all
        select 3,'contact_3','friend' from dual union all
        select 3,'first_name','mona' from dual union all
        select 3,'last_name','lisa' from dual
     )
select * from table_1;

3 个答案:

答案 0 :(得分:1)

这很丑,但是我认为您需要做

select t1.* , t2.value, t3.n, t3.f
from table_1 t1
inner join table_1 t2 on t1.id = t2.id and REPLACE(t1.key, 'phone_num_', '') = REPLACE(t2.key, 'contact_', '')
inner join (
    select ID, min(case when Key = 'first_name' then Value end) as n, min(case when Key = 'last_name' then Value end) as f
    from table_1
    group by ID
) t3 on t1.id = t3.id
where
t1.Key not in('first_name','last_name')

答案 1 :(得分:1)

这不是动态枢轴,因为您有一组固定的键-您只需要首先将键的枚举与键本身分开即可。

您需要:

  • 从枚举项中分离phone_numcontact键前缀;然后
  • 枢转没有枚举的公用密钥,以便它们与每个枚举密钥相关联;最后,
  • 再次旋转一次以连续获取枚举的键。

Oracle设置

CREATE TABLE table_1 ( id, key, value ) as
select 1,'phone_num_1','111-111-1111' from dual union all
select 1,'phone_num_2','222-222-2222' from dual union all
select 1,'contact_1','friend' from dual union all
select 1,'contact_2','family' from dual union all
select 1,'first_name','mike' from dual union all
select 1,'last_name','smith' from dual union all
select 2,'phone_num_1','333-333-3333' from dual union all
select 2,'phone_num_2','444-444-4444' from dual union all
select 2,'contact_1','family' from dual union all
select 2,'contact_2','friend' from dual union all
select 2,'first_name','john' from dual union all
select 2,'last_name','adams' from dual union all
select 3,'phone_num_1','555-555-5555' from dual union all
select 3,'phone_num_2','666-666-6666' from dual union all
select 3,'phone_num_3','777-777-7777' from dual union all
select 3,'contact_1','work' from dual union all
select 3,'contact_2','family' from dual union all
select 3,'contact_3','friend' from dual union all
select 3,'first_name','mona' from dual union all
select 3,'last_name','lisa' from dual

查询

SELECT *
FROM   (
  SELECT id,
         CASE
         WHEN key LIKE 'phone_num_%' THEN 'phone_num'
         WHEN key LIKE 'contact_%'   THEN 'contact'
         ELSE key
         END AS key,
         CASE
         WHEN key LIKE 'phone_num_%'
         OR   key LIKE 'contact_%'
         THEN TO_NUMBER( SUBSTR( key, INSTR( key, '_', -1 ) + 1 ) )
         ELSE NULL
         END AS item,
         value,
         MAX( CASE key WHEN 'first_name' THEN value END )
           OVER ( PARTITION BY id ) AS first_name,
         MAX( CASE key WHEN 'last_name'  THEN value END )
           OVER ( PARTITION BY id ) AS last_name
  FROM   table_1
)
PIVOT( MAX( value ) FOR key IN ( 'contact' AS contact, 'phone_num' AS phone_num ) )
WHERE item IS NOT NULL
ORDER BY id, item

输出

ID | ITEM | FIRST_NAME | LAST_NAME | CONTACT | PHONE_NUM   
-: | ---: | :--------- | :-------- | :------ | :-----------
 1 |    1 | mike       | smith     | friend  | 111-111-1111
 1 |    2 | mike       | smith     | family  | 222-222-2222
 2 |    1 | john       | adams     | family  | 333-333-3333
 2 |    2 | john       | adams     | friend  | 444-444-4444
 3 |    1 | mona       | lisa      | work    | 555-555-5555
 3 |    2 | mona       | lisa      | family  | 666-666-6666
 3 |    3 | mona       | lisa      | friend  | 777-777-7777

db <>提琴here


如果您可以重构表,那么一个简单的改进就是添加一个额外的列来保存键的枚举,并在每个枚举通用的值时使用NULL

CREATE TABLE table_1 ( id, key, line, value ) as
select 1, 'phone_num',  1,    '111-111-1111' from dual union all
select 1, 'phone_num',  2,    '222-222-2222' from dual union all
select 1, 'contact',    1,    'friend'       from dual union all
select 1, 'contact',    2,    'family'       from dual union all
select 1, 'first_name', NULL, 'mike'         from dual union all
select 1, 'last_name',  NULL, 'smith'        from dual

那么您的密钥集始终是固定的,您无需从密钥中提取枚举值。

答案 2 :(得分:0)

选择ID,
   电话,
  联系,   first_value(last)IGNORE NULLS超过(按ID DESC按ID DESC划分的范围,在CURRENT行之间,并且在其后无边界)last_name,
  first_value(FIRST)IGNORE NULLS over(按ID DESC范围按ID顺序划分,在CURRENT行与后无界之后)first_name
从   (选择ID,
    值,
    row_number()over(partition by id,SUBSTR(KEY,1,instr(KEY,'',1)-1)by KEY order)rn,     SUBSTR(KEY,1,instr(KEY,'',1)-1)键   FROM table_1
  )枢纽(MAX(value)FOR KEY IN('phone'AS phone,'last'AS last,'first'AS FIRST,'contact'AS contact)) 按ID排序;