oracle 11g pivot查询优化 - 多行到单行

时间:2014-05-09 10:44:21

标签: sql oracle11g pivot-table sql-optimization sql-tuning

我有以下表格

user table
USER_ID    USER_NAME
1          smith
2          clark
3          scott
4          chris
5          john

property table
P_ID    PROPERTY
1       first_name
2       last_name
3       age
4       skill

user_property table
PV_ID    USER_ID    P_ID VALUE
1        1          1    Smith
2        1          2    A
3        1          3    34
4        1          4    Java
5        1          4    DB
6        2          1    Clark
7        2          2    B
8        2          3    39
9        2          4    Java
10       2          4    net
11       2          4    linux
12       3          1    Scott
13       3          2    C
14       3          3    31

我想编写一个查询,它将从以上所有表中获取数据,如下所示:(如果可用,技能将是该用户的第一项技能,否则为null)

USER_ID USER_NAME FIRST_NAME LAST_NAME SKILL
1       smith     Smith      A         Java
2       clark     Clark      B         Java
3       scott     Scott      C         null

我尝试过如下但遇到性能问题:

SELECT
  u.user_id,
  u.user_name,
  MAX(DECODE(p.property, 'first_name', text_value)) firstName,
  MAX(DECODE(p.property, 'last_name', text_value)) lastName,
  MAX(DECODE(p.property, 'age', text_value)) age,
  MAX(DECODE(p.property, 'skill', text_value)) skill
FROM user u,
  property p,
  user_property up,
WHERE u.user_id    = up.user_id
AND p.p_id = up.p_id
GROUP BY u.user_id,
  u.user_name;

我怎么能把它写成oracle 11g的优化查询。

2 个答案:

答案 0 :(得分:0)

查询的性能取决于表的大小以及这些表上的索引。在大多数情况下,最佳做法是在每个主键和外键上都有一个索引。无论如何,主键上的索引是必须的。当您删除行时,外键上的索引会加速连接并防止表锁。

查询的替代方法是使用更多连接而不是子选择,并使用WITH子句来简化它:

with t as (
  select u.user_id, u.user_name, up.p_id, up.value
  from user_property up
  join user u on u.user_id = up.user_id
)
select u.user_id, u.user_name,
    t_first_name.value first_name,
    t_last_name.value last_name,
    (select min(value) from t where t.user_id = u.user_id and t.p_id = 4) skill
from user u
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2;
BTW:这是一个不太适合SQL的数据模型。我希望这些用户属性是例外,数据库的其余部分设计更清晰。

答案 1 :(得分:0)

我已尝试过以下查询但获取笛卡尔积。

with t as (
select u.user_id, u.user_name, up.p_id, up.value
from user_property up
join user u on u.user_id = up.user_id
where u.user_name = 'smith'
)
select u.user_id, u.user_name,
t_first_name.value first_name,
t_last_name.value last_name,
(select min(value) from t where t.user_id = u.user_id and t.p_id = 4) skill
from user u
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2;

如果我执行下面的查询,我会在上面的示例中提到5(因为我的user_property表中有5行,例如user_id 1)

select count(u.user_id)
from user_property up
join user u on u.user_id = up.user_id
where u.user_name = 'smith'

因此,如果我执行下面的查询,我将计数为3,因为我的使用表示例中有3行

with t as (
select u.user_id, u.user_name, up.p_id, up.value
from user_property up
join user u on u.user_id = up.user_id
where u.user_name = 'smith'
)
select count(u.user_id)
from user u
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2;