Oracle SQL 从每天分区的 table 中提取选定日期的数据

Oracle SQL pull data for a selected date from a table that is daily partitioned

我喜欢做的是只在每个星期五的繁忙时间拉取一个数据集,持续 1 年。 我使用了以下查询:

select to_char(datetimelocal,'DD/MM/YYYY HH24:MI:SS'), colA,colB,colC,colD 
from Schema_X.Table_Y
where DATETIMELOCAL between '1-Apr-2014' and '1-Apr-2015' 
    and to_char(datetimelocal,'D')=6  
    and to_number(to_char(datetimelocal,'sssss')) between 57600 and 64800

此查询有效,但我从系统管理员那里收到以下警告消息,提示我已耗尽系统资源。

"There is an user xxx running a query over Schema_X tables and those are scanning the whole table and not doing a partition pruning. So the user should use partitioned field also reduce the date range, which is too big"

我发现 Table_X 每天分区,但不知道如何明智地使用分区来减少系统负载。

分区是这样的:

PARTITION_NAME,HIGH_VALUE,HIGH_VALUE_LENGTH,TABLESPACE_NAME,COMPRESSION,NUM_ROWS,BLOCKS,EMPTY_BLOCKS,LAST_ANALYZED,AVG_SPACE,SUBPARTITION_COUNT
20121230,TO_DATE(' 2012-12-31 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'),83,NATIONAL_RPT,DISABLED,,,,,,0
20121231,TO_DATE(' 2013-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'),83,NATIONAL_RPT,DISABLED,,,,,,0
20130101,TO_DATE(' 2013-01-02 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'),83,NATIONAL_RPT,DISABLED,,,,,,0
20130102,TO_DATE(' 2013-01-03 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'),83,NATIONAL_RPT,DISABLED,,,,,,0
20130103,TO_DATE(' 2013-01-04 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'),83,NATIONAL_RPT,DISABLED,,,,,,0
....

您的查询正在分阶段过滤,首先按年份,然后按天,然后按时间。这意味着原则上任何记录都可以计算在内;系统无法轻松放大您需要的分区。您需要根据特定日期来表达过滤器:

SELECT *
FROM Tbl
WHERE
        DateTimeLock >= DATE'2015-04-03' AND DateTimeLock < DATE'2015-04-04' AND 
    AND EXTRACT(HOUR FROM DateTimeLock)) BETWEEN 16 AND 17  -- Inclusive

这将放大您需要的确切分区。

然而,它显然只是给你一天的数据。您可能需要使用循环在每个星期五的单独查询中进行查询,将结果收集在 table.

您可以尝试通过在第一个 DateTimeLock 过滤器中使用 OR 将其保留为单个查询:

(
   DateTimeLock >= DATE'2015-04-03' AND DateTimeLock < DATE'2015-04-04'
OR DateTimeLock >= DATE'2015-03-27' AND DateTimeLock < DATE'2015-03-28'
OR DateTimeLock >= DATE'2015-03-20' AND DateTimeLock < DATE'2015-03-21'
)
AND EXTRACT(HOUR FROM DateTimeLock)) BETWEEN 16 AND 17  -- Inclusive

...但是,我怀疑查询引擎会将其转换为 table 扫描,这就是您开始时的扫描。

正如乔恩所说,由于您的 where 子句的表达方式,Oracle 无法放大到特定分区。如果你想要所有的星期五,你必须给 sql 引擎特定的日子。这可以通过创建一个包含您需要的所有星期五的 table 或即时生成一个来完成。

-- generate table
CREATE TABLE friday_table (friday_date DATE);

DECLARE
  v_last_friday_of_period DATE := to_date('2015.04.10','yyyy.mm.dd');
  v_particular_friday DATE := v_last_friday_of_period;
BEGIN
  WHILE v_last_friday_of_period - v_particular_friday < 365 LOOP
    INSERT INTO friday_table VALUES (v_particular_friday);
    v_particular_friday := v_particular_friday - 7;
  END LOOP;
END;
/

SELECT *
FROM tbl t
    ,friday_table f
WHERE t.datetimelock BETWEEN to_date(to_char(f.friday_date,'yyyy.mm.dd ')||'12:00:00','yyyy.mm.dd hh24:mi:ss') 
                         AND to_date(to_char(f.friday_date,'yyyy.mm.dd ')||'13:00:00','yyyy.mm.dd hh24:mi:ss');

-- on the fly
SELECT *
FROM tbl t
    ,(SELECT to_date('2015.04.10','yyyy.mm.dd') - rownum * 7 AS friday_date
      FROM dual
      CONNECT BY rownum <= 52) f
WHERE t.datetimelock BETWEEN to_date(to_char(f.friday_date,'yyyy.mm.dd ')||'12:00:00','yyyy.mm.dd hh24:mi:ss') 
                         AND to_date(to_char(f.friday_date,'yyyy.mm.dd ')||'13:00:00','yyyy.mm.dd hh24:mi:ss');

我认为理想情况下,您应该编写一个查询来生成您想要 运行 查询的日期列表。

类似...

(select <somedate_that_is_a_friday> + rownum * 7
from   dual
connect by level <= <however_many_you_want>)

如果你那么:

select ...
where  DATETIMELOCAL in (select <somedate_that_is_a_friday> + ... etc

... 然后一个解释计划应该显示分区正在被 KEY 选择,这表明分区 p运行ing 将发生但优化器不知道哪些分区将被访问直到执行时间。