我不是查询性能专家,我正在学习Oracle优化器如何处理不同的查询并调整它们以供其使用。下面是我的项目中的这样的查询,我坚持为大型数据集优化它(对于大型数据集来说它正在减慢)。
SELECT
v1.id,
v1.date_created,
v1.name,
v1.size
FROM
ver v1
INNER JOIN (
SELECT
id,
MAX(date_created) AS last_date_created
FROM
ver
WHERE
id IN (
...500 ids
)
AND active = 'Y'
AND archived = 'N'
GROUP BY
id
) v2 ON v1.date_created = v2.last_date_created
AND v1.id = v2.id
我尝试了SQL开发人员查询调优顾问,没有推荐。这里的问题是它将在查询的两个部分进行全表扫描,而不使用任何索引和ver表包含近1M的记录。下面是ver表脚本
create table ver
( "Ver_id" VARCHAR2(36 BYTE) Primary key
"NAME" VARCHAR2(255 BYTE)
"ACTIVE" VARCHAR2(1 BYTE)
"ARCHIVED" VARCHAR2(1 BYTE)
"DESCRIPTION" VARCHAR2(255 BYTE),
"ID" VARCHAR2(36 BYTE)
"DATE_CREATED" NUMBER(*,0)
"CREATED_BY_USER" VARCHAR2(64 BYTE)
"SIZE" NUMBER(*,0)
"LAST_MODIFIED" NUMBER(*,0))
和索引是id上的一个非唯一索引和last_modified上的(id,name)和非唯一的唯一索引。
该查询现在需要将近2-3分钟才能执行。有任何建议。
如果从1M行表中仅选择2到3 K行,则可以从使用索引中获益。
你基本上想要a)选择所有具有ID
s定义列表的行,并b)使用ID
date_created过滤每个MAX
only记录。
你只需要一个关于ID
的索引
create index ver_idx on ver(id);
以下是可以使用的两种替代策略:
使用分析函数获取最近的行
在子查询中,您将获得具有指定ID
s的所有行,并使用RANK
解析函数定义行的顺序。主查询仅选择具有rn = 1
的行,即使用max(date_created)。
请注意,我使用RANK
来获得与查询相同的结果。如果max datum上有关联,则会获得更多记录。如果你只想要一条记录,你可以使用ROW_NUMBER
。
with dt as (
select
id,date_created, name, "SIZE",
rank() over (partition by id order by date_created desc) rn
from ver
where id between 1 and 500
AND active = 'Y'
AND archived = 'N')
select
id,date_created, name, "SIZE"
from dt
where rn = 1;
您可以使用索引获取具有选定ID
s的所有行以及其他过滤器,以仅获取具有最大日期的行。
使用相关子查询
您使用相关子查询来过滤具有最大日期的行:
select
id,date_created, name, "SIZE"
from ver a
where id between 1 and 500
AND active = 'Y'
AND archived = 'N'
AND date_created in (select max(date_created)
from ver where id = a.id and active = a.active
and archived = a.archived)
不可能说哪种方法是最好的。这取决于表中的数据。
简单测试,检查execution plans并找到性能最佳的查询。
首先,你不需要在id
上有索引,因为你已经有(id,name)
对的索引。
你可以在ver(id,date_created)和ver(active,Archived)
上的位图索引上有Btree索引
你也没有在SELECT
语句中使用v2,因此上面的查询可以重写为
SELECT
v1.id,
v1.date_created,
v1.name,
v1.size
FROM
ver v1
WHERE EXISTS (
SELECT 1
FROM ver v2
WHERE v1.id = v2.id
and v2.id IN (
...500 ids
)
AND v2.active = 'Y'
AND v2.archived = 'N'
GROUP BY v2.id
HAVING MAX(v2.date_created) = v1.date_created
)