Hi Andrus,

here's my stab at it. I'm sure it's all sorts of wrong :-) but the
pathological performance drop on PostgreSQL is gone.

I actually tested this on Cayenne 3 and then copied it over to a patch
on origin/master.


---------- Forwarded message ----------
From: Andrus Adamchik <and...@objectstyle.org>
Date: Mon, Apr 20, 2015 at 6:07 PM
Subject: Re: Problem with catastrophic performance degradation under PostgreSQL
To: dev@cayenne.apache.org


Hi,

So, Cayenne adds DISTINCT whenever a qualifier includes a to-many
relationship (for obvious reasons). Since we are dealing with an
object query, I guess we always know the ID columns and can rewrite
DISTINCT for PostgreSQL as "DISTINCT ON(id1, id2, ...)" to achieve the
desired effect.

You want to take a shot at providing a fix for that?

Thanks,
Andrus


> On Apr 20, 2015, at 6:09 PM, Øyvind Harboe <oyvind.har...@zylin.com> wrote:
>
> Q: Would it be a good idea to modify the PostgreSQL Cayenne adapter to use
> the 'DISTINCT ON()' syntax?
>
> I've been testing out PostgreSQL vs. Derby for our application when I ran
> into a problem where the performance of PostgreSQL went from great to
> abysmal for no apparent reason.
>
> After a bit of digging, I've found that the problem is with the SQL
> statement that Cayenne generates.
>
> Cayenne generates statements of the following form which yields bad
> performance on PostgreSQL with complicated WHERE statements and numerous
> columns:
>
> 1) SELECT DISTINCT a,b,c,d,e,f ... WHERE somecomplicatedstatement
>
> If I rewrite this statement to the form below using the 'DISTINCT ON()'
> syntax(which is PostgreSQL specific dialect), then I get great performance
> again:
>
> 2) SELECT DISTINCT ON(a) a,b,c,d,e,f ... WHERE somecomplicatedstatement
>
>
>
>
> Numbers on my machine:
>
> 1) 44000ms
>
> 2) 4300ms
>
> Here's where I read up on the DISTINCT ON syntax:
> http://www.postgresql.org/docs/9.4/static/sql-select.html
>
>
>
> --
> Øyvind Harboe - Can Zylin Consulting help on your project?
> http://www.zylin.com/




-- 
Øyvind Harboe - Can Zylin Consulting help on your project?
http://www.zylin.com/
From 94a028569f5705b197a2f223398e49e0869f3447 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Harboe?= <oyvind.har...@zylin.com>
Date: Mon, 20 Apr 2015 19:36:46 +0200
Subject: [PATCH] postgresql: improves performance by taking advantage of the
 DISTINCT ON dialect

---
 .../access/translator/select/SelectTranslator.java | 14 +++++++++--
 .../dba/postgres/PostgresSelectTranslator.java     | 29 ++++++++++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java
index 4c0aa4d..aa48de0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/SelectTranslator.java
@@ -156,8 +156,8 @@ public class SelectTranslator extends QueryAssembler {
                 }
             }
 
-            if (!suppressingDistinct) {
-                queryBuf.append("DISTINCT ");
+            if (!suppressingDistinct)  {
+                queryBuf.append(buildDistinctStatement() + " ");
             }
         }
 
@@ -230,6 +230,16 @@ public class SelectTranslator extends QueryAssembler {
     }
 
     /**
+     * Allows subclasses to insert their own dialect of DISTINCT
+     * statement to improve performance.
+     *
+     * @return string representing the DISTINCT statement
+     */
+    protected String buildDistinctStatement() {
+        return "DISTINCT";
+    }
+
+    /**
      * Handles appending optional limit and offset clauses. This implementation
      * does nothing, deferring to subclasses to define the LIMIT/OFFSET clause
      * syntax.
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java
index 3344e25..7eed165 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresSelectTranslator.java
@@ -53,4 +53,33 @@ class PostgresSelectTranslator extends SelectTranslator {
         }
     }
 
+    @Override
+    protected String buildDistinctStatement() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("DISTINCT ON (");
+
+        DbEntity table = getRootDbEntity();
+        boolean addComma = false;
+        for (final DbAttribute dba : table.getPrimaryKeys()) {
+            if (!dba.isPrimaryKey()) {
+                continue;
+            }
+
+            for (ColumnDescriptor column : getResultColumns()) {
+                if (column.getTableName().equals(table.getName())) {
+                    if (column.getName().equals(dba.getName())) {
+                        if (addComma) {
+                            builder.append(", ");
+                        }
+                        builder.append(column.getQualifiedColumnName());
+                        addComma = true;
+                        break;
+                    }
+                }
+            }
+        }
+        builder.append(")");
+        return builder.toString();
+    }
+
 }
-- 
1.9.1

Reply via email to