我有一个名为SAMPLE_TABLE的表,该表具有以下列,由CAR_TYPE,COLOR和CAR_BRAND组成复合索引。
VALUE_ID VALUE CAR_TYPE COLOR SUBMIT_DT CAR_BRAND
1 10 Sedan Blue 3/7/2019 Ford
2 70 Sedan Blue 3/6/2019 Ford
3 20 Sedan Blue 3/5/2019 Ford
4 77 SUV Red 3/7/2019 Volvo
5 100 SUV Red 3/1/2019 Volvo
有没有一种方法可以编写更有效的方法来查询与最新SUBMIT_DT
相关的值?将来,TABLE将具有数百万行的数据,因此,我将需要找到一个可以查询的运行时/成本最低的查询。
例如,以下是在查询蓝色福特轿车时我想要的结果集中的内容:
VALUE
10
以下是我到目前为止的内容:
SELECT value
FROM (
SELECT *
FROM TABLE
WHERE CAR_TYPE = rCar_Type
AND COLOR = rColor
AND CAR_BRAND = rCar_Brand
ORDER by submit_dt desc
)
WHERE rownum = 1;
效率低下吗?
预先感谢
答案 0 :(得分:4)
哇...已经有很多答案了,但是我想其中有些人错过了我认为是您的问题的重点。
您的表中将有数百万行,并且您对(CAR_TYPE,COLOR,CAR_BRAND)的复合索引的选择不会很严格。您正在寻找一种方法,使复合索引中给定条目的最后一行具有SUBMIT_DT,而不必从该索引中读取所有匹配项。
答案:将SUBMIT_DT DESC
添加到您的综合索引
让我们设置一个测试:
create table matt_objects as select * from dba_objects;
-- This is our analog of your composite index
create index matt_objects_n1 on matt_objects ( object_type, owner );
exec dbms_stats.gather_table_stats(user,'MATT_OBJECTS');
现在,让我们自动跟踪以下语句:
select object_name
from matt_objects
where object_type = 'TABLE'
and owner = 'INV'
order by last_ddl_time desc
fetch first 1 row only;
--------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 88 | 17 (6)| 00:00:01 | |* 1 | VIEW | | 1 | 88 | 17 (6)| 00:00:01 | |* 2 | WINDOW SORT PUSHED RANK | | 162 | 7290 | 17 (6)| 00:00:01 | | 3 | TABLE ACCESS BY INDEX ROWID BATCHED| MATT_OBJECTS | 162 | 7290 | 16 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | MATT_OBJECTS_N1 | 162 | | 3 (0)| 00:00:01 | --------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=1) 2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("LAST_DDL_TIME") DESC )<=1) 4 - access("OBJECT_TYPE"='TABLE' AND "OWNER"='INV')
结果(来自自动跟踪):获得72个一致的读取缓冲区
现在,让我们用可以帮助我们更多的索引来代替您的复合索引:
drop index matt_objects_n1;
create index matt_objects_n1 on matt_objects ( object_type, owner, last_ddl_time desc );
exec dbms_stats.gather_table_stats(user,'MATT_OBJECTS');
..让我们再次自动跟踪该语句:
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 88 | 54 (2)| 00:00:01 |
| 1 | SORT ORDER BY | | 1 | 88 | 54 (2)| 00:00:01 |
|* 2 | VIEW | | 1 | 88 | 53 (0)| 00:00:01 |
|* 3 | WINDOW NOSORT STOPKEY | | 162 | 7290 | 53 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| MATT_OBJECTS | 162 | 7290 | 53 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | MATT_OBJECTS_N1 | 162 | | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=1)
3 - filter(ROW_NUMBER() OVER ( ORDER BY SYS_OP_DESCEND("LAST_DDL_TIME"))<=1)
5 - access("OBJECT_TYPE"='TABLE' AND "OWNER"='INV')
结果(来自自动跟踪):5次读取一致
该索引帮助很大。注意计划不同吗? “ WINDOW SORT PUSHED RANK”已被“ WINDOW NOSORT STOPKEY”取代。索引已经按照您想要的方式(以降序排列)进行排序,Oracle知道它可以按顺序读取索引行并在第一个索引行之后停止-从而轻松完成查询。
有趣的是,尽管第二次查询的性能要好10倍以上,但第二次查询的成本却比第一次查询的成本高。它只是向您显示“成本”是一个估计值,有时应该与一粒盐一起考虑。
答案 1 :(得分:3)
好吧,在这种情况下,您编写的查询不能完全称为“低效率”,而不能称为“无用”,因为它会返回一个 random 行。您可能在子查询中丢失了ORDER BY
。
无论如何:观察其行为:
select value
from (select row_number() over (partition by car_type, color, car_brand
order by submit_dt desc) rn,
value
from sample_table
where car_type = rcar_type
and color = rcolor
and car_brand = rcar_brand
)
where rn = 1;
不要忘记在WHERE
子句中使用的列上创建索引。
答案 2 :(得分:2)
我猜您在问题中的意思是“索引”而不是“关键字”。如果是这样,那么我将创建索引:
create index ix1 on sample_table (car_type, color, car_brand, submit_dt);
然后以下查询将是瞬时的,因为它不会读取堆:
select max(submit_dt)
from sample_table
where CAR_TYPE = rCar_Type
and COLOR = rColor
and CAR_BRAND = rCar_Brand
答案 3 :(得分:2)
您正在寻找每个car_type
,color
,car_brand
的最后一个值。 Oracle为此提供了KEEP LAST
:
SELECT MAX(value) KEEP (DENSE_RANK LAST ORDER BY submit_dt)
FROM table
WHERE car_type = :rcar_type
AND color = :rcolor
AND car_brand = :rcar_brand;
答案 4 :(得分:2)
只需使用FETCH FIRST
:
SELECT *
FROM TABLE
WHERE CAR_TYPE = rCar_Type
AND COLOR = rColor
AND CAR_BRAND = rCar_Brand
ORDER BY submit_dt DESC
FETCH FIRST 1 ROW ONLY
如果您的数据库版本为12c
。
答案 5 :(得分:1)
您可以使用row_number来解决此问题。
例如
SELECT x.value
FROM (
SELECT VALUE,
ROW_NUMBER() OVER (PARTITION BY CAR_TYPE, CAR_COLOR, CAR_BRAND ORDER BY SUBMIT_DATE DESC) AS RN
FROM table
) x
WHERE x.RN = 1