From a48beac84544ed2db89ac094cc80508742284c1c Mon Sep 17 00:00:00 2001 From: Guy Marriott Date: Mon, 15 Apr 2019 11:36:57 +1200 Subject: [PATCH] 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);