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 1sPknh-006BIG-0g for pgsql-general@arkaria.postgresql.org; Fri, 05 Jul 2024 15:24:41 +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 1sPknf-00B0Pc-1R for pgsql-general@arkaria.postgresql.org; Fri, 05 Jul 2024 15:24:39 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sPkne-00B0PT-LK for pgsql-general@lists.postgresql.org; Fri, 05 Jul 2024 15:24:39 +0000 Received: from mail-ot1-x32a.google.com ([2607:f8b0:4864:20::32a]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1sPknd-000ZhW-Cq for pgsql-general@lists.postgresql.org; Fri, 05 Jul 2024 15:24:38 +0000 Received: by mail-ot1-x32a.google.com with SMTP id 46e09a7af769-700d29bb4e2so940859a34.1 for ; Fri, 05 Jul 2024 08:24:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1720193075; x=1720797875; darn=lists.postgresql.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=0kwag0p6dscnzT1EmKnuZw9S2svoood3sO1aWNJA5Qg=; b=bdAQgrgtp9JXh6H+MZ05B1ho9oSRufg6zRVoZXodmIjOEmxZNJoU2BewjSvqQzaG6b bgtVZ97z5uKSA1wCiPDfa3jodyoksW6LqcSNhK/4bKFsNQ7anayj23D5WcKHMj+fXRhe /aqTRA8u5IaM/BxMQNzV2s48Sp9pU/pK+y9izwVq3ZyEFpR7KpDFeRVoOER8H+V4cYFc 2tsMqc2MtCy1h0zbdGUN85wMCnrnL70Ya8o6N9BKlyYkvBgjyDOef81nTlu62PYihf/m 50Idxk3l2Mhp59htxskXitxqhQhbDkQm1ryun2jhlg0pw3pflklIHfz26nrcL35nzFcn mtcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720193075; x=1720797875; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=0kwag0p6dscnzT1EmKnuZw9S2svoood3sO1aWNJA5Qg=; b=BtUT4nbkjDxvwKQKCIK5jcPcFVJqAZyiiJxaEDrmsak+9AFujFJ7YQcso6UCqwS+Dq qe2K4Sdkg80jwZEfEBmebOvoZKEgV7JydKClLN97SlJvxUXkGN4QttoANvf8IWIw7BhE 3PFX6LFGZC4w4qzryY5AoWycFcW8onW4NctEaKQU+RGCSCQx31Usdjb4T9EKIY1I2C0I 8fU93jJUoj6jkdECSAe3nfHNgb8/+oFXv7yp51KDHTowRmAnl1ec4xBx52W3zTmrcxgC lC0vGjtxHa7VkaYj6nJemGS2URN84EwX0NDjYcr6+36ABnbwcrK5ZfWvAx/2n6M0ygIW IR+A== X-Gm-Message-State: AOJu0Yydg7SJHCACIV2yx2H/HCao7uIC0eSWj00DHWuNgNdKJkY1CcUL p0DrhQSM/4v0Bic85VO8pWtpewR4G2H58iohTFBT5FWoNDz/Pq6KFLFpDNSKkIB0LOXacGKYq8k crauUHAG1BVqDa3+5/thOzTo78NYKwQ== X-Google-Smtp-Source: AGHT+IE5EVG/BzeqFU2cmPn1ACuwa+hA+UR+GCdE8sOysupiIQRNcql0Kb7hrm3whXigFf/LvcoqPU9x70TDbtclIF4= X-Received: by 2002:a05:6870:2216:b0:25e:1688:ddd7 with SMTP id 586e51a60fabf-25e2bf3eac6mr3551843fac.58.1720193074879; Fri, 05 Jul 2024 08:24:34 -0700 (PDT) MIME-Version: 1.0 From: Matt Hughes Date: Fri, 5 Jul 2024 11:24:23 -0400 Message-ID: Subject: Planning of sub partitions To: pgsql-general@lists.postgresql.org Content-Type: multipart/alternative; boundary="0000000000002c6515061c81a937" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000002c6515061c81a937 Content-Type: text/plain; charset="UTF-8" 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 days 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=0.00..110.91 rows=2322 width=25) -> Seq Scan on event_open event_1 (cost=0.00..33.10 rows=774 width=25) 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=0.00..33.10 rows=774 width=25) 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=0.00..33.10 rows=774 width=25) 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. --0000000000002c6515061c81a937 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I am trying to model a stateful `event` table using p= artitions.=C2=A0 An event has date_raised (not null) and date_cleared (null= able) 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_cleare= d is set.=C2=A0 Once date_cleared is set, it won't change.
While most events close after a short period of time, a few st= ragglers stay open for weeks or months.=C2=A0 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 ope= n events OR closed events within the past N days

T= o do this, I came up with the following schema:

cr= eate table event (
=C2=A0 id uuid not null,
=C2=A0 cleared boolean n= ot null,
=C2=A0 date_raised timestamp without time zone not null,
=C2= =A0 date_cleared timestamp without time zone,
=C2=A0 primary key (id, da= te_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_rais= ed);
CREATE TABLE event_closed_y2024_m01 PARTITION OF event_closed FOR V= ALUES FROM ('2024-01-01') to ('2024-02-01');
CREATE TABL= E event_closed_y2024_m02 PARTITION OF event_closed FOR VALUES FROM ('20= 24-02-01') to ('2024-03-01');

This wor= ks for the most part but the plan for my compound query does not behave as = I would expect:

-- 1. correctly only picks event_o= pen partition
explain select * from event where cleared is false;
-- 2. correctly picks all event_closed_* partitions
explain select * fr= om event where cleared is true;

-- 3. correctly picks just the event= _closed_y2024_m01 partition =C2=A0
explain select * from event where cle= ared is true and date_raised > '2024-01-01' AND date_raised <= '2024-01-02';

-- 4. uses all partitions; should exclude eve= nt_closed_y2024_m02
explain select * from event
where
=C2=A0cle= ared 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 event_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_raised <= ; '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=3D= 0.00..33.10 rows=3D774 width=3D25)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Fil= ter: ((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 Sc= an on event_closed_y2024_m02 event_3 =C2=A0(cost=3D0.00..33.10 rows=3D774 w= idth=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 tim= e zone) AND (date_raised < '2024-01-02 00:00:00'::timestamp with= out time zone)))


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

Note that `closed` column is somewhat of a hack as it i= s completely computeable from `date_cleared is not null`.=C2=A0 However, as= I understand it, partitions can only be declared on values that are part o= f the primary key.=C2=A0

--0000000000002c6515061c81a937--