问题
CREATE TABLE foos ( batch_id NUMBER, creation_date DATE ) PARTITION BY RANGE (creation_date) SUBPARTITION BY HASH (batch_id) SUBPARTITION TEMPLATE ( SUBPARTITION H0, SUBPARTITION H1, SUBPARTITION H2, SUBPARTITION H3 ) ( PARTITION R0 VALUES LESS THAN (DATE'2018-04-01') ) ; CREATE INDEX foos_n1 ON foos (batch_id); INSERT INTO foos VALUES (1234, SYSDATE); INSERT INTO foos VALUES (1234, SYSDATE); INSERT INTO foos VALUES (1234, SYSDATE); INSERT INTO foos VALUES (1234, SYSDATE);
If I know a batch_id, say 1234
in advance, what is the most efficient way of determining the subpartition name that it would belong to in this table, ideally without having to have already committed a row with this value into the table?
I know I can do this, but the explain plan looks pretty nasty, and it requires that a row with a batch id of 1234
already be committed into the table
SELECT subpartition_name FROM ( SELECT ao.subobject_name subpartition_name FROM foos JOIN all_objects ao ON DBMS_ROWID.ROWID_OBJECT(foos.rowid) = ao.data_object_id WHERE 1=1 AND foos.batch_id = 1234 --AND ao.owner = 'ME' AND ao.object_name = 'FOOS' AND ao.object_type = 'TABLE SUBPARTITION' ) WHERE rownum = 1
回答1:
With this test data
INSERT INTO foos VALUES (1234, SYSDATE);
INSERT INTO foos VALUES (1235, SYSDATE);
INSERT INTO foos VALUES (1236, SYSDATE);
As described here https://jonathanlewis.wordpress.com/2009/11/21/ora_hash-function/
One important feature of the ora_hash() function is that it seems to be the function used internally – with a zero seed – to determine which partition a row belongs to in a hash partitioned table. Of course, as I pointed out in the previous article, you have to tailor the use of the function to the number of partitions you have declared – rounding up to the nearest power of two (and subtracting one) for the “max bucket” input, then adding one to the result, then dropping the highest bit of the result if the number of partitions is not a power of two.
you get
with hsh as (
select BATCH_ID, ora_hash(BATCH_ID, 3)+1 subpartition_position from foos)
select BATCH_ID, SUBPARTITION_POSITION,
(select subpartition_name from user_tab_subpartitions where table_name = 'FOOS' and SUBPARTITION_POSITION = hsh.SUBPARTITION_POSITION) subpartition_name
from hsh;
BATCH_ID SUBPARTITION_POSITION SUBPARTITION_NAME
---------- --------------------- ------------------------------
1236 1 R0_H0
1235 3 R0_H2
1234 4 R0_H3
Note that the parameter 3 in ora_hash
is the number of (sub)partitions subtracted by 1. (= 4-1). You'll have to do additional processing if the number of partition is not a power of two (which is not recommendet) as described in the reference.
You may verify the result with an explicite partition query as below
select * from foos subpartition( R0_H0 ); -- 1236
select * from foos subpartition( R0_H1 ); -- empty
select * from foos subpartition( R0_H2 ); -- 1235
select * from foos subpartition( R0_H3 ); -- 1234
And of course it works also for new keys, new for 1237 which in not in the table.
with hsh as (
select 1237 BATCH_ID, ora_hash(1237, 3)+1 subpartition_position from dual)
select BATCH_ID, SUBPARTITION_POSITION,
(select subpartition_name from user_tab_subpartitions where table_name = 'FOOS' and SUBPARTITION_POSITION = hsh.SUBPARTITION_POSITION) subpartition_name
from hsh;
BATCH_ID SUBPARTITION_POSITION SUBPARTITION_NAME
---------- --------------------- ------------------------------
1237 2 R0_H1
The "predicted" subpartition is R0_H1
, let*s see where the INSERT will go:
INSERT INTO foos VALUES (1237, SYSDATE);
select * from foos subpartition( R0_H1 ); -- 1237
But use with caution, as it is IMO not documented feature ...
来源:https://stackoverflow.com/questions/49566194/how-to-tell-which-partition-a-row-would-go-into-given-a-known-partition-key-val