diff --git a/core/expression.class.inc.php b/core/expression.class.inc.php index 7aeb3dc59..2f30a1498 100644 --- a/core/expression.class.inc.php +++ b/core/expression.class.inc.php @@ -1121,13 +1121,20 @@ class QueryBuilderExpressions protected $m_aSelectExpr; protected $m_aGroupByExpr; protected $m_aJoinFields; + protected $m_aClassIds; - public function __construct($oCondition, $aGroupByExpr = null) + public function __construct($oSearch, $aGroupByExpr = null) { - $this->m_oConditionExpr = $oCondition; + $this->m_oConditionExpr = $oSearch->GetCriteria(); $this->m_aSelectExpr = array(); $this->m_aGroupByExpr = $aGroupByExpr; $this->m_aJoinFields = array(); + + $this->m_aClassIds = array(); + foreach($oSearch->GetJoinedClasses() as $sClassAlias => $sClass) + { + $this->m_aClassIds[$sClassAlias] = new FieldExpression('id', $sClassAlias); + } } public function GetSelect() @@ -1166,6 +1173,20 @@ class QueryBuilderExpressions array_push($this->m_aJoinFields, $oExpression); } + /** + * Get tables representing the queried objects + * Could be further optimized: when the first join is an outer join, then the rest can be omitted + */ + public function GetMandatoryTables(&$aTables = null) + { + if (is_null($aTables)) $aTables = array(); + + foreach($this->m_aClassIds as $sClass => $oExpression) + { + $oExpression->CollectUsedParents($aTables); + } + } + public function GetUnresolvedFields($sAlias, &$aUnresolved) { $this->m_oConditionExpr->GetUnresolvedFields($sAlias, $aUnresolved); @@ -1204,6 +1225,11 @@ class QueryBuilderExpressions { $this->m_aJoinFields[$index] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved); } + + foreach($this->m_aClassIds as $sClass => $oExpression) + { + $this->m_aClassIds[$sClass] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved); + } } public function RenameParam($sOldName, $sNewName) diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 11269d27f..c1829cd69 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2290,7 +2290,8 @@ abstract class MetaModel // Simplify the query if just getting the count $oSelect->SetSelect(array()); } - $oSelect->OptimizeJoins(); + $oBuild->m_oQBExpressions->GetMandatoryTables($aMandatoryTables); + $oSelect->OptimizeJoins($aMandatoryTables); } $oKPI->ComputeStats('MakeQuery (select)', $sOqlQuery); @@ -2504,7 +2505,6 @@ abstract class MetaModel { // default to the whole list of attributes + the very std id/finalclass $oBuild->m_oQBExpressions->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias)); - if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad)) { $aAttList = self::ListAttributeDefs($sClass); @@ -2798,19 +2798,15 @@ abstract class MetaModel // 1/a - Get the key and friendly name // - // We need one pkey to be the key, let's take the one corresponding to the root class - // (used to be based on the leaf, then moved to the root class... now back to the leaf for optimization concerns) + // We need one pkey to be the key, let's take the first one available $oSelectedIdField = null; - if ($sTableClass == $sTargetClass) - { - $oIdField = new FieldExpressionResolved(self::DBGetKey($sTableClass), $sTableAlias); - $aTranslation[$sTargetAlias]['id'] = $oIdField; + $oIdField = new FieldExpressionResolved(self::DBGetKey($sTableClass), $sTableAlias); + $aTranslation[$sTargetAlias]['id'] = $oIdField; - if ($bIsOnQueriedClass) - { - // Add this field to the list of queried fields (required for the COUNT to work fine) - $oSelectedIdField = $oIdField; - } + if ($bIsOnQueriedClass) + { + // Add this field to the list of queried fields (required for the COUNT to work fine) + $oSelectedIdField = $oIdField; } // 1/b - Get the other attributes diff --git a/core/querybuildercontext.class.inc.php b/core/querybuildercontext.class.inc.php index 2755590de..b72c2bc32 100644 --- a/core/querybuildercontext.class.inc.php +++ b/core/querybuildercontext.class.inc.php @@ -35,7 +35,7 @@ class QueryBuilderContext public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null) { $this->m_oRootFilter = $oFilter; - $this->m_oQBExpressions = new QueryBuilderExpressions($oFilter->GetCriteria(), $aGroupByExpr); + $this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr); $this->m_aClassAliases = $oFilter->GetJoinedClasses(); $this->m_aTableAliases = array(); diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php index aff3811a5..58e11406c 100644 --- a/core/sqlquery.class.inc.php +++ b/core/sqlquery.class.inc.php @@ -597,12 +597,12 @@ class SQLQuery return $this->m_sTableAlias; } - public function OptimizeJoins($aUsedTables = null) + public function OptimizeJoins($aUsedTables, $bTopCall = true) { - if (is_null($aUsedTables)) + if ($bTopCall) { - // Top call: build the list of tables absolutely required to perform the query - $aUsedTables = $this->CollectUsedTables(); + // Top call: complete the list of tables absolutely required to perform the right query + $this->CollectUsedTables($aUsedTables); } $aToDiscard = array(); @@ -610,7 +610,7 @@ class SQLQuery { $oSQLQuery = $aJoinInfo["select"]; $sTableAlias = $oSQLQuery->GetTableAlias(); - if ($oSQLQuery->OptimizeJoins($aUsedTables) && !array_key_exists($sTableAlias, $aUsedTables)) + if ($oSQLQuery->OptimizeJoins($aUsedTables, false) && !array_key_exists($sTableAlias, $aUsedTables)) { $aToDiscard[] = $i; } @@ -623,29 +623,24 @@ class SQLQuery return (count($this->m_aJoinSelects) == 0); } - protected function CollectUsedTables(&$aTables = null) + protected function CollectUsedTables(&$aTables) { - if (is_null($aTables)) + $this->m_oConditionExpr->CollectUsedParents($aTables); + foreach($this->m_aFields as $sFieldAlias => $oField) { - $aTables = array(); - - $this->m_oConditionExpr->CollectUsedParents($aTables); - foreach($this->m_aFields as $sFieldAlias => $oField) + $oField->CollectUsedParents($aTables); + } + if ($this->m_aGroupBy) + { + foreach($this->m_aGroupBy as $sAlias => $oExpression) { - $oField->CollectUsedParents($aTables); - } - if ($this->m_aGroupBy) - { - foreach($this->m_aGroupBy as $sAlias => $oExpression) - { - $oExpression->CollectUsedParents($aTables); - } - } - if (!is_null($this->m_oSelectedIdField)) - { - $this->m_oSelectedIdField->CollectUsedParents($aTables); + $oExpression->CollectUsedParents($aTables); } } + if (!is_null($this->m_oSelectedIdField)) + { + $this->m_oSelectedIdField->CollectUsedParents($aTables); + } foreach ($this->m_aJoinSelects as $i => $aJoinInfo) {