PostgreSQL partitioning syntax FOR VALUES - postgresql

I'm working on table partitioning in PostgreSQL.
I created a partition for my master table:
CREATE TABLE head_partition_table PARTITION OF master_table
FOR VALUES FROM (DATE_START) TO (DATE_END)
PARTITION BY RANGE (ENTITY_ID, GROUP_NAME);
After that, I want to divide head_partition_table into smaller partitions, so I wrote code:
CREATE TABLE subpartition_table OF head_partititon_table
FOR VALUES FROM ('0', 'A') TO ('0', 'Z');
I can't find how I can specify individual values rather than a range.
Something like
CREATE TABLE subpartition_table OF head_partititon_table
FOR VALUES ('0', 'A');
CREATE TABLE subpartition_table OF head_partititon_table
FOR VALUES ('0', 'Z');
I get a syntax error at or near "(".
Is this possible?
P.S. I tried PARTITION BY LIST, but in that case, I can use just one field.

You could partition these by list like you want by introducing another layer of partitions:
CREATE TABLE head_partition_table PARTITION OF master_table
FOR VALUES FROM ('2019-01-01') TO ('2020-01-01')
PARTITION BY LIST (entity_id);
CREATE TABLE subpartition1 PARTITION OF head_partition_table
FOR VALUES IN ('0')
PARTITION BY LIST (group_name);
CREATE TABLE subsubpartition1 PARTITION OF subpartition1
FOR VALUES IN ('A');
But this is more an academic exercise than something useful.
Anything exceeding a at most few hundred partitions will not perform well at all.

Related

Postgres table partition by list limit

I wonder if there is a limit for partition table by list where each subpartition table contains only one element.
For example, I have this partition table:
CREATE TABLE whatever (
city_id int not null,
country_id int not null,
) PARTITION BY LIST (country_id);
And I create millions of subpartition tables:
CREATE TABLE whatever_1 PARTITION OF whatever
FOR VALUES IN (1);
CREATE TABLE whatever_2 PARTITION OF whatever
FOR VALUES IN (2);
# until millions...
CREATE TABLE whatever_10000000 PARTITION OF whatever
FOR VALUES IN (10000000);
Assuming an index on country_id, would that still work?
Or Will I hit the 65000 limit as described here?
Even with PostgreSQL v13, anything that goes beyond at most a few thousand partitions won't work well, and it's better to stay lower.
The reason is that when you use a partitioned table in an SQL statement, the optimizer has to consider all partitions separately. It has to figure out which of the partitions it has to use and which not, and for all partitions that it uses it has to come up with an execution plan. Consequently, planning time will go up as the number of partitions increases. This may not matter for large analytical queries, where execution time dominates, but it will considerably slow down the execution of small statements.
Use longer lists or use range partitioning.

Test HASH function for Postgres table partitioning

I'm using Postgres 11 and would like to use a Hash Partitioning on a table where the primary key is a UUID. I understand I need to select a number of partitions up front, and that the modulus of a hash function on the primary key will be used to assign rows to each partition.
Something like this:
CREATE TABLE new_table ( id uuid ) PARTITION BY HASH (id);
CREATE TABLE new_table_0 PARTITION OF new_table FOR VALUES WITH (MODULUS 3, REMAINDER 0);
CREATE TABLE new_table_1 PARTITION OF new_table FOR VALUES WITH (MODULUS 3, REMAINDER 1);
CREATE TABLE new_table_2 PARTITION OF new_table FOR VALUES WITH (MODULUS 3, REMAINDER 2);
The documentation mentions "the hash value of the partition key" but doesn't specify how that hashing takes place. I'd like to test this hash function against my existing data to see the distribution patterns for different numbers of partitions. Something like this:
SELECT unknown_partition_hash_function(id) AS hash_value, COUNT(id) AS number_of_records
FROM existing_table
GROUP BY 1
Is there a way to use this hash function in a SELECT statement?
It should use hash_any. It doesn't seem to be exposed in any way that's directly accessible.
hash_any

How to update a hash partitioned column in Oracle 12c?

I have a table that is partitioned by hash on a column.
CREATE TABLE TEST(
ACCOUNT_NUMBER VARCHAR(20)
)
PARTITION BY HASH (ACCOUNT_NUMBER)
PARTITIONS 16
Now, I want to update the account_number column itself in the table because of certain requirements.
As this column is partitioned, I'm not able to issue an update command on the table like
Update test set account_number = new_value
as it results to the below error:
Error is: ORA-14402: UPDATING PARTITION WOULD CAUSE PARTITION CHANGE.
Row movement is set to disable for the table.
The one way I know is to enable row movement but I also want to explore other options.
Could you please advise me on how to solve this?

PostgreSQL: Partition table by external (non-column) value

I want to split up a table into partitions, where I have a partition for each distinct value of a column. So, if foo_id is the column, I want to put a row with foo_id=23 into the partition bar_23. Is it possible to then remove the column from the partitions and still use the value for selecting the partition via constraints or do I have to explicitly name the partition table in the query?
I.e., can I do this
INSERT INTO bar (foo_id, other) (23, 'value');
without actually having foo_id in the table? Or do I have to go the explicit way:
INSERT INTO bar_23 (other) ('value');

Hive partitioning external table based on range

I want to partition an external table in hive based on range of numbers. Say numbers with 1 to 100 go to one partition. Is it possible to do this in hive?
I am assuming here that you have a table with some records from which you want to load data to an external table which is partitioned by some field say RANGEOFNUMS.
Now, suppose we have a table called testtable with columns name and value. The contents are like
India,1
India,2
India,3
India,3
India,4
India,10
India,11
India,12
India,13
India,14
Now, suppose we have a external table called testext with some columns along with a partition column say, RANGEOFNUMS.
Now you can do one thing,
insert into table testext partition(rangeofnums="your value")
select * from testtable where value>=1 and value<=5;
This way all records from the testtable having value 1 to 5 will come into one partition of the external table.
The scenario is my assumption only. Please comment if this is not the scenario you have.
Achyut