From a48beac84544ed2db89ac094cc80508742284c1c Mon Sep 17 00:00:00 2001 From: Guy Marriott Date: Mon, 15 Apr 2019 11:36:57 +1200 Subject: [PATCH 1/2] FIX Calculate threshold condition with SQL rather than PHP This is a performance fix. Modern SQL engines can avoid counting a whole result set (potentially thousands of records) when you are only interested if the count exceeds a threshold. --- src/ORM/FieldType/DBForeignKey.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ORM/FieldType/DBForeignKey.php b/src/ORM/FieldType/DBForeignKey.php index 1c4532204..5071e83e8 100644 --- a/src/ORM/FieldType/DBForeignKey.php +++ b/src/ORM/FieldType/DBForeignKey.php @@ -87,12 +87,27 @@ class DBForeignKey extends DBInt // Add the count of the list to a cache for subsequent calls if (!isset(static::$foreignListCache[$hasOneClass])) { + // Let the DB do the threshold check as it will be faster - depending on the SQL engine it might only have + // to count indexes + $dataQuery = $list->dataQuery()->getFinalisedQuery(); + + // Clear order-by as it's not relevant for counts + $dataQuery->setOrderBy(false); + // Remove distinct. Applying distinct shouldn't be required provided relations are not applied. + $dataQuery->setDistinct(false); + + $dataQuery->setSelect(['over_threshold' => 'count(*) > ' . (int) $threshold]); + $result = $dataQuery->execute()->column('over_threshold'); + $overThreshold = !empty($result) && ($result[0] === 't' || (int) $result[0] === 1); + static::$foreignListCache[$hasOneClass] = [ - 'count' => $list->count(), + 'overThreshold' => $overThreshold, ]; } - if (static::$foreignListCache[$hasOneClass]['count'] < $threshold) { + $overThreshold = static::$foreignListCache[$hasOneClass]['overThreshold']; + + if (!$overThreshold) { // Add the mapped list for the cache if (!isset(static::$foreignListCache[$hasOneClass]['map'])) { static::$foreignListCache[$hasOneClass]['map'] = $list->map('ID', $titleField); From 7fd6e14423dca2f9ff21d90146630f03c3af53bb Mon Sep 17 00:00:00 2001 From: Guy Marriott Date: Tue, 16 Apr 2019 12:10:13 +1200 Subject: [PATCH 2/2] Adding comment about the === 't' condition for supporting postgres --- src/ORM/FieldType/DBForeignKey.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ORM/FieldType/DBForeignKey.php b/src/ORM/FieldType/DBForeignKey.php index 5071e83e8..d8afaab8c 100644 --- a/src/ORM/FieldType/DBForeignKey.php +++ b/src/ORM/FieldType/DBForeignKey.php @@ -98,6 +98,8 @@ class DBForeignKey extends DBInt $dataQuery->setSelect(['over_threshold' => 'count(*) > ' . (int) $threshold]); $result = $dataQuery->execute()->column('over_threshold'); + + // Checking for 't' supports PostgreSQL before silverstripe/postgresql@2.2 $overThreshold = !empty($result) && ($result[0] === 't' || (int) $result[0] === 1); static::$foreignListCache[$hasOneClass] = [