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 将发生但优化器不知道哪些分区将被访问直到执行时间。
我喜欢做的是只在每个星期五的繁忙时间拉取一个数据集,持续 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 将发生但优化器不知道哪些分区将被访问直到执行时间。