【问题标题】:Oracle: How can I pivot an EAV table with a dynamic cardinality for certain keys?甲骨文:我怎样才能对特定键具有动态基数的 EAV 表进行透视?
【发布时间】:2019-12-02 05:55:59
【问题描述】:

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

|身份证 |钥匙 |价值 | |----|-------------|--------------| | 1 |电话号码_1 | 111-111-1111 | | 1 | phone_num_2 | 222-222-2222 | | 1 |联系方式_1 |朋友 | | 1 |联系方式_2 |家庭 | | 1 |名字 |迈克 | | 1 |姓氏 |史密斯 | | 2 |电话号码_1 | 333-333-3333 | | 2 | phone_num_2 | 444-444-4444 | | 2 |联系方式_1 |家庭 | | 2 |联系方式_2 |朋友 | | 2 |名字 |约翰 | | 2 |姓氏 |亚当斯 | | 3 |电话号码_1 | 555-555-5555 | | 3 | phone_num_2 | 666-666-6666 | | 3 | phone_num_3 | 777-777-7777 | | 3 |联系方式_1 |工作 | | 3 |联系方式_2 |家庭 | | 3 |联系方式_3 |朋友 | | 3 |名字 |莫娜 | | 3 |姓氏 |丽莎 |

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

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

这是我想要的结果:

|身份证 |电话号码 |联系方式 |名字 |姓氏 | |----|-------------|---------|------------|------ -----| | 1 | 111-111-1111 |朋友 |迈克 |史密斯 | | 1 | 222-222-2222 |家庭 |迈克 |史密斯 | | 2 | 333-333-3333 |家庭 |约翰 |亚当斯 | | 2 | 444-444-4444 |朋友 |约翰 |亚当斯 | | 3 | 555-555-5555 |工作 |莫娜 |丽莎 | | 3 | 666-666-6666 |家庭 |莫娜 |丽莎 | | 3 | 777-777-7777 |朋友 |莫娜 |丽莎 |

我尝试过/看过什么:

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

Pivot rows to columns without aggregate

问题:

我想要完成的任务是否完全可以通过 SQL 完成?如果是这样,怎么办?如果不是,请解释原因。

非常感谢您提供任何帮助,这里是 with 表格,可帮助您入门:

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;

【问题讨论】:

  • 所谓的 EAV 模型被广泛质疑,因为查询起来既困难又慢(因此这个问题和许多人喜欢它)。不幸的是,由于那个神奇的词“灵活性”,它具有蟑螂般的生存能力。但是,这个问题提出了一个甚至不是 EAV 的模型。没有实体,只有具有公共前缀的键。伤心。

标签: sql oracle pivot entity-attribute-value


【解决方案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

输出

身份证 |项目 | FIRST_NAME | LAST_NAME |联系 | PHONE_NUM -: | ---: | :--------- | :-------- | :-------- | :----------- 1 | 1 |迈克 |史密斯 |朋友 | 111-111-1111 1 | 2 |迈克 |史密斯 |家庭 | 222-222-2222 2 | 1 |约翰 |亚当斯 |家庭 | 333-333-3333 2 | 2 |约翰 |亚当斯 |朋友 | 444-444-4444 3 | 1 |莫娜 |丽莎 |工作 | 555-555-5555 3 | 2 |莫娜 |丽莎 |家庭 | 666-666-6666 3 | 3 |莫娜 |丽莎 |朋友 | 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】:

    这很丑,但我认为可以满足您的需要

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

    【讨论】:

      【解决方案3】:

      选择 ID,
      电话,
      接触, first_value(last) IGNORE NULLS over (partition BY id order by id DESC range BETWEEN CURRENT row AND unbounded following ) last_name,
      first_value(FIRST) IGNORE NULLS over (partition BY id order by id DESC range BETWEEN CURRENT row AND unbounded following ) first_name
      从 (选择 ID,
      价值,
      row_number() over ( partition BY id,SUBSTR(KEY,1 ,instr(KEY,'',1)-1) order by KEY) rn, SUBSTR(KEY,1 ,instr(KEY,'',1) -1) KEY FROM table_1
      ) pivot ( MAX(value) FOR KEY IN ('phone' AS phone,'last' AS last,'first' AS FIRST,'contact' AS contact)) 按 ID 排序;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-05-21
        • 1970-01-01
        • 2021-02-10
        • 1970-01-01
        • 1970-01-01
        • 2020-09-15
        • 2012-07-22
        相关资源
        最近更新 更多