Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sPlSA-006El1-KW for pgsql-general@arkaria.postgresql.org; Fri, 05 Jul 2024 16:06:30 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1sPlS8-00BQ7F-IQ for pgsql-general@arkaria.postgresql.org; Fri, 05 Jul 2024 16:06:29 +0000 Received: from makus.postgresql.org ([72.32.157.229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sPlS8-00BQ77-2n for pgsql-general@lists.postgresql.org; Fri, 05 Jul 2024 16:06:28 +0000 Received: from mail-oa1-x29.google.com ([2001:4860:4864:20::29]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1sPlS5-000Yiq-Cq for pgsql-general@lists.postgresql.org; Fri, 05 Jul 2024 16:06:27 +0000 Received: by mail-oa1-x29.google.com with SMTP id 586e51a60fabf-25e3bc751daso669496fac.3 for ; Fri, 05 Jul 2024 09:06:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1720195584; x=1720800384; darn=lists.postgresql.org; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=NcloznqCROimIUIhg66JyDiu/sR2/1m3mF8VsBmx0iw=; b=VeS5plD6kYtk4qwZOBop+cjHE6BJSKxWK+Qx17g+eThALxJ+6u80aUidSnBtWv62P2 qFctcxONpkj6t3pCTnJG0onsH1Vw7gD2/mjZhII/rGYK76zA9EwPNs192O/kDv1+7zk6 2/5Vn8mv4OH2ZnBit8j43Sqrf9BQHvu98Y7/9U6hAuovQydtuP2EKVBOwuEDDk/FfQDI I7MmHVjGZzZIVn8j6pTGQrkG23iM21n/N0RUvAyLNyZRi2LEg6gravhP98EmefNQJxPC B3llEVNdvnxLr1FJBpwISFjQG9H0R4bYjzj9oBM+X9HvdqAnYAACJDYw6sp2FxRqCjlD hJGw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720195584; x=1720800384; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=NcloznqCROimIUIhg66JyDiu/sR2/1m3mF8VsBmx0iw=; b=YkUy0LCgEAxl+O9U3JqR0ys3lmUvtAgxZEbI4YkMfkfUm5THTYOqq0r3IUVDIyTzOD JzVWSAciEp56lda8Pzogtk+C1ZVhqI3Y6VTr9SvjarE/zFaqWHJ5Z+VEUOpTc0rC8JlZ he7JKmeVTA93ZvnXuBN2CtenFaVYZCJzZUzQr9aCBZpOfxC8o8dniWg6ypyQJTWj9RuW eGsxDVe7AIF99rcHN8+yIO6CbZnyJjgqjfoldk2lKl0hLnwAmrJ1gTB7O4x6PFWUtlqZ yggbtaIBtzho0tHwwdvKBW108bWGbbsmMiWuS9nNCQP8zFhsKxd0Za/TDF017XfevmN/ 1sSg== X-Gm-Message-State: AOJu0YyXWF9MNYMoHjrvDQf72IgJPdfv4AzJYm4t68h6+76/Isvz1b3y w/zyUvgWP6q2VCyauUAi3gKPT3CEmwybnmpVKQsdWxb5P9ZhXqcg4O31vUvvjLbF8L73QWbLQA8 3QqYmr9xD2K4wAROW83NjwZaEGj1GuOL1 X-Google-Smtp-Source: AGHT+IExxhNP1+J6G/S3Wlv2X5MRPMChFPLITlIpM4w8TLbSU90WQTs595ivbuECKdKzVbaE/gRyTKZyN8tW63dlrjw= X-Received: by 2002:a05:6870:d889:b0:25e:1885:3f71 with SMTP id 586e51a60fabf-25e2bf7b6ccmr4282081fac.58.1720195584319; Fri, 05 Jul 2024 09:06:24 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Matt Hughes Date: Fri, 5 Jul 2024 12:06:13 -0400 Message-ID: Subject: Re: Planning of sub partitions To: pgsql-general@lists.postgresql.org Content-Type: multipart/alternative; boundary="000000000000bf67ef061c823e6c" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000bf67ef061c823e6c Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable One workaround seems to be using a CTE with a union: with openAndRecentlyRaisedEvents as ( select * from event where cleared is false union all select * from event where cleared is true and date_raised > '2024-01-01' AND date_raised < '2024-01-02'; ) select * from openAndREcentlyRaisedEvents On Fri, Jul 5, 2024 at 11:24=E2=80=AFAM Matt Hughes = wrote: > I am trying to model a stateful `event` table using partitions. An event > has date_raised (not null) and date_cleared (nullable) columns. An event > is in the "open" state when it has a null date_cleared; it is in the > "closed" state when date_cleared is set. Once date_cleared is set, it > won't change. > > While most events close after a short period of time, a few stragglers > stay open for weeks or months. I'm trying to optimize my system for the > following things: > > - I need to drop events older than N days; I want to use partitions so I > can just drop the table rather than an expensive DELETE > - Users want to see all open events OR closed events within the past N da= ys > > To do this, I came up with the following schema: > > create table event ( > id uuid not null, > cleared boolean not null, > date_raised timestamp without time zone not null, > date_cleared timestamp without time zone, > primary key (id, date_raised, cleared) > ) PARTITION BY LIST (cleared); > > CREATE TABLE event_open PARTITION OF event FOR VALUES IN (false); > CREATE TABLE event_closed PARTITION OF event FOR VALUES IN (true) > partition by range(date_raised); > CREATE TABLE event_closed_y2024_m01 PARTITION OF event_closed FOR VALUES > FROM ('2024-01-01') to ('2024-02-01'); > CREATE TABLE event_closed_y2024_m02 PARTITION OF event_closed FOR VALUES > FROM ('2024-02-01') to ('2024-03-01'); > > This works for the most part but the plan for my compound query does not > behave as I would expect: > > -- 1. correctly only picks event_open partition > explain select * from event where cleared is false; > > -- 2. correctly picks all event_closed_* partitions > explain select * from event where cleared is true; > > -- 3. correctly picks just the event_closed_y2024_m01 partition > explain select * from event where cleared is true and date_raised > > '2024-01-01' AND date_raised < '2024-01-02'; > > -- 4. uses all partitions; should exclude event_closed_y2024_m02 > explain select * from event > where > cleared is false OR > (cleared is true and date_raised > '2024-01-01' AND date_raised < > '2024-01-02'); > > QUERY PLAN > > -------------------------------------------------------------------------= ---------------------------------------------------------------------------= --------------------------------- > Append (cost=3D0.00..110.91 rows=3D2322 width=3D25) > -> Seq Scan on event_open event_1 (cost=3D0.00..33.10 rows=3D774 wid= th=3D25) > Filter: ((cleared IS FALSE) OR ((date_raised > '2024-01-01 > 00:00:00'::timestamp without time zone) AND (date_raised < '2024-01-02 > 00:00:00'::timestamp without time zone))) > -> Seq Scan on event_closed_y2024_m01 event_2 (cost=3D0.00..33.10 > rows=3D774 width=3D25) > Filter: ((cleared IS FALSE) OR ((date_raised > '2024-01-01 > 00:00:00'::timestamp without time zone) AND (date_raised < '2024-01-02 > 00:00:00'::timestamp without time zone))) > -> Seq Scan on event_closed_y2024_m02 event_3 (cost=3D0.00..33.10 > rows=3D774 width=3D25) > Filter: ((cleared IS FALSE) OR ((date_raised > '2024-01-01 > 00:00:00'::timestamp without time zone) AND (date_raised < '2024-01-02 > 00:00:00'::timestamp without time zone))) > > > Is this a bug? Query 4 is just a union of queries 2/3 which pick the > right partitions. Do you see anything else wrong with this approach? > > Note that `closed` column is somewhat of a hack as it is completely > computeable from `date_cleared is not null`. However, as I understand it= , > partitions can only be declared on values that are part of the primary > key. > > --000000000000bf67ef061c823e6c Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
One workaround seems to be using a CTE with a union:<= /div>

with openAndRecentlyRaisedEvents as (
= =C2=A0 select * from event where cleared is false
=C2=A0 union al= l
=C2=A0 select * from event
=C2=A0 where cleared is tr= ue and date_raised > '2024-01-01' AND date_raised < '2024= -01-02';
)
select * from openAndREcentlyRaisedEvent= s



<= div dir=3D"ltr" class=3D"gmail_attr">On Fri, Jul 5, 2024 at 11:24=E2=80=AFA= M Matt Hughes <hughes.matt@gmai= l.com> wrote:
I am trying to model a stateful `event` table us= ing partitions.=C2=A0 An event has date_raised (not null) and date_cleared = (nullable) columns.=C2=A0 An event is in the "open" state when it= has a null date_cleared; it is in the "closed" state when date_c= leared is set.=C2=A0 Once date_cleared is set, it won't change.

While most events close after a short period of time, a f= ew stragglers stay open for weeks or months.=C2=A0 I'm trying to optimi= ze my system for the following things:

- I need to= drop events older than N days; I want to use partitions so I can just drop= the table rather than an expensive DELETE
- Users want to see al= l open events OR closed events within the past N days

<= div>To do this, I came up with the following schema:

create table event (
=C2=A0 id uuid not null,
=C2=A0 cleared bool= ean not null,
=C2=A0 date_raised timestamp without time zone not null,=C2=A0 date_cleared timestamp without time zone,
=C2=A0 primary key (i= d, date_raised, cleared)
) PARTITION BY LIST (cleared);

CREATE T= ABLE event_open PARTITION OF event FOR VALUES IN (false);
CREATE TABLE e= vent_closed PARTITION OF event FOR VALUES IN (true) partition by range(date= _raised);
CREATE TABLE event_closed_y2024_m01 PARTITION OF event_closed = FOR VALUES FROM ('2024-01-01') to ('2024-02-01');
CREATE= TABLE event_closed_y2024_m02 PARTITION OF event_closed FOR VALUES FROM (&#= 39;2024-02-01') to ('2024-03-01');

Thi= s works for the most part but the plan for my compound query does not behav= e as I would expect:

-- 1. correctly only picks ev= ent_open partition
explain select * from event where cleared is false;
-- 2. correctly picks all event_closed_* partitions
explain select= * from event where cleared is true;

-- 3. correctly picks just the = event_closed_y2024_m01 partition =C2=A0
explain select * from event wher= e cleared is true and date_raised > '2024-01-01' AND date_raised= < '2024-01-02';

-- 4. uses all partitions; should exclud= e event_closed_y2024_m02
explain select * from event
where
=C2= =A0cleared is false OR
=C2=A0(cleared is true and date_raised > '= ;2024-01-01' AND date_raised < '2024-01-02');
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 QUERY = PLAN
-------------------------------------------------------------------= ---------------------------------------------------------------------------= ---------------------------------------
=C2=A0Append =C2=A0(cost=3D0.00.= .110.91 rows=3D2322 width=3D25)
=C2=A0 =C2=A0-> =C2=A0Seq Scan on eve= nt_open event_1 =C2=A0(cost=3D0.00..33.10 rows=3D774 width=3D25)
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0Filter: ((cleared IS FALSE) OR ((date_raised >= ; '2024-01-01 00:00:00'::timestamp without time zone) AND (date_rai= sed < '2024-01-02 00:00:00'::timestamp without time zone)))
= =C2=A0 =C2=A0-> =C2=A0Seq Scan on event_closed_y2024_m01 event_2 =C2=A0(= cost=3D0.00..33.10 rows=3D774 width=3D25)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0Filter: ((cleared IS FALSE) OR ((date_raised > '2024-01-01 00:= 00:00'::timestamp without time zone) AND (date_raised < '2024-01= -02 00:00:00'::timestamp without time zone)))
=C2=A0 =C2=A0-> =C2= =A0Seq Scan on event_closed_y2024_m02 event_3 =C2=A0(cost=3D0.00..33.10 row= s=3D774 width=3D25)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Filter: ((cleared = IS FALSE) OR ((date_raised > '2024-01-01 00:00:00'::timestamp wi= thout time zone) AND (date_raised < '2024-01-02 00:00:00'::times= tamp without time zone)))


Is this a= bug?=C2=A0 Query 4 is just a union of queries 2/3 which pick the right par= titions.=C2=A0 Do you see anything else wrong with this approach?=C2=A0=C2= =A0

Note that `closed` column is somewhat of a hac= k as it is completely computeable from `date_cleared is not null`.=C2=A0 Ho= wever, as I understand it, partitions can only be declared on values that a= re part of the primary key.=C2=A0

--000000000000bf67ef061c823e6c--