Skip to content
Snippets Groups Projects
Commit 0ca51c9f authored by Jan-Hendrik Willms's avatar Jan-Hendrik Willms Committed by Jan-Hendrik Willms
Browse files

optimize query that loads the courses by using a subselect and EXISTS and...

optimize query that loads the courses by using a subselect and EXISTS and avoid duplication of costly query, fixes #3999

Closes #3999

Merge request studip/studip!2905
parent 93fab494
No related branches found
No related tags found
No related merge requests found
......@@ -375,14 +375,14 @@ class Admin_CoursesController extends AuthenticatedController
}
PluginEngine::sendMessage(AdminCourseWidgetPlugin::class, 'applyFilters', $filter);
$count = $filter->countCourses();
if ($count > $this->max_show_courses && !Request::submitted('without_limit')) {
$this->render_json([
'count' => $count
]);
try {
$courses = $filter->fetchCourses(
Request::bool('without_limit') ? null: $this->max_show_courses
);
} catch (OverflowException $e) {
$this->render_json(['count' => (int) $e->getMessage()]);
return;
}
$courses = AdminCourseFilter::get()->getCourses();
$data = [
'data' => []
......
......@@ -28,6 +28,8 @@
class AdminCourseFilter
{
static protected $instance = null;
/** @var SQLQuery|null */
public $query = null;
public $max_show_courses = 500;
public $settings = [];
......@@ -109,13 +111,10 @@ class AdminCourseFilter
}
if (Config::get()->ALLOW_ADMIN_RELATED_INST) {
$sem_inst = 'seminar_inst';
$this->query->join('seminar_inst', 'seminar_inst', 'seminar_inst.seminar_id = seminare.Seminar_id');
$this->query->where('seminar_inst', 'EXISTS (SELECT 1 FROM seminar_inst WHERE seminar_id = seminare.Seminar_id AND institut_id IN (:institut_ids))');
} else {
$sem_inst = 'seminare';
$this->query->where("seminar_inst", "seminare.institut_id IN (:institut_ids)");
}
$this->query->where('seminar_inst', "$sem_inst.institut_id IN (:institut_ids)");
$this->query->parameter('institut_ids', $inst_ids);
if ($GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE) {
......@@ -195,6 +194,16 @@ class AdminCourseFilter
return $this->query->count();
}
/**
* @param int|null $limit
* @return Course[]
*/
public function fetchCourses(?int $limit = null): array
{
NotificationCenter::postNotification('AdminCourseFilterWillQuery', $this);
return $this->query->fetchAll(Course::class, $limit);
}
/**
* Returns the data of the resultset of the AdminCourseFilter.
*
......@@ -206,16 +215,16 @@ class AdminCourseFilter
*/
public function getCoursesForAdminWidget(string $order_by = 'name')
{
$count_courses = $this->countCourses();
$order = 'seminare.name';
if ($order_by === 'number') {
$order = 'seminare.veranstaltungsnummer, seminare.name';
}
if ($count_courses && $count_courses <= $this->max_show_courses) {
try {
$order = 'seminare.name';
if ($order_by === 'number') {
$order = 'seminare.veranstaltungsnummer, seminare.name';
}
$this->query->orderBy($order);
return $this->getCourses();
return $this->fetchCourses($this->max_show_courses);
} catch (OverflowException $e) {
return [];
}
return [];
}
}
......@@ -215,14 +215,22 @@ class SQLQuery
/**
* Fetches the whole resultset as an array of associative arrays. If you define
* a sorm_class the result will be an array of the sorm-objects.
* @param $sorm_class_or_column : column name, a class of SimpleORMap or null for associative array.
* @return array of arrays or array of objects or array of values.
*
* @template T of SimpleORMap
* @param class-string<T>|string|null $sorm_class_or_column : column name, a class of SimpleORMap or null for associative array.
* @param int|null $max_results Maximum number of results to return
* @return array[]|T[]|mixed[] arrays or array of objects or array of values.
*
* @throws OverflowException if number of found rows is greater than $max_results
*/
public function fetchAll($sorm_class_or_column = null)
public function fetchAll($sorm_class_or_column = null, ?int $max_results = null)
{
NotificationCenter::postNotification('SQLQueryWillExecute', $this);
if (is_string($sorm_class_or_column) && !is_subclass_of($sorm_class_or_column, "SimpleORMap")) {
if (
is_string($sorm_class_or_column)
&& !is_subclass_of($sorm_class_or_column, SimpleORMap::class)
) {
$sql = "SELECT `{$this->settings['table']}`.`{$sorm_class_or_column}` ";
} else {
$sql = "SELECT `{$this->settings['table']}`.* ";
......@@ -239,16 +247,31 @@ class SQLQuery
NotificationCenter::postNotification('SQLQueryDidExecute', $this);
if (is_string($sorm_class_or_column) && !is_subclass_of($sorm_class_or_column, "SimpleORMap")) {
return $statement->fetchAll(PDO::FETCH_COLUMN, 0);
if (
is_string($sorm_class_or_column)
&& !is_subclass_of($sorm_class_or_column, SimpleORMap::class)
) {
return $statement->fetchAll(PDO::FETCH_COLUMN);
}
$alldata = $statement->fetchAll(PDO::FETCH_ASSOC);
if (!$sorm_class_or_column) {
return $alldata;
$statement->setFetchMode(PDO::FETCH_ASSOC);
$result = [];
$count = 0;
foreach ($statement as $row) {
$result[$count++] = $sorm_class_or_column ? $sorm_class_or_column::buildExisting($row) : $row;
if ($max_results && $count > $max_results) {
// Count remaining rows
$statement->setFetchMode(PDO::FETCH_COLUMN, 0);
while ($statement->fetch()) {
$count += 1;
}
throw new OverflowException($count);
}
}
return array_map("{$sorm_class_or_column}::buildExisting", $alldata);
return $result;
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment