Exchange & split partition have a issue - oracle12c

I am trying to exchange non partition data with partition data. I have done following steps.
Created a new table TEMP_TABLE with partition with the TEMP_TABLE_1 range as date('1-09-2019').
And I have used
ALTER TABLE TEMP_TABLE
EXCHANGE PARTITION TEMP_TABLE_1
WITH TABLE ORG_TABLE
WITHOUT VALIDATION
UPDATE GLOBAL INDEXES;
With this my table data is exchanged with the partition and new table I can see the partition with data.
But now the problem is that the data contains rows with date more than 1-09-2019, when I try
select count(*) from TEMP_TABLE where date > '1-09-2019';
its giving 0 though there is data with the date till today.
If I try to split this partition
ALTER TABLE TEMP_TABLE SPLIT PARTITION TEMP_TABLE_1 INTO (PARTITION
TEMP_TABLE_2 values LESS THAN (TO_DATE('01-OCT-2019 00:00:00', 'DD-MON-
YYYY HH24:MI:SS')), PARTITION TEMP_TABLE_1) UPDATE GLOBAL INDEXES
PARALLEL 4;
Its throwing partition cannot be split along the specified high bound.
How to get the data which is more than the range date i have provided.

As you are exchanging data without validation (probably to improve performance) Oracle won't validate whether the value for partition key column of the data that is inserted matches the partition range condition of the partition into which that data is inserted.
--partitioned table
create table mytabp(n date)
partition by range(n)
interval(numtodsinterval(1, 'DAY'))
(partition p0 values less than (to_date('20190901','yyyymmdd')));
--nonpartitioned table to hold the data outside partition range
create table temp_mytab(n date);
insert into temp_mytab values(to_date('20191001','yyyymmdd'));
--exchanging without validation
alter table mytabp exchange partition p0 with table temp_mytab without validation;
--Data exists
select count(1) from mytabp;--1
Due to partition pruning in the below query the record is searched in the partition which must hold this data by definition. As the record exists in an incorrect partition that data is not returned.
select count(1) from mytabp where n > to_date('20190901','yyyymmdd');--0
By applying TRUNC on partitioned column, Oracle is presented with an option to scan all partitions. So the below SQL produces the record. For me on Oracle 12cR1 on Exadata, the subsequent executions of this SQL with TRUNC scanned the exact partition where the record was sitting and did not scan all partitions. I checked this with my explain plan's PARTITON_START and PARTITION_STOP columns.
select count(1) from mytabp where trunc(n) > to_date('20190901','yyyymmdd');--1
By design it is bad to place data on incorrect partitions. Please validate or filter for the correct data before executing exchange without validation.

Related

How to get value list of list partitioning table of postgresql?

I am trying to use list partitioning in PostgreSQL.
https://www.postgresql.org/docs/current/ddl-partitioning.html
So, I have some questions about that.
Is there a limit on the number of values or partition tables in list partitioning?
When a partitioning table is created as shown below, can i check the value list with SQL? (like keys = [test, test_2])
CREATE TABLE part_table (id int, branch text, key_name text) PARTITION BY LIST (key_name);
CREATE TABLE part_default PARTITION OF part_table DEFAULT;
CREATE TABLE part_test PARTITION OF part_table FOR VALUES IN ('test');
CREATE TABLE part_test_2 PARTITION OF part_table FOR VALUES IN ('test_2');
When using the partitioning table created above, if data is added with key_name = "test_3", it is added to the default table. If 'test_3' exists in the default table and partitioning is attempted with the corresponding value, the following error occurs.
In this case, is there a good way to partition with the value 'test_3' without deleting the value in the default table?
CREATE TABLE part_test_3 PARTITION OF part_table FOR VALUES IN ('test_3');
Error: updated partition constraint for default partition "part_default" would be violated by some row
Is it possible to change the table name or value of a partition table?
Thank you..!
Is there a limit on the number of values or partition tables in list
partitioning?
Some test: https://www.depesz.com/2021/01/17/are-there-limits-to-partition-counts/
The value in current table and value reside in which partition.
SELECT
tableoid::pg_catalog.regclass,
array_agg(DISTINCT key_name)
FROM
part_table
GROUP BY
1;
To get all the current partition, and the configed value range. Use the following.
SELECT
c.oid::pg_catalog.regclass,
c.relkind,
inhdetachpending as is_detached,
pg_catalog.pg_get_expr(c.relpartbound, c.oid)
FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
WHERE c.oid = i.inhrelid
AND i.inhparent = '58281'
--the following query will return 58281.
select c.oid
from pg_catalog.pg_class c
where relname ='part_table';

how to test if a postgres partition has been populated or not

How can I (quickly) test if a postgres partition has any rows in it?
I have a partitioned postgres table 'TABLE_A', partitioned by date-range. The name of each individual partition indicates the date-range i.e. TABLE_A_20220101 (1st Jan this year) TABLE_A_20220102 (2nd Jan 2022)
The table includes many years of data, so it includes several thousand individual partitions, each partition contains many millions of rows.
Is there a quick way of testing if a partition has any data in it? There are several solutions I've found, but they all involve count(*) and all take ages.
Please note - I'm NOT trying to accurately determine the row-count, just determine if each partition has any rows in it.
You can use an exists condition:
select exists (select * from partition_name limit 1)
That will return true if partition_name contains at least one row

Insert into partition table postgresql

everyone!
I'm trying to insert data from non-partition table t1 to a partition one t2 with
insert into t2 (select * from t1);
But I get an error: Partition key of the falling row contains (column_name) = (value)
What can be wrong?
t2 is partitioned by months by column date_name , not column_name
P.s. when I try to insert data from partition to partition table with the same way, I get the same error
Hoe should I insert data in partition table?
Version: Postgresql 11
There must be at least one row in t1 for which there is no matching partition in t2. You have to create all partitions for the table before you insert data.
To figure out which row gives you trouble, look at the value from the error message.

Predict partition number for Postgres hash partitioning

I'm writting an app which uses partitions in Postgres DB. This is will be send to customers and run on their server. This implies that I have to be prepared for many different scenarios.
Lets start with simple table schema:
CREATE TABLE dir (
id SERIAL,
volume_id BIGINT,
path TEXT
);
I want to partition that table by volume_id column.
What I would like to achieve:
limited number of partitions (right now it's 500 but I'm will be tweaking this parameter later)
Do not create all partitions at once - add them only when they are needed
support volume ids up to 100K
[NICE TO HAVE] - been able for human to calculate partition number from volume_id
Solution that I have right now:
partition by LIST
each partition handles volume_id % 500 like this:
CREATE TABLE dir_part_1 PARTITION OF dir FOR VALUES IN (1, 501, 1001, 1501, ..., 9501);
This works great because I can create partition when it's needed, and I know exactly to which partition given volume_id belongs. But I have to manually declare numbers and I cannot support high volume_ids because speed of insert statements decrease drastically (more than 2 times).
It looks like I could try HASH partitioning but my biggest concern is that I have to create all partitions at the very beginning and I would like to be able to create them dynamically when they are needed, because planning time increases significantly up to 5 seconds for 500 partitions. For example I know that I will be adding rows with volume_id=5. How can I tell which partition should I create?
I was able to force Postgres to use dummy hash function by adding hash operator for partitioned table.
CREATE OR REPLACE FUNCTION partition_custom_bigint_hash(value BIGINT, seed BIGINT)
RETURNS BIGINT AS $$
-- this number is UINT64CONST(0x49a0f4dd15e5a8e3) from
-- https://github.com/postgres/postgres/blob/REL_13_STABLE/src/include/common/hashfn.h#L83
SELECT value - 5305509591434766563;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
CREATE OPERATOR CLASS partition_custom_bigint_hash_op
FOR TYPE int8
USING hash AS
OPERATOR 1 =,
FUNCTION 2 partition_custom_bigint_hash(BIGINT, BIGINT);
Now you can declare partitioned table like this:
CREATE TABLE some_table (
id SERIAL,
partition_id BIGINT,
value TEXT
) PARTITION BY HASH (partition_id);
CREATE TABLE some_table_part_2 PARTITION OF some_table FOR VALUES WITH (modulus 3, remainder 2);
Now you can safely assume that allow rows with partition_id % 3 = 2 will land in some_table_part_2 partition. So if you are sure what values you will receive in partition_id column you can create only required partitions.
DISCLAIMER 1: Unfortunately this will not work correctly right now (Postgres 13.1) because of bug #16840
DISCLAIMER 2: There is not point of using this technic unless you are planning to create large number of partitions (I would say 50 or more) and prolonged planning time is an issue.

How to divide single partition into two different partitions in PostgreSQL and then drop single partitioning

I have one range partition from 2020/01/01 and 2020/06/01
I want to move this to two different partitions i.e 2020/01/01 to 2020/03/31 and 2nd partition from 2020/04/01 to 2020/06/01
how I can achieve as it not allow me to create two partition in same date range and then move data as it say "violates partition rule"
We can detach partition like that
ALTER TABLE "eMAR" Detach PARTITION "eMAR_default";
then can create any partitons like that
CREATE TABLE "eMAR_inBw" PARTITION OF public."eMAR"
FOR VALUES FROM ('2016-04-17 20:00:00') TO ('2016-06-17 20:00:00');
and then can move data from detached partitions like that
insert into "eMAR_inBw"
select * from "eMAR_default" where "MedDateTime" >= '2016-04-17 20:00:00' and "MedDateTime" <= '2016-06-17 20:00:00'
and then you can delete the old partition as well if not required