From: Michael Date: Mon, 1 May 2017 09:34:15 +0000 (+0000) Subject: The number of queries is reduced dramatically X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=e90ae79d35eecf6e107c26a989df2dafe5bb895a;p=friendica.git The number of queries is reduced dramatically --- diff --git a/include/dba.php b/include/dba.php index 6fc51dfbe7..dfba269bf4 100644 --- a/include/dba.php +++ b/include/dba.php @@ -783,80 +783,105 @@ class dba { * @param boolean $in_commit Internal use: Only do a commit after the last delete * @param array $callstack Internal use: prevent endless loops * - * @return boolean was the delete successfull? + * @return boolean|array was the delete successfull? When $in_commit is set: deletion data */ static public function delete($table, $param, $in_commit = false, $callstack = array()) { + $commands = array(); + // Create a key for the loop prevention $key = $table.':'.implode(':', array_keys($param)).':'.implode(':', $param); // We quit when this key already exists in the callstack. if (isset($callstack[$key])) { - return true; + return $commands; } $callstack[$key] = $key; $table = self::$dbo->escape($table); + $commands[$key] = array('table' => $table, 'param' => $param); + // To speed up the whole process we cache the table relations if (count(self::$relation) == 0) { self::build_relation_data(); } + // Is there a relation entry for the table? + if (isset(self::$relation[$table])) { + // We only allow a simple "one field" relation. + $field = array_keys(self::$relation[$table])[0]; + $rel_def = array_values(self::$relation[$table])[0]; + + // When the search field is the relation field, we don't need to fetch the rows + // This is useful when the leading record is already deleted in the frontend but the rest is done in the backend + if ((count($param) == 1) AND ($field == array_keys($param)[0])) { + foreach ($rel_def AS $rel_table => $rel_field) { + $retval = self::delete($rel_table, array($rel_field => array_values($param)[0]), true, $callstack); + $commands = array_merge($commands, $retval); + } + } else { + // Fetch all rows that are to be deleted + $sql = "SELECT ".self::$dbo->escape($field)." FROM `".$table."` WHERE `". + implode("` = ? AND `", array_keys($param))."` = ?"; + $retval = false; + $data = self::p($sql, $param); + while ($row = self::fetch($data)) { + // Now we accumulate the delete commands + $retval = self::delete($table, array($field => $row[$field]), true, $callstack); + $commands = array_merge($commands, $retval); + } + + // When we don't find data then we don't need to delete it + if (is_bool($retval)) { + return $in_commit ? $commands : true; + } + // Since we had split the delete command we don't need the original command anymore + unset($commands[$key]); + } + } + if (!$in_commit) { + // Now we finalize the process self::p("COMMIT"); self::p("START TRANSACTION"); - } - // Is there a relation entry for the table? - if (isset(self::$relation[$table])) { - foreach (self::$relation[$table] AS $field => $rel_def) { - // When the search field is the relation field, we don't need to fetch the rows - // This is useful when the leading record is already deleted in the frontend but the rest is done in the backend - if ((count($param) == 1) AND ($field == array_keys($param)[0])) { - foreach ($rel_def AS $rel_table => $rel_field) { - $retval = self::delete($rel_table, array($rel_field => array_values($param)[0]), true, $callstack); - if (!$retval) { - return false; - } + $compacted = array(); + foreach ($commands AS $command) { + if (count($command['param']) > 1) { + $sql = "DELETE FROM `".$command['table']."` WHERE `". + implode("` = ? AND `", array_keys($command['param']))."` = ?"; + + logger(dba::replace_parameters($sql, $command['param']), LOGGER_DATA); + + if (!self::e($sql, $param)) { + self::p("ROLLBACK"); + return false; } } else { - // Fetch all rows that are to be deleted - $sql = "SELECT ".self::$dbo->escape($field)." FROM `".$table."` WHERE `". - implode("` = ? AND `", array_keys($param))."` = ?"; - $retval = false; - $data = self::p($sql, $param); - while ($row = self::fetch($data)) { - foreach ($rel_def AS $rel_table => $rel_field) { - // We have to do a separate delete process per row - $retval = self::delete($rel_table, array($rel_field => $row[$field]), true, $callstack); - if (!$retval) { - return false; - } - } - } - if (!$retval) { - return true; - } + $value = array_values($command['param'])[0]; + $compacted[$command['table']][array_keys($command['param'])[0]][$value] = $value; } } - } - - $sql = "DELETE FROM `".$table."` WHERE `". - implode("` = ? AND `", array_keys($param))."` = ?"; + foreach ($compacted AS $table => $values) { + foreach ($values AS $field => $field_values) { + $sql = "DELETE FROM `".$table."` WHERE `".$field."` IN (". + substr(str_repeat("?, ", count($field_values)), 0, -2).");"; - $retval = self::e($sql, $param); + logger(dba::replace_parameters($sql, $field_values), LOGGER_DATA); - if (!$in_commit) { - if ($retval) { - self::p("COMMIT"); - } else { - self::p("ROLLBACK"); + if (!self::e($sql, $param)) { + self::p("ROLLBACK"); + return false; + } + } } + self::p("COMMIT"); + return true; } - return $retval; + return $commands; } /**