Oracle:如何使用某些键的动态基数来旋转 EAV table?
Oracle: How can I pivot an EAV table with a dynamic cardinality for certain keys?
我在 Oracle 中有以下实体-属性-值 (EAV) table:
| 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 |
我 tried/looked 在:
我研究了Oracle的pivot函数;但是,我不认为这可以解决我的问题,因为我没有固定数量的属性可以作为重点。
我看过这些帖子:
Pivot rows to columns without aggregate
问题:
我想通过 SQL 完成所有可能的事情吗?如果可以,怎么办?如果不是,请说明原因。
非常感谢任何帮助,这里是 with table 来帮助您入门:
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;
这很丑陋,但我认为可以满足您的需求
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')
这不是动态枢轴,因为您有一组固定的键 - 您只需要先将键的枚举与键本身分开。
您需要:
- 将
phone_num
和contact
键前缀与枚举项分开;然后
- 旋转没有枚举的公共键,使它们与每个枚举键相关联;最后,
- 第二次旋转以获得连续的枚举键。
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<>fiddle here
如果您可以重构 table,那么一个简单的改进是添加一个额外的列来保存键的枚举,并在它是每个枚举的公共值时使用 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
那么你的key集合永远是固定的,你不需要从key中提取枚举值。
SELECT id,
phone,
接触,
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
从
(SELECT 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
来自 table_1
) pivot ( MAX(value) FOR KEY IN ( 'phone' AS phone,'last' AS last,'first' AS FIRST,'contact' AS contact))
按 ID 排序;
我在 Oracle 中有以下实体-属性-值 (EAV) table:
| 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 |
我 tried/looked 在:
我研究了Oracle的pivot函数;但是,我不认为这可以解决我的问题,因为我没有固定数量的属性可以作为重点。
我看过这些帖子:
Pivot rows to columns without aggregate
问题:
我想通过 SQL 完成所有可能的事情吗?如果可以,怎么办?如果不是,请说明原因。
非常感谢任何帮助,这里是 with table 来帮助您入门:
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;
这很丑陋,但我认为可以满足您的需求
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')
这不是动态枢轴,因为您有一组固定的键 - 您只需要先将键的枚举与键本身分开。
您需要:
- 将
phone_num
和contact
键前缀与枚举项分开;然后 - 旋转没有枚举的公共键,使它们与每个枚举键相关联;最后,
- 第二次旋转以获得连续的枚举键。
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<>fiddle here
如果您可以重构 table,那么一个简单的改进是添加一个额外的列来保存键的枚举,并在它是每个枚举的公共值时使用 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
那么你的key集合永远是固定的,你不需要从key中提取枚举值。
SELECT id,
phone,
接触,
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
从
(SELECT 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
来自 table_1
) pivot ( MAX(value) FOR KEY IN ( 'phone' AS phone,'last' AS last,'first' AS FIRST,'contact' AS contact))
按 ID 排序;