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 1qbctg-00CZl6-BR for pgsql-performance@arkaria.postgresql.org; Thu, 31 Aug 2023 08:19:24 +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 1qbcte-0058fl-Pp for pgsql-performance@arkaria.postgresql.org; Thu, 31 Aug 2023 08:19:22 +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 1qbcte-0058fX-Ca for pgsql-performance@lists.postgresql.org; Thu, 31 Aug 2023 08:19:22 +0000 Received: from mail-ed1-x529.google.com ([2a00:1450:4864:20::529]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1qbctY-001vxX-D9 for pgsql-performance@lists.postgresql.org; Thu, 31 Aug 2023 08:19:20 +0000 Received: by mail-ed1-x529.google.com with SMTP id 4fb4d7f45d1cf-52a1ce529fdso630800a12.1 for ; Thu, 31 Aug 2023 01:19:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1693469954; x=1694074754; darn=lists.postgresql.org; h=content-transfer-encoding:subject:from:to:content-language :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=ve9T5khH4BJRHr0v85BRV4IKiK3aq3CENItzIQJ47nI=; b=m4KZv2XfZSo1FT90Knm0ThOEROvNZfr24bxJHmgi+1YSIB5QyFknBsJKq81z70Xl1l uJDlmTP+8AhvkOD/lS/S7AsVb/Za33zlrYnIAZwPPyTCZJ4bHmSjjDtVljKKyEylL9oG kaetMlcCtWvmzhleihqnP6e8cZMBjaOmggK8ounlaa1rWd5RFwHVus0RJAZK3XwBSuu3 T0mH892Ltz/LIVOtEGR2funnw/PU8c2zY8i2DxDVCy5yfWqJpWpW5FRDS72Pm7EfHvKM PEJ9EBr8xqtO5NDW0gIVa4VP3lq5YZNQQSosw6rfN0mSZCLaoiQz+wcmXoDJiGEHd8MQ mAzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693469954; x=1694074754; h=content-transfer-encoding:subject:from:to:content-language :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=ve9T5khH4BJRHr0v85BRV4IKiK3aq3CENItzIQJ47nI=; b=YwW4REZUs3FfcPvvVOHAgvtcjfZ5eRUBZrJryQ19b/+6If/u8dVDqN2LAMuTapFT1j FSAutnBMZKk5z7WnEU8j9AJjHqoeHp1Mf1ojuk9qJZSRWx8n1SAtiv+Rh7OZu7Ij/4pX lGDFmRKMnuUjJm8j1zuF6odh83yZNSJxvGz18UYrOPDB+a7eelvSI71ZkBFMiL7kqoGs hR9jjxJnAgqXPQBxfyObpotfv+WyoVofTeB3C5vG1kwEfsZtGUK1+P2u1dbFBX6CLS2z H/tcqtdmtii3Lt3IwpJSBOTaw/w/dHrUAikwFAuAvupuFpV55ztMSIRsk65PznrnVAT2 dNRg== X-Gm-Message-State: AOJu0Yy5Hhq5RlBnbrJnm9rW3TAyljvGEXraThDUWFDEnp756dxOp9Yv 6DMmzk0xdA0/qztjnL1iPm3EkD6GogI= X-Google-Smtp-Source: AGHT+IGfvvuB/qaA84T8MTrMNn+yq9bzqOsH2LOyeAGmcqIyEKIB+WbtY9dFPxk9FARlLiXSt9nJgg== X-Received: by 2002:aa7:d603:0:b0:52b:c980:43f3 with SMTP id c3-20020aa7d603000000b0052bc98043f3mr4064544edr.28.1693469954052; Thu, 31 Aug 2023 01:19:14 -0700 (PDT) Received: from ?IPV6:2a02:8071:6340:43a0:27df:723a:1952:1fc? ([2a02:8071:6340:43a0:27df:723a:1952:1fc]) by smtp.gmail.com with ESMTPSA id n13-20020a05640206cd00b0052a1a623267sm504028edy.62.2023.08.31.01.19.13 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 31 Aug 2023 01:19:13 -0700 (PDT) Message-ID: <7b36ee6b-495c-4a8e-a131-70150762430e@gmail.com> Date: Thu, 31 Aug 2023 10:19:10 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: de-DE, en-US To: pgsql-performance@lists.postgresql.org From: Christian Beikov Subject: Join order optimization Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, I'm from the Hibernate team (Java ORM) and a user recently reported that a change in our SQL rendering affected his query plans in a bad way. In short, we decided to model certain constructs in our ORM with "nested joins" i.e. using parenthesis to express the join order. This is what we want to model semantically, though there are cases when we could detect that we don't need the explicit join ordering to get the same semantics. I expected that the PostgreSQL optimizer can do the same reasoning and do further join re-ordering to produce an optimal plan, but to my surprise it seems it isn't capable to do that. The query we generate right now is of the following structure: from tbl1 t1 join (tbl2 t2     left join tbl3 t3 on t3.pkAndFk = t2.pk     left join tbl4 t4 on t4.pkAndFk = t2.pk     ...     left join tbl9 t9 on t9.pkAndFk = t2.pk ) on t2.fk = t1.pk where t1.indexedColumn = ... whereas the query we generated before, which is semantically equivalent, is the following: from tbl1 t1 join tbl2 t2 on t2.fk = t1.pk left join tbl3 t3 on t3.pkAndFk = t2.pk left join tbl4 t4 on t4.pkAndFk = t2.pk ... left join tbl9 t9 on t9.pkAndFk = t2.pk where t1.indexedColumn = ... You can find the full queries in the attachments section of the issue report from the user: https://hibernate.atlassian.net/browse/HHH-16595 Query_Hibernate5.txt shows the old style query without parenthesis and Query_Hibernate6.txt shows the new style. You will also find the query plans for the two queries attached as CSV files. It almost seems like the PostgreSQL optimizer sees the parenthesis for join ordering as an optimization fence!? The user reported that the behavior is reproducible in PostgreSQL versions 11 and 15. He promised to provide a full reproducer for this which I am still waiting for, but I'll share it with you as soon as that was provided if needed. I think that we can detect that the parenthesis is unnecessary in this particular case, but ideally PostgreSQL would be able to detect this as well to plan the optimal join order. Any ideas what is going on here? Is this a bug or missed optimization in the query optimizer? I'm a bit worried about what PostgreSQL will produce for queries that really need the parenthesis for join ordering e.g. from tbl1 t1 left join (tbl2 t2     join tbl3 t3 on t3.pkAndFk = t2.pk     join tbl4 t4 on t4.pkAndFk = t2.pk     ...     join tbl9 t9 on t9.pkAndFk = t2.pk ) on t2.fk = t1.pk where t1.indexedColumn = ... Thanks for any help. Christian