我有一个需要花费大量时间的选择查询:
select user_id, variable, round(AVG(v_Score),1) v_score
from TEST_1M_SCORE_V1 where clock between 1 and 12 group by user_id, variable
此表格 - TEST_1M_SCORE_V1
有260,000,000行。
是否还有其他编写group by子句的方法,以便更快地运行?
表格定义:
Name Null Type
------------- ---- -------------
USER_ID NUMBER
CLOCK NUMBER
VARIABLE VARCHAR2(255)
V_SCORE NUMBER
答案 0 :(得分:2)
根据数据,这是两个答案,而不是一个答案。这是您的查询:
select user_id, variable, round(AVG(v_Score), 1) as v_score
from TEST_1M_SCORE_V1
where clock between 1 and 12
group by user_id, variable;
选项1是相对较少的行满足where
条件 - 其中“相对较少”绝对不超过少数百分比。在这种情况下,TEST_1M_SCORE_V1(clock)
上的索引会很有用。对于覆盖索引,您可以将其扩展到TEST_1M_SCORE_V1(clock, user_id, variable, score)
。 Oracle需要为group by
完成所有工作,但只需要处理较少的数据。
选项2是当更多行满足where
条件时。在这种情况下,您希望Oracle对group by
执行完整索引扫描。问题是where
条款。一种方法是使用基于函数的索引将其合并到索引中。但是,这是非常具体的(它适用于1和12但不适用于1和11)。
相反,请将查询写为:
select user_id, variable,
round(AVG(case when clock between 1 and 12 then v_Score end), 1) as v_score
from TEST_1M_SCORE_V1
group by user_id, variable
having sum(case when clock between 1 and 12 then 1 else 0 end) > 0;
(having
子句可能没有必要,具体取决于您对user_id
/ variable
组合的关注程度avg()
NULL
TEST_1M_SCORE_V1(user_id, variable, clock, v_score)
。 )
此查询等同于原始查询。它似乎做了更多的工作,但这项工作针对索引扫描进行了高度优化:group by
。这个想法是Oracle可以按顺序读取索引,同时执行group by
和计算。它永远不需要在原始数据集中查找数据,也不需要使用基于散列或排序的算法处理set deadlock_priority high; -- could also try "10" instead of "high" (5)
alter database dbname set multi_user; -- can also add "with rollback immediate"
。