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 Left Join 导致单行子查询返回多行错误