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 1us27u-00GRAZ-Rv for pgsql-performance@arkaria.postgresql.org; Fri, 29 Aug 2025 16:39:00 +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 1us27u-00B9kH-7M for pgsql-performance@arkaria.postgresql.org; Fri, 29 Aug 2025 16:38:58 +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 1us27t-00B9k2-T9 for pgsql-performance@lists.postgresql.org; Fri, 29 Aug 2025 16:38:58 +0000 Received: from mail-ej1-x631.google.com ([2a00:1450:4864:20::631]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1us27s-002XJr-09 for pgsql-performance@postgresql.org; Fri, 29 Aug 2025 16:38:57 +0000 Received: by mail-ej1-x631.google.com with SMTP id a640c23a62f3a-afebb6d4093so376738866b.1 for ; Fri, 29 Aug 2025 09:38:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756485534; x=1757090334; darn=postgresql.org; h=mime-version:message-id:date:user-agent:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=euHED38cc9lTsklyXLugAhO2nX8twgAzEvoQKxY5xJ0=; b=HlaWnZSdH1RvnJH0w75lXMP/iXbvo5qeO15Bzn/MFeZGLuZTJjnpzI99h6Np7VfqPJ oWrpcFRXQ2YdzsTdjbyTN8vU1XjxfYFeW6dfGAMhUw0hQFUM3BU//0zLftYLCwyW6Mpe vvQVOEffGyFkpWuYCWLCQhdPS+AMZi693BzN0hqdTNOmnBfTg8WZYKI5QNEhZ4RVQFda C+Bil6GYya1V1ss4BYdolXCw3OpMqbGVT5DrXDkntpSMtuy1NtE0QFlvkISYmnaYRb77 IvUgNlzRcFVtTwwCgkPzZWtArQcHJNbuEqpFhVHrWmAgfioa9jz2ygzr28WJddWzwYtN 3V8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756485534; x=1757090334; h=mime-version:message-id:date:user-agent:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=euHED38cc9lTsklyXLugAhO2nX8twgAzEvoQKxY5xJ0=; b=HuXyuwNp+MX4T6sD3NTXgHlynQLJ14RY/Qf2MF1FnEyNd7QtNgcpAsSRcVikUsLkTZ csMUqyXARtsiZjIm2ZpTyEVFitN94iNS1MO0+bm2/V93q1O3RJInk7opaF0JyLCgdGLc hxJ0Mz7Bsn0ozpQeAni0DTErb+Y6zIQE84znOEHJhOT6HwCN1z5iTKdtb/tiAvNoEooA k14utAce/pmfoeDgYP92ksN8X5blZcQnmBUGUj0P4Fz44Jwibi0Mh+ZjDI/X+PnT5T7y fAEp943dVlS1nVCjyoNWAOkR1jI2HfGaja69ahUEFZl0TAk/WCNYo2b5dsJ0qvVntc3o /vvg== X-Gm-Message-State: AOJu0YyqKH/GSa6tExGY5FndP/SMkfNoNhu4egtQpOtrsearjzGLbOw0 lVgyNO69nq+ZvHbK6dYecjHfu0TPNHdn6gJWOJMFAW52VrjHvvo+lR+5OSSSgmie X-Gm-Gg: ASbGnctBkTcWZIk87p2YgEh4tOnfosCHq0GpTkQKke8QkHS0YL8lCdKTjjpO7sqX8yF hp8zVMXtaaiYljQM25XLQUQx29603Z8Jgoqxj9X/7FvxcNY1Utb0opzFzj3NX5VBgo7SnxLlIRR XWT82OW+tTvUcCmgBuF0tHqtXtffzbubl+eYFZUp9mvQq3Ju38h/DKy3+FIGi3ZC6t5gYkBNQlo gMeLXMXCCo5kjYitrYOyuHmdqfiCA7exTDz+3TC4vP2wll7ZppWiL6/NIsybAL0PsbEyCBfjYAy sWlZFIOw6jurlZV3ZXwYdif1cyfDuKfkumQZlz+oAlZeNuqCINE7sG82FCWwKthGAlbOHk1Cf4T hOsgSeQgydWtDE+WKXCcEeAFkYQ== X-Google-Smtp-Source: AGHT+IFwMX7r73zJ5Rr/MJqe7b0bK8EUz70FG9LrfB37eoqrcAmWoIFJLAj3D+m8QDpJaP5/kXIzlg== X-Received: by 2002:a17:907:a49:b0:afe:ef48:ee44 with SMTP id a640c23a62f3a-afeef48f4c3mr549749266b.51.1756485533788; Fri, 29 Aug 2025 09:38:53 -0700 (PDT) Received: from small-boat ([193.207.100.184]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-afefca40da0sm242073466b.46.2025.08.29.09.38.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Aug 2025 09:38:53 -0700 (PDT) From: Renan Alves Fonseca To: Clemens Eisserer Cc: pgsql-performance@postgresql.org Subject: Re: Any way to get nested loop index joins on CTEs? In-Reply-To: (Clemens Eisserer's message of "Tue, 15 Jul 2025 15:29:27 +0200") References: User-Agent: mu4e 1.12.1; emacs 29.4 Date: Fri, 29 Aug 2025 18:38:50 +0200 Message-ID: <87seharskl.fsf@gmail.com> MIME-Version: 1.0 Content-Type: text/plain List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, it is definitively possible to get nested loop joins on successively aggregated CTEs. However, for the index to be used, it must exist. And you can only create the index on a real table, not on the intermediate CTEs. > WITH series1h AS MATERIALIZED (SELECT generate_series AS ts FROM > generate_series('1990-01-01 00:00', '1990-12-31 23:59', INTERVAL '1 > hour') ORDER BY ts), > series15m AS MATERIALIZED (SELECT generate_series AS ts FROM > generate_series('1990-01-01 00:00', '1990-12-31 23:59', INTERVAL '15 > minutes') ORDER BY ts) > SELECT count(*) FROM (SELECT h1.ts, count(*) FROM series1h h1 JOIN > series15m m15 ON (m15.ts > (h1.ts - INTERVAL '1 hour') AND m15.ts <= > h1.ts ) GROUP BY h1.ts ORDER BY h1.ts); Here is an example based on the query above: CREATE TEMP TABLE series15m AS (SELECT generate_series('1990-01-01 00:00', '1990-12-31 23:59', INTERVAL '15 minutes')::timestamp as ts, random() as your_data ORDER BY 1); CREATE INDEX short_period ON series15m (ts); CREATE INDEX long_period ON series15m (date_trunc('hour',ts)); EXPLAIN (costs 'off') WITH series1h AS (SELECT date_trunc('hour',ts) as ts FROM series15m) SELECT h1.ts, sum(your_data) FROM series1h h1 JOIN series15m m15 ON (m15.ts >(h1.ts - INTERVAL '1 hour') AND m15.ts <= h1.ts ) GROUP BY h1.ts ORDER BY h1.ts ; QUERY PLAN ------------------------------------------------------------------- GroupAggregate Group Key: date_trunc('hour'::text, series15m.ts) -> Nested Loop -> Index Scan using long_period on series15m -> Index Scan using short_period on series15m m15 Index Cond: (...) So, the general idea is to create functional indexes (long_period in this example) on the base table that will cover the aggregate keys of intermediate CTEs. This approach works as long as the aggregate keys depend only on one table. Best regards, Renan Fonseca