@Ic2d
Самоучка:)

Оптимизировать запрос MySQL. Как узнать сколько всего рядов когда использую limit?

$numPosts = 3; //Постов на страницу
$start=abs($page*$numPosts); //Начинать запрос начиная с поста

$STH = $GLOBALS["mysqlcon"]->prepare("SELECT * FROM post WHERE MATCH (tags) AGAINST ('".clearStr($_GET['search'])."' IN BOOLEAN MODE)");
$STH->execute();
$totalPages=ceil($STH->rowCount()/$numPosts); //Узнаем сколько всего страниц существует

 $STH = $GLOBALS["mysqlcon"]->prepare("SELECT * FROM post WHERE MATCH (tags) AGAINST ('".clearStr($_GET['search'])."' IN BOOLEAN MODE) LIMIT $start, $numPosts");
 $STH->execute();

while($row = $STH->fetch()) { ... }


Необходимо послать запрос в бд MySQL с лимитами (использую для страниц), но в тоже время знать сколько всего страниц существует. Сейчас делается запрос 2 раза, но это ведь лишняя работа. Посоветуйте как правильнее и лучше это делать:)
  • Вопрос задан
  • 411 просмотров
Решения вопроса 1
FanatPHP
@FanatPHP
Чебуратор тега РНР
Сам по себе еще один запрос - это не лишняя работа, а ерунда на постном масле.
Вопрос не в том, сколько запросов выполняется, а что именно они делают.

И в данном случае эти два запроса делают ужас, летящий на крыльях ночи. Я сейчас расскажу, что делает ваш код.
Сначала этот код просит базу данных отправить в РНР все найденные запросом строки. Все. Без лимита. Чтобы РНР мог посчитать их и потом выбросить.
После этого код еще раз просит базу отправить, только не все строки, а только часть. И теперь РНР уже не выбрасывает полученные строки, а честно выводит.
Понятное дело, что смысла в этих замысловатых телодвижениях - ноль целых, ноль десятых.

Как правильно получить общее количество найденных строк? Надо попросить базу посчитать их

Ну и разумеется, использование prepare вместе с доморощенной и небезопасной функцией "clearStr" - это вообще за гранью добра и зла. Это примерно как хранить миллион в сейфе, но не запирать его на ключ, а подпирать дощечкой.

$GLOBALS["mysqlcon"] - это отдельный ужас, но на фоне всего остального меркнет.

Правильный код будет таким
$sql = "SELECT count(*) FROM post WHERE MATCH (tags) AGAINST (? IN BOOLEAN MODE)";
$STH = $GLOBALS["mysqlcon"]->prepare($sql);
$STH->execute([$_GET['search']]);
$totalPages=ceil($STH->fetchColumn()/$numPosts); //Узнаем сколько всего страниц существует

$sql = "SELECT * FROM post WHERE MATCH (tags) AGAINST (? IN BOOLEAN MODE) LIMIT ?,?";
$STH = $GLOBALS["mysqlcon"]->prepare($sql);
$STH->execute([$_GET['search'],$start, $numPosts]);


Поскольку все советуют синглтон, вот готовый пример: https://phpdelusions.net/pdo/pdo_wrapper#singleton
С ним, кроме прочего, код будет короче

$sql = "SELECT count(*) FROM post WHERE MATCH (tags) AGAINST (? IN BOOLEAN MODE)";
$numRows = DB::run($sql, [$_GET['search']])->fetchColumn();
$totalPages=ceil($numRows/$numPosts); //Узнаем сколько всего страниц существует

$sql = "SELECT * FROM post WHERE MATCH (tags) AGAINST (? IN BOOLEAN MODE) LIMIT ?,?";
$STH = DB::run($sql, [$_GET['search'],$start, $numPosts]);
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
Stalker_RED
@Stalker_RED
FanatPHP все правильно написал, но если стоит задача поменьше нагружать БД, то можно еще круче.

1. Делаете выборку с лимитом, указав при этом опцию SQL_CALC_FOUND_ROWS.
2. Сразу после этого запрашиваете FOUND_ROWS(), который вернет общее количество найденный строк (без учета лимита)

Эта циферка уже и так посчитана при предыдущем запросе с выборкой, и FOUND_ROWS() просто вернет значение, без дополнительного обращения к таблицам.

https://dev.mysql.com/doc/refman/8.0/en/informatio...

В принципе это можно и в один запрос собрать, используя UNION, но уменьшится читабельность.
Ответ написан
Radjah
@Radjah
Считай ряды средствами сервера БД. Для количества есть COUNT(поле). И первом запросе оставь какое-нибудь одно поле типа ID или что-то похожее.
Ответ написан
ThunderCat
@ThunderCat Куратор тега PHP
{PHP, MySql, HTML, JS, CSS} developer
$GLOBALS это ваще жесть конечно... как и вставка гданных из гет в запрос(даже с какой-то обработкой), у вас же препэйреды по идее должны работать!

$STH = $GLOBALS["mysqlcon"]->prepare("
SELECT count(id) as count
FROM `post` 
WHERE MATCH (tags) 
AGAINST ('".clearStr($_GET['search'])."' 
IN BOOLEAN MODE)");


UPD: вам в любом случае придется делать 2 запроса, так как количество результирующих строк разное, кроме того каунт по первичному ключу будет быстрым, а выборка скорее всего сразу закешируется после первого запроса.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы