Home > other >  Partitioning tables with a timestamp column in PostgreSQL generates ERROR
Partitioning tables with a timestamp column in PostgreSQL generates ERROR

Time:04-29

I have created a dummy table like:

CREATE TABLE IF NOT EXISTS TBD_partitioning_table (
    shop character varying(200),
    region character varying(200),
    sale_date date not null
) PARTITION BY LIST(sale_date);

The table should be partitioned per sale_date. When I trie to insert values on that table like:

INSERT INTO TBD_partitioning_table
VALUES ('A', 'LA', '2022-04-28'::date);

I receive the following error:

ERROR: no partition of relation "tbd_partitioning_table" found for row DETAIL: Partition key of the failing row contains (sale_date) = (2022-04-28). SQL state: 23514

Checking the official documentation of PostgreSQL I found out that I might first need to create a child table that will include the partition of the parent table like:

CREATE TABLE TBD_partitioning_table_20220428 PARTITION OF TBD_partitioning_table
    FOR VALUES FROM ('2022-04-28') TO ('2022-04-28');

Given that I will insert-append the parent table once per day with the current date, is it efficient to create a different child table each day to hold the date loaded?

Moreover, since I am not aware of an automated way to create generated table names from functions in postgreSQL, creating a different table like CREATE TABLE TBD_partitioning_table_{current_date::date} is not quite possible.

CodePudding user response:

You can create a table with variable name in a function if you use dynamic SQL with EXECUTE.

Yes, you need to create a partition before you insert data for that partition. About partitioning per day, I would consider how long the data should be retained. You should not have too many partitions, otherwise query planning will become quite slow.

CodePudding user response:

CREATE TABLE TBD_partitioning_table_20220428 PARTITION OF TBD_partitioning_table
    FOR VALUES FROM ('2022-04-28') TO ('2022-04-28');

Will not work. because it's empty range.
You can use list partition.
Demo

CREATE TABLE IF NOT EXISTS TBD_partitioning_table (
    shop text,
    region text,
    sale_date date not null
) PARTITION BY list(sale_date);

script for generating partitions.

CREATE OR REPLACE PROCEDURE generate_partitioned_tables (_begindate date, _enddate date)
    AS $$
DECLARE
    _sql text;
    loopdate date DEFAULT _begindate;
BEGIN
    IF _begindate > _enddate THEN
        RAISE EXCEPTION '_begindate should smaller than _end_date';
    END IF;
    IF _begindate is null or _enddate is null THEN
        raise exception '_begindate and _enddate cannot be null';
    end if;
    while loopdate <= _enddate LOOP
        _sql := 'CREATE TABLE TBD_partitioning_table_'
                    || to_char(loopdate, 'YYYYMMDD')
                    || ' partition of TBD_partitioning_table for values in ( '''
                    || loopdate || ''')';
        RAISE NOTICE '_sql:  %', _sql;
        EXECUTE _sql;
        SELECT
            (loopdate   interval '1 day') INTO loopdate;
    END LOOP;
END
$$
LANGUAGE plpgsql;
  • Related