Почему у меня не работает сортировка с помощью ORDER BY GROUP BY?

Пробовал написать запрос по данному ответу для сортировки таблицы по передаваемому параметру.

Вот код с запросом:

$tasks_request = "SELECT * FROM (SELECT * FROM tasks ORDER BY :param ASC) AS tmp_table GROUP BY UPPER(:param) LIMIT :limit OFFSET :offset) ";
$stmp = $this->db->prepare($tasks_request);
$stmp->bindValue(":param", $ord_by, \PDO::PARAM_STR);
$stmp->bindValue(":limit", $tasks_per_page, \PDO::PARAM_INT);
$stmp->bindValue(":offset", $offset, \PDO::PARAM_INT);
$stmp->execute();

В итоге в выборке получаю только первую запись, а у меня их в таблице 5.

Пытался сделать так:

$tasks_request = "SELECT * FROM tasks GROUP BY UPPER(:param) ORDER BY :param LIMIT :limit OFFSET :offset ";

Результат тот же.

Пытался сделать без GROUP BY:

$tasks_request = "SELECT * FROM tasks ORDER BY :param LIMIT :limit OFFSET :offset ";

Выдаёт все нужные записи по (по лимиту их 3), но не сортирует.

В phpMyAdmin выполнил это:

SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));

Тоже не помогло... Параметры я проверял, все валидные, вывод ошибок у меня включён, при запросе их нет. В чём же тогда проблема?


Ответы (2 шт):

Автор решения: Мелкий

prepared statements не могут менять структуру запроса. Поля в order by или group by - это всё ещё структура запроса.

Формально вы отправляете

ORDER BY 'string' ASC

Внимание на кавычки. Просто строковую константу, одно и то же значение для всех строк результата. Конечно сортировка ничего полезного не сделает, если у всех строк одно и то же значение для сортировки.


При сортировке у вас заведомо известен список возможных полей, по которым вы можете разрешить сортировать. Вот и проверьте переданное пользователем значение, есть ли оно среди допустимых. Если есть - подставьте в сам текст запроса. То есть проверка по белому списку, что безопасно с точки зрения sql injection.


Пытаться через выключенный ONLY_FULL_GROUP_BY получать первую строку в группе - к большим приключениям. Поищите (или задайте новый вопрос если найдёте) как эта задача должна нормально решаться для вашей версии СУБД.

→ Ссылка
Автор решения: freshITmeat

Вот так работает:

$tasks_request = "SELECT * FROM tasks ORDER BY "."`".$ord_by."`"." LIMIT :limit OFFSET :offset ";

Конечно же- это костыль. В моём случае значение приходит из выпадающего списка, поэтому в $ord_by навряд ли получится запихнуть SQL Injection. Но, в целом, этот вариант решения проблемы плохой, хоть и работает. Если кто знает более адекватное решение данной проблемы- буду рад отметить как правильный ответ.

→ Ссылка