基于复合指数的更有效的方法来获取最新价值?

时间:2019-03-07 19:24:56

标签: sql oracle

我有一个名为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;

效率低下吗?

预先感谢

6 个答案:

答案 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_typecolorcar_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