Этот запрос возвращает 5 результатов в phpMyAdmin:
SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
И это возвращает count = 12 в phpMyAdmin (это нормально, потому что есть 12 записей):
SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
Эта функция работала нормально, прежде чем я добавил две переменные (смещение, отображение), однако теперь это не сработает, и распечатка переменных дает мне смещение = 0, display = 5 (так что еще LIMIT 0,5
).
function getProducts($offset, $display) { $sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;"; $data = array((int)$offset, (int)$display); $rows = dbRowsCount($sql, $data); logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows."); if ($rows > 0) { dbQuery($sql, $data); return dbFetchAll(); } else { return null; } }
Он не работает, потому что dbRowsCount(...)
возвращал пустую строку (глупое PDOStatement::fetchColumn
), поэтому я изменил ее, чтобы вернуть счет с помощью PDO::FETCH_ASSOC
и возвращает count = 0.
Вот функция, которая выполняет подсчет строк:
function dbRowsCount($sql, $data) { global $db, $query; $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is'; if (preg_match($regex, $sql, $output) > 0) { $query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}"); logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}"); $query->setFetchMode(PDO::FETCH_ASSOC); if ($data != null) $query->execute($data); else $query->execute(); if (!$query) { echo "Oops! There was an error: PDOStatement returned false."; exit; } $result = $query->fetch(); return (int)$result["count"]; } else { logErrors("Regex did not match: ".$sql); } return -1; }
Мой журнал ошибок дает мне этот результат из программы:
Вывод регулярного выражения: SELECT COUNT (*) AS count FROM tbl_product ГДЕ 1 ORDER BY last_update DESC LIMIT?,?;
getProducts (0,5) возвратил 0 строк.
Как вы можете видеть, SQL не был искажен, а входные переменные метода были равны 0 и 5, как ожидалось.
Кто-нибудь знает, что пошло не так?
Следуя предложению, я попытался выполнить запрос напрямую, и он вернул правильный результат:
function dbDebugTest() { global $db; $stmt = $db->query("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT 0,5;"); $result = $stmt->fetch(); $rows = (int)$result["count"]; logErrors("dbDebugTest() returned rows=".$rows); }
Вывод:
> dbDebugTest() returned rows=12
Следуя другому предложению, я изменил! = Null на! == null, и я также распечатал массив $ data:
logErrors("Data: ".implode(",",$data)); if ($data !== null) $query->execute($data); else $query->execute();
Вывод:
> Data: 0,5
Однако dbRowsCount ($ sql, $ data) по-прежнему возвращает 0 строк для этого запроса!
Следуя советам по внедрению пользовательского класса PDOStatement, который позволит мне выводить запрос после привязки значений, я обнаружил, что функция остановится после $ query-> execute ($ data), и поэтому вывод не будет напечатан, хотя пользовательский класс работает для каждого другого запроса в моей программе.
Обновленный код:
function dbRowsCount($sql, $data) { global $db, $query; $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is'; if (preg_match($regex, $sql, $output) > 0) { $query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}"); logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}"); $query->setFetchMode(PDO::FETCH_ASSOC); logErrors("Data: ".implode(",",$data)); $query->execute($data); logErrors("queryString:".$query->queryString); logErrors("_debugQuery():".$query->_debugQuery()); if (!$query) { echo "Oops! There was an error: PDOStatement returned false."; exit; } $result = $query->fetch(); return (int)$result["count"]; } else { logErrors("Regex did not match: ".$sql); } return -1; }
Вывод:
Вывод регулярного выражения : SELECT COUNT ( ) AS count FROM tbl_product_category WHERE id = ?;
Данные: 5
queryString: SELECT COUNT ( ) AS count FROM tbl_product_category WHERE id = ?;
_debugQuery (): SELECT COUNT (*) AS count FROM tbl_product_category WHERE id = ?;Вывод регулярного выражения: SELECT COUNT (*) AS count FROM tbl_product ГДЕ 1 ORDER BY last_update DESC LIMIT?,?;
Данные: 0,5
// функция остановлена и _debugQuery не может быть выведен
Поскольку я не мог получить собственный класс PDOStatement, чтобы дать мне некоторый результат, я подумал, что я переписал getProducts(...)
чтобы вместо этого привязать параметры с именами-заполнителями.
function getProducts($offset, $display) { $sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;"; $data = array(':offset'=>$offset, ':display'=>$display); $rows = dbRowsCount($sql, $data); logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows."); if ($rows > 0) { dbQuery($sql, $data); return dbFetchAll(); } else { return null; } }
Вывод:
Вывод регулярного выражения: SELECT COUNT (*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT: offset,: display;
Данные: 0,5
// По-прежнему происходит сбой после $ query-> execute ($ data), поэтомуlogErrors("getProducts(".$offset."...))
не распечатывается
Этот dbDebugTest ранее работал с объявлением предельных значений 0,5 непосредственно в строке SQL. Теперь я обновил его, чтобы правильно привязать параметры:
function dbDebugTest($offset, $display) { logErrors("Beginning dbDebugTest()"); global $db; $stmt = $db->prepare("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT :offset,:display;"); $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); $stmt->bindParam(':display', $display, PDO::PARAM_INT); if ($stmt->execute()) { $result = $stmt->fetch(); $rows = (int)$result["count"]; logErrors("dbDebugTest() returned rows=".$rows); } else { logErrors("dbDebugTest() failed!"); } }
Функция сработает, и только это выводится:
Начало dbDebugTest ()
Следуя предложению об ошибках (они отключены по умолчанию), я сделал следующее:
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Это само по себе сделало работу dbDebugTest () из Update 4!
Начало dbDebugTest () dbDebugTest () возвращенных строк = 12
И теперь в моих журналах веб-сервера генерируется ошибка:
[warn] mod_fcgid: stderr: PHP Неустранимая ошибка:
Исключить исключение «PDOException» с сообщением «SQLSTATE [42000]:
Ошибка синтаксиса или нарушение доступа: 1064 У вас есть ошибка в синтаксисе SQL;
проверьте руководство, соответствующее версии сервера MySQL, для правильного использования синтаксиса
около '0', '5' 'в строке 1'
в /home/linweb09/b/example.com-1050548206/user/my_program/database/dal.php:36
Строка 36 относится к dbRowsCount(...)
а строка – $query->execute($data)
.
Таким образом, другой метод getProducts(...)
прежнему не работает, потому что он использует этот метод привязки данных, а params превращаются в «0» и «5» (это ошибка?). Немного раздражает, но мне нужно будет создать новый метод в моем dal.php, чтобы я мог привязывать параметры более строгим способом – с помощью bindParam
.
Особенно спасибо @ Travesty3 и @eggyal за помощь! Многое, очень ценится.
На основе обновления 2 в вопросе, где выполнение останавливается после инструкции execute
, похоже, что запрос не работает. Изучив некоторую документацию PDO , похоже, что для параметра обработки ошибок по умолчанию используется PDO :: ERRMODE_SILENT, что приведет к поведению, которое вы видите.
Это, скорее всего, связано с тем, что числа в вашем предложении LIMIT помещаются в одинарные кавычки, когда они передаются как параметры, как это происходило в этом сообщении .
Решение этой должности состояло в том, чтобы указать параметры как целые числа, используя метод bindValue
. Поэтому вам, вероятно, придется сделать что-то подобное.
И похоже, что вы также должны выполнять свои запросы с помощью блоков try-catch, чтобы уловить ошибку MySQL.
Метод bindValue:
if ($data !== null) { for ($i=0; $i<count($data); $i++) $query->bindValue($i+1, $data[$i], PDO::PARAM_INT); $query->execute($data); } else $query->execute();
Вы проверяете, является ли $data
NULL
с помощью оператора равенства, а не идентификатора (см. Руководство по PHP для получения дополнительной информации о том, как значения NULL
обрабатываются различными операторами сравнения). Вам нужно либо использовать тест идентификации ===
/ !==
, либо вызвать is_null()
.
Как упоминалось выше, Travesty3, чтобы проверить, пуст ли массив, используйте empty()
.