调整基于日期选择最新行的查询

时间:2018-01-19 07:51:48

标签: sql oracle sql-tuning

编辑:抱歉,我忘记添加重要条件value1 - value 2 <> 0;。没有它,结果有~111k记录。并且当我使用CTE重写查询时,在最大日期获取代码集以加入主表,我认为索引似乎无法帮助我的情况。 - ==================================

我们有一个查询(CTE),可以获取最新日期的记录。

对于最糟糕的情况之一,这需要8.6秒才能获得11k行。 表table_name有3.1密耳行。

目前此查询使用全表扫描。我尝试但无法找到要在此查询中添加的索引。任何人都可以调整它吗?

--sample - one of worse cases
p_filter_code_1 VARCHAR2(200) := ' ';
p_filter_code2 VARCHAR2(10) := ' ';
p_filter_code3 VARCHAR2(10) := ' ';
p_filter_code4 VARCHAR2(10) := ' ';
p_filter_code5 VARCHAR2(10) := ' ';
p_filter_start_date NUMBER := 20170101;
p_filter_end_date NUMBER := 20171231;    
--===============


SELECT code_2, code_3, code_4, code5
    value1, value2
FROM (
    SELECT t.*,
        ROW_NUMBER() OVER (PARTITION BY code_1, code_2, code_3, code_4, code_5 ORDER BY date_col DESC) AS rn
    FROM table_name t
    WHERE
        (
            code_1 IN (SELECT code_1 FROM temp_code)
        )
        AND date_col < p_filter_start_date
        AND (code_2 LIKE p_filter_code2 OR p_filter_code2 = ' ')
        AND (code_3 LIKE p_filter_code3 OR p_filter_code3 = ' ')
        AND (code_4 = p_filter_code4 OR p_filter_code4 = ' ')
        AND (code_5 = p_filter_code5 OR p_filter_code5 = ' ')
)
WHERE rn = 1 AND value1 - value 2 <> 0;

这是code_1_table和temp_code的示例值,带有来自用户的过滤器:'1002,1020-1025,1030,1040-1050'(就像单词中的打印页面)

code_1_table (sample)
code_1     code_1_name
1001        test
1002         x..
1023          .sona
1025        .sojj
1026        .oifhal


temp_code sample records with p_filter_code_1 := '1002,1020-1025,1030,1040-1050'
code_1
1002
1023
1025

CREATE TABLE table_name
    (code_1                         VARCHAR2(10 BYTE) ,
    code_2                          VARCHAR2(20 BYTE) ,
    code_3                         VARCHAR2(20 BYTE) ,
    code_4                         VARCHAR2(25 BYTE) ,
    code_5                          VARCHAR2(25 BYTE) ,
    value1                          NUMBER,
    value2                          NUMBER,
    value3                          NUMBER,
    value4                          NUMBER,
    date_col                        NUMBER  )

ALTER TABLE table_name
ADD CONSTRAINT table_name_p 
    PRIMARY KEY (code_1, code_2, code_3, code_4, code_5, date_col)
/

3 个答案:

答案 0 :(得分:0)

执行select t.*... Oracle最有可能跳过t上的任何索引。

也许您可以尝试在date_col(或已排序的索引)上创建索引并使用with构造来实现数据集,您可以使用row_number获取最新记录,然后在table_name上与rowid加入。

答案 1 :(得分:0)

确保table_name.code_1和temp_code.t_code1上有NOT NULL约束 索引可能对您没有帮助,除非您的table_name包含大量列,并且此查询所需的8列都在其自己的索引结构中。

您的查询需要处理相当多的行,并且可能永远不会是亚秒,但是,您使用分析函数的输出作为过滤器,其中分组/聚合更有效,因此通过重写您的查询如下所示,你会看到略有改善。

select t.code_2
     , t.code_3
     , t.code_4
     , t.code5
     , max(t.value1) keep (dense_rank last order by date_col) value1
     , max(t.value2) keep (dense_rank last order by date_col) value2
  from table_name t
       inner join temp_code tc on (t.code_1 = tc.code_1)
 where t.date_col < l_filter_start_date
   and (t.code_2 LIKE l_filter_code2 OR l_filter_code2 = ' ')
   and (t.code_3 LIKE l_filter_code3 OR l_filter_code3 = ' ')
   and (t.code_4 = l_filter_code4 OR l_filter_code4 = ' ' )
   and (t.code_5 = l_filter_code5 OR l_filter_code5 = ' ' )
 group by t.code_1
     , t.code_2
     , t.code_3
     , t.code_4
     , t.code5

答案 2 :(得分:-1)

实际上,我相信一旦你编写了window函数,Oracle就不愿意使用索引了。请改用GROUP BY + MAX

SELECT tn.code_2, tn.code_3, tn.code_4, tn.code5,
    tn.value1, tn.value2
FROM  table_name tn
JOIN (
    SELECT code_1, code_2, code_3, code_4, code_5, max(date_col) max_date_col
    FROM table_name t
    WHERE
        (
            code_1 IN (SELECT code_1 FROM temp_code)
        )
        AND date_col < l_filter_start_date
        AND (code_2 LIKE l_filter_code2 OR l_filter_code2 = ' ')
        AND (code_3 LIKE l_filter_code3 OR l_filter_code3 = ' ')
        AND (code_4 = l_filter_code4 OR l_filter_code4 = ' ' )
        AND (code_5 = l_filter_code5 OR l_filter_code5 = ' ' )
    GROUP BY code_1, code_2, code_3, code_4, code_5
) t ON tn.code_1 = t.code_1,
       tn.code_2 = t.code_2,
       tn.code_3 = t.code_3,
       tn.code_4 = t.code_4,
       tn.code_5 = t.code_5,           
       tn.max_date_col = t.date_col

并创建像这样的索引

create index ix_table_name_code_max on table_name (
     date_col, code_1, code_2, code_3, code_4, code_5, value1, value2);