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

Posted

技术标签:

【中文标题】oracle 11g 透视查询优化 - 多行到单行【英文标题】:oracle 11g pivot query optimization - multiple rows to single row 【发布时间】:2014-05-09 10:44:20 【问题描述】:

我有以下表格

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

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

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 的优化查询。

【问题讨论】:

那么每个表有多少行? 【参考方案1】:

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

您的查询的替代方法是使用更多连接而不是子选择并使用 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;

顺便说一句:这是一种不太适合 SQL 的数据模型。我希望这些用户属性是例外,并且数据库的其余部分具有更简洁的设计。

【讨论】:

property 表有 286 条记录,user 表有 115838 条记录,user_property 有 3221472 条记录。所有表都有 PK 索引。 property 表有 286 条记录(这里我们感兴趣的是大约 45 个 properties,表示 output select 子句中的 45 列),user 表有 115838 条记录,user_property 有 3221472 条记录.所有表都有 PK 索引。由于我们有大约 45 列,因此最小/最大可能会影响性能 您希望最终输出为 115,838 个用户中的每个用户都包含一行吗?还是您对一部分用户感兴趣? 没有最后一行,仅包含状态为活动并已注册到特定站点的某些用户。所以我在内部选择子句上添加了 where 子句。最终用户列表大约有 500 行。我尝试了你给出的查询,但给了我错误 ORA-00904: "t_first_name"."user_id": invalid identifier 00904. 00000 - "%s: invalid identifier" 我已经修改了我的答案并希望修复了查询。至于索引:最重要的是在表 USER_PROPERTY 的 USER_ID 和 P_ID 上有一个索引。将表转换为以这两个属性作为主键的索引组织表可能会更好。此外,我推荐的索引有助于有效地选择要显示的 500 个用户。【参考方案2】:

我尝试了以下查询,但是得到了笛卡尔积。

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;

【讨论】:

你为什么添加这个作为答案而不更新你原来的问题?编辑自己的问题是否需要最低声望? 我不明白你为什么要得到笛卡尔积。我的查询是正确的:看到这个fiddle。 @Torino:感谢您的更新。它的工作工作。但是原始查询需要 145 秒,而这个新查询需要 130 秒才能执行。

以上是关于oracle 11g 透视查询优化 - 多行到单行的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 单行子查询在使用嵌套子查询时返回多行

Oracle Left Join 导致单行子查询返回多行错误

DataFrame查询2 - 专用查询:索引和切片

Oracle!你必须要知道的Knowledge points(下)

Oracle-子查询实例

oracle单行多列,拆分成多行