对于日期类型的字段收集直方图后,在直方图统计视图中显示的endpoint_value为长串数字,怎么将这些数字与实际的日期值对应起来呢?
我们举例说明一下:
表TEST_D的列D1的统计信息如下:
SQL> select column_name,high_value,low_value from user_tab_col_statistics where table_name='TEST_D' and column_name='D1';
----------------- ------------------ --------------------
D1 78790914010101 78790103010101
这个视图中的high_value与low_value为raw类型的值,我们可以使用dbms_stats.convert_raw_value将其转换为日期类型。
SQL> set serveroutput on;
SQL> declare
2 highvalue date;
3 lowvalue date;
4 begin
5 dbms_stats.convert_raw_value('78790914010101',highvalue);
6 dbms_stats.convert_raw_value('78790103010101',lowvalue);
7 dbms_output.put_line('highvalue:'||to_char(highvalue,'yyyy-mm-dd hh24:mi:ss'));
8 dbms_output.put_line('lowvalue:'||to_char(lowvalue,'yyyy-mm-dd hh24:mi:ss'));
9 end;
10 /
highvalue:2021-09-20 00:00:00
lowvalue:2021-01-03 00:00:00
PL/SQL procedure successfully completed.
这个列的直方图统计信息如下:
SQL> select column_name,endpoint_value,endpoint_number from user_histograms where table_name='TEST_D' and column_name='D1';
COLUMN_NAME ENDPOINT_VALUE ENDPOINT_NUMBER
-------------------- -------------------------- ------------------
D1 2459218 1
D1 2459252 2
D1 2459255 3
D1 2459354 4
D1 2459385 5
D1 2459425 6
D1 2459445 7
D1 2459478 8
我们发现endpoint_value是一长串数字,这串数字是怎么来的,是从公元前4713年1月1日到这个日期的所经过的天数,闰年的算法比较复杂,我们可以利用差值来推算每个桶的endpoint_value。
首先根据lag分析函数算出每个endpoint_value与前一个桶的endpoint_value的差值div。
SQL> select endpoint_value,
2 endpoint_value - lag(endpoint_value, 1) over(order by endpoint_number) as div,
3 endpoint_number
4 from user_histograms
5 where table_name = 'TEST_D'
6 and column_name = 'D1';
ENDPOINT_VALUE DIV ENDPOINT_NUMBER
------------------------ --------- ---------------
2459218 1
2459252 34 2
2459255 3 3
2459354 99 4
2459385 31 5
2459425 40 6
2459445 20 7
2459478 33 8
8 rows selected.
我们使用sum分析函数计算出当前桶距离第一个桶的差值sumdiv,再使用列统计视图中low_value对应的日期值加上sumdiv,这样就可以推算出每个endpoint_value。
SQL> select to_date('2021-01-03 00:00:00','yyyy-mm-dd hh24:mi:ss') + sum(nvl(div, 0)) over(order by endpoint_number) as endpoint_value,
2 endpoint_number
3 from (select endpoint_value,
4 endpoint_value - lag(endpoint_value, 1) over(order by endpoint_number) as div,
5 endpoint_number
6 from user_histograms
7 where table_name = 'TEST_D'
8 and column_name = 'D1') a
9 ;
ENDPOINT_VALUE ENDPOINT_NUMBER
------------------- ---------------
2021-01-03 00:00:00 1
2021-02-06 00:00:00 2
2021-02-09 00:00:00 3
2021-05-19 00:00:00 4
2021-06-19 00:00:00 5
2021-07-29 00:00:00 6
2021-08-18 00:00:00 7
2021-09-20 00:00:00 8
我们可以看到最大桶的endpoint_value与列统计视图的high_value对应的日期值是吻合的。
经勇哥提醒,此数字为儒略日,oracle其实有函数根据这个值计算出实际日期。
SQL> select to_date(2459218,'J') from dual;
TO_DATE(2459218,'J'
-------------------
2021-01-03 00:00:00
但是这个转换只针对整形数字,无法对小数数字进行转换,官方文档中这样说的
对格式串‘J’的转换:Julian day; the number of days since January 1, 4712 BC. Number specified with J must be integers.
如果时间有具体时分秒,直方图中endpoint_value是带小数点,所以用窗口函数来处理还是有用的。
当我们想依据直方图计算某个日期值的选择性时可以使用此方法。
下面普及一下儒略日常识:
儒略日的起点订在公元前4713年(天文学上记为 -4712年)1月1日格林威治时间平午(世界时12:00),即JD 0指定为UT时间B.C.4713年1月1日12:00到UT时间B.C.4713年1月2日12:00的24小时。每一天赋予了一个唯一的数字,顺数而下,如:1996年1月1日12:00:00的儒略日是2450084。这个日期是考虑了太阳、月亮的轨道运行周期,以及当时收税的间隔而订出来的。




