2

I have a PostgreSQL table which I am trying to convert to a TimescaleDB hypertable.

The table looks as follows:

CREATE TABLE public.data
(
    event_time timestamp with time zone NOT NULL,
    pair_id integer NOT NULL,
    entry_id bigint NOT NULL,
    event_data int NOT NULL,
    CONSTRAINT con1 UNIQUE (pair_id, entry_id ),
    CONSTRAINT pair_id_fkey FOREIGN KEY (pair_id)
        REFERENCES public.pairs (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
)

When I attempt to convert this table to a TimescaleDB hypertable using the following command:

SELECT create_hypertable(
        'data',
        'event_time',
        chunk_time_interval => INTERVAL '1 hour',
        migrate_data => TRUE
);

I get the Error: ERROR: cannot create a unique index without the column "event_time" (used in partitioning)

Question 1: From this post https://stackoverflow.com/questions/55312365/how-to-convert-a-simple-postgresql-table-to-hypertable-or-timescale-db-table-usi my understanding is that this is because I have specified a unique constraint (con1) which does not contain the column I am partitioning by - event_time. Is that correct?

Question 2: How should I change my table or hypertable to be able to convert this? I have added some data on how I plan to use the data and the structure of the data bellow.

Data Properties and usage:

  • There can be multiple entries with the same event_time - those entries would have entry_id's which are in sequence
    • This means that if I have 2 entries (event_time 2021-05-18::10:16, id 105, <some_data>) and (event_time 2021-05-18::10:16, id 107, <some_data>) then the entry with id 106 would also have event_time 2021-05-18::10:16
  • The entry_id is not generated by me and I use the unique constraint con1 to ensure that I am not inserting duplicate data
  • I will query the data mainly on event_time e.g. to create plots and perform other analysis
  • At this point the database contains around 4.6 Billion rows but should contain many more soon
  • I would like to take advantage of TimescaleDB's speed and good compression
  • I don't care too much about insert performance

Solutions I have been considering:

  • Pack all the events which have the same timestamp in to an array somehow and keep them in one row. I think this would have downsides on compression and provide less flexibility on querying the data. Also I would probably end up having to unpack the data on each query.
  • Remove the unique constraint con1 - then how do I ensure that I don't add the same row twice?
  • Expand unique constraint con1 to include event_time - would that not somehow decrease performance while at the same time open up for the error where I accidentally insert 2 rows with entry_id and pair_id but different event_time? (I doubt this is a likely thing to happen though)

2 Answers 2

3

A couple questions as well as answers here:

  1. it's the unique constraint (con1) that's the problem not the foreign key constraint but that was probably a typo.
  2. Yes, it's a problem because the time partitioning column isn't included, the first question I have is, the entry_id seems like it might be a proxy for time from the other system, can you just use pair_id, time as your unique constraint? Will there be multiple records with different entry_ids same pair_ids and same times? You could probably run a query with a count(*) grouped by pair_id, time to figure this out... Note that a three column unique constraint could work, it's a question of how the time is assigned, is it assigned by you at write time or by the upstream system? If it's the upstream system then I wouldn't worry too much about duplicates.
  3. the array thing is somewhat similar to compression, but the compression does different, more efficient algorithms than simple array packing.
  4. figuring out a unique constraint that works, either on pair_id, time or pair_id, entry_id, time will probably be much more efficient than trying to reduce your data to the unique set at query time. And a three column unique constraint isn't going to have a huge impact on performance. The ordering of those columns should be chosen based on query patterns, what sorts of where clauses do you usually have? ie on pair id or something like that as well as time? or just by time?
  5. finally, given the size of your table, I'd highly recommend creating the hypertable without migrating data, so create a separate table, make it a hypertable, then make a small job to migrate the data. The migrate_data = true is really meant for much smaller tables, having an outside job do it means you could also introduce some parallelism which will make it go much faster.
5
  • 1.) fixed 2.) There are events which happen 'by definition' simultaneously on the upstream system and thus have the same event_time which makes the entry_id necessary as an identifier 3.) makes sense 4.) As I am quite a novice on this it's hard for me to tell - most likely the queries will look like: Select * From ... Where time=<some time range> and pair_id=<one or a list of pairs> 5.) I assume I would then follow these instructions? docs.timescale.com/timescaledb/latest/how-to-guides/…
    – sev
    Commented May 18, 2021 at 14:35
  • I will also likely create continuous aggregates and query these very frequently based on time
    – sev
    Commented May 18, 2021 at 14:41
  • I understand that they have the same event time, but do they have the same event time and pair_id?
    – David K
    Commented May 18, 2021 at 23:45
  • There exist entries which have the same event_time, and pair_id but a different entry_id
    – sev
    Commented May 19, 2021 at 0:43
  • I think your point number 4 is basically the answer so I create a constraint which is (event_time, pair_id, entry_id)
    – sev
    Commented May 19, 2021 at 9:22
2

my understanding is that this is because I have specified a unique constraint (pair_id_fkey) which does not contain the column I am partitioning by - event_time. Is that correct?

Yes.

Question 2: How should I change my table or hypertable to be able to convert this? I have added some data on how I plan to use the data and the structure of the data below.

I think you can keep the structure by removing the constraints. You can use avg, last, or first combined with some time_bucket.

Pack all the events which have the same timestamp into an array somehow and keep them in one row.

It's what Timescale compression is doing behind the scenes, check this.

Remove the unique constraint con1 - then how do I ensure that I don't add the same row twice?

You can probably use last or first to avoid duplicates as I mentioned before.

Expand unique constraint con1 to include event_time - would that not somehow decrease performance while at the same time open up for the error where I accidentally insert 2 rows with entry_id and pair_id but different event_time? (I doubt this is a likely thing to happen though)

It's faster to keep the first or last from your query and avoid the constraint. But if performance is not an issue, I think it will work. You can combine time, entry_id, and pair_id too as constraints accept multiple columns.

1
  • I'm still fairly new to SQL and TimescaleDB - would you mind giving me an example of how I can use first or last together with a timebucket to ensure uniqueness in my case?
    – sev
    Commented May 18, 2021 at 13:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.