OLAP 函数与自连接以获得最高值的记录

问题描述 投票:0回答:1

有一种常见的情况,我们需要在单个表中查找,并获取另一个字段具有最高值、最新日期或其他内容的单个键的记录。我们可以使用子查询选择键/最大值,然后将其连接回原始表,或者我们可以使用 OLAP 函数(RANK() 或 ROW_NUMBER() 根据需要)来完成此操作。

我正在调整一个针对大型数据集(40M 记录)执行此操作的查询。 Oracle 的 EXPLAIN 并不总是反映真实的性能,所以我对是否依赖它犹豫不决。

下面是一个简单的例子。我想获取订单日期与该订单的最大日期匹配的每条记录的订单号和其他各种字段。

    -- Self-join version:
    with ords as (
            select 'AAA1' as ord_no, to_date('2023-01-01','yyyy-mm-dd') as ord_date, 'A' field1, 'B' field2 from dual union all
            select 'AAA1' as ord_no, to_date('2023-02-01','yyyy-mm-dd') as ord_date, 'C' field1, 'D' field2 from dual union all
            select 'AAA1' as ord_no, to_date('2023-03-01','yyyy-mm-dd') as ord_date, 'E' field1, 'F' field2 from dual union all
            select 'AAA1' as ord_no, to_date('2023-03-01','yyyy-mm-dd') as ord_date, 'E1' field1, 'F1' field2 from dual union all
            select 'BBB1' as ord_no, to_date('2023-01-01','yyyy-mm-dd') as ord_date, 'G' field1, 'H' field2 from dual union all
            select 'BBB1' as ord_no, to_date('2023-02-01','yyyy-mm-dd') as ord_date, 'I' field1, 'J' field2 from dual union all
            select 'BBB1' as ord_no, to_date('2023-03-01','yyyy-mm-dd') as ord_date, 'K' field1, 'L' field2 from dual
        ),
        max_ord as (
            select ord_no, max(ord_date) max_ord_date from ords group by ord_no
        )
        select mo.ord_no, mo.max_ord_date, o.field1, o.field2
        from max_ord mo join ords o on mo.ord_no = o.ord_no and mo.max_ord_date = o.ord_Date order by mo.ord_no;

    -- OLAP version
    -- same data setup as above, omitted for brevity
select ord_no, ord_date, field1, field2 from 
    (select ord_no, ord_date, field1, field2, rank() over (partition by ord_no order by ord_date desc) maxdt
        from ords
    )
where maxdt = 1 order by ord_no;

在这两种情况下,预期结果都是这样的:

"ORD_NO"  "ORD_DATE"  "FIELD1"  "FIELD2"
"AAA1"    3/1/2023    "E"       "F"
"AAA1"    3/1/2023    "E1"      "F1"
"BBB1"    3/1/2023    "K"       "L"

显然,这两种方法都有效;我只是很难找到任何好的引用来说明哪个(一般来说)会更有效。我们使用 Oracle;显然其他数据库的表现可能完全不同。

对我来说,OLAP 版本似乎更容易阅读 - 使您在做什么更明显。

欢迎任何指导或个人经验!

oracle performance olap
1个回答
0
投票

哪个更好完全取决于您想跳过多少个历史版本。

如果在您的示例中,大多数

ORD_NO
值具有数百或数千个
ORD_DATE
,特别是如果表非常宽(每行许多字节),那么想要最后一个将使您转向子查询+索引方法所以你不必扫描整个表。在这种情况下,您只需要占表的一小部分。在这种情况下,两列
(ORD_NO,ORD_DATE)
上的索引可以通过并行快速全扫描来满足
GROUP BY
查询,然后对表进行嵌套循环。过度暗示主要只是为了传达预期的计划:

SELECT /*+ LEADING(x) USE_NL_WITH_INDEX(o) */
       o.*
  FROM (SELECT /*+ NO_MERGE INDEX_FFS(o) PARALLEL(8) */
               ord_no,
               MAX(ord_date) ord_date
          FROM ordertable o  -- will use only the index for the scan
         GROUP BY ord_no) x,
       ordertable o          -- will index seek after only your target rows
 WHERE x.ord_no = o.ord_no
   AND x.ord_date = o.ord_date

但是,如果只有少量历史版本(每个

ORD_DATE
很少有
ORD_NO
值),那么索引访问就会伤害你。您需要进行全表扫描,这将使窗口选项变得有吸引力,因为它避免了第二次扫描。您已经正确编写了该 SQL(您的第二个示例)。

因为这是一个门槛问题,所以没有硬性规定,只有一般准则。最终,只有通过实际尝试两种方法并查看哪种方法最适合您的数据环境才能回答这样的问题。不同的环境,不同的数据,会有不同的结果。

© www.soinside.com 2019 - 2024. All rights reserved.