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 1tnIzV-0044ch-2h for pgsql-performance@arkaria.postgresql.org; Wed, 26 Feb 2025 15:06:29 +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 1tnIzT-007g0E-HC for pgsql-performance@arkaria.postgresql.org; Wed, 26 Feb 2025 15:06:27 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::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 1tnIzT-007g06-2b for pgsql-performance@lists.postgresql.org; Wed, 26 Feb 2025 15:06:27 +0000 Received: from mail-wr1-x42b.google.com ([2a00:1450:4864:20::42b]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1tnIzP-000Crv-1L for pgsql-performance@lists.postgresql.org; Wed, 26 Feb 2025 15:06:26 +0000 Received: by mail-wr1-x42b.google.com with SMTP id ffacd0b85a97d-390cf7458f5so1909747f8f.2 for ; Wed, 26 Feb 2025 07:06:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bowt-ie.20230601.gappssmtp.com; s=20230601; t=1740582383; x=1741187183; darn=lists.postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=8Cwws6TdwdNvOOuK9ws1HRLB7kpIJu0iyAUvbJt+0R8=; b=P4u2a2qkdslEyZsFqGozwhTCcfy6CvjhRXfA/vTIoKSp9VqSTBPDnMmjPpJbU3eddw vLI1Uk18sh8hMlBLTofIUpb6DDuW5VifOCq0lZVEv11NGr++esdEiMbJFWIXijiXjhI2 32dHtcknhWl9PvyftMO2aXASkL8ADxNTer6wMncBYqeovQwVD/k+zq+zAivtpd5yXKAs HK6npxr2+dGdqpKgF335F97daacmEsr/84ZGxjw0YeL7cUT0DOcDc4bq9ZQ3cZIQyS8L ADiyiWy5NAriWXERgRfopCEiPrS3mkNyxSzXYYKjmgxaoI6k5WwaH6Ck74HHXoaD5Ud/ +6Ng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740582383; x=1741187183; h=content-transfer-encoding:cc: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=8Cwws6TdwdNvOOuK9ws1HRLB7kpIJu0iyAUvbJt+0R8=; b=ix08Ztz8BkmUzWrU5T/LWxiyyKlOwYGnkv2ZMBLRx3X+JXw2Bc63RwuawpuqtoRwBA MkPXA7+rWlM/FL1HxdoddTD5hGyBMQoiLKbaIhrsYEbHf3GDk6lMTQRqRZtGp2K0Cx9M HZoWBx7owtJN13Eyrf9JsJLValGklM1Ym3Jbj4sg3SmSMRUpFoW+YEsi6UBCU4q9Y8eb DyTd+yWeLDQtTtnRqUzW3HppCqWHM75rGXxPNqDBJ8s0cUdTlhN6LZEKbwXMbIjcgHpH KHNnrv/oqoE4+rGVgoADxmJgDYUWNHFW4v9mpAFBpA1sqF1xbhd3fi9nIX5qg2cuYm3Y YYIg== X-Gm-Message-State: AOJu0YyK3fXrnl9TjoB0EntFMA1sT3Zib+8r+/9fVTBpVqYPlDgb7PCF MZg/oEAULh+CY+qqQtuRcS9mRaR4c83XwXKJ1vz8btz1rN64eQNSKqMDl1PJu1hS34afvaibyIz zADM2LW9ZPaOnz1pAltyc+tPnZS+zuniwqoEpoEy5aiX4eOkLJ5A= X-Gm-Gg: ASbGncs/NF1ZTd5aBwshHv76c/nxxp/QAG1Xh+OfPi9yRvSbQif71HJd0aUWtPR41kT /IVIAQHibF+UmMy/auO8pNRttJ40eOKMFo83ok6psP7p/8fG4ua/Z0rLdxiLdJDsFMP0MAk0q7l XqjsthoV8= X-Google-Smtp-Source: AGHT+IFdNC00DJzDFPEEH2zYqcq8nmafr0Icl/idoy+dT+ojw9p7GOJRw7ev3zFOy5QxiTCePQ8avYbRDSRGg+xudAs= X-Received: by 2002:a5d:6d8f:0:b0:38d:d8fb:e91a with SMTP id ffacd0b85a97d-390cc60a7c9mr8311086f8f.27.1740582381066; Wed, 26 Feb 2025 07:06:21 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Peter Geoghegan Date: Wed, 26 Feb 2025 10:05:52 -0500 X-Gm-Features: AQ5f1JoGuVo0msnaMjZ5RSYCzUMGU4zwb8j38l8ylDFQeyGKrUXmf65T0csLah8 Message-ID: Subject: Re: Efficient pagination using multi-column cursors To: large.goose2829@salomvary.com Cc: pgsql-performance@lists.postgresql.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Wed, Feb 26, 2025 at 9:29=E2=80=AFAM wro= te: > Without being familiar the internals of the query planner, I *think* ther= e *should* be a way to come up with WHERE conditions that results in the "p= erfect" plan. There is a fundamental trade-off involved here. The simple, fast "WHERE (col_1, col_2, col_3) > (10, 20, 29)" query returns whatever tuples are stored immediately after "(10, 20, 29)" in the index. Naturally, they're returned in index order, which is usually the most useful order (simple ASC order or simple DESC order for all columns). The B-Tree code can physically traverse your mixed-ASC-and-DESC order index in almost the same way. But it is much less useful, since the matching index tuples won't be physically located together as exactly one contiguous group of tuples. And so (with your "handwritten" row comparison) you get a filter qual that filters out non-matching tuples using lower-order index columns. The index scan actually just returns "Index Cond: (col_1 >=3D 10)" (which still returns a contiguous group of index tuples), while a filter condition excludes those tuples returned by the index scan node that don't satisfy the later/lower-order column condition. The book "Relational Database Index Design and the Optimizers" proposes a vocabulary for the trade-offs in this area -- the 3 star model. When creating the best possible index for certain queries it is sometimes inherently necessary to choose between what it calls the first star (which means avoiding a sort) and the second star (which means having the thinnest possible "row slice"). Sometimes those things are in tension, which makes sense when you imagine how the index must be physically traversed. --=20 Peter Geoghegan