Страницы

Поиск по вопросам

Показаны сообщения с ярлыком mysqli. Показать все сообщения
Показаны сообщения с ярлыком mysqli. Показать все сообщения

вторник, 17 марта 2020 г.

Скрипт вывода товаров, очень долго грузится

#mysqli #while #php #foreach


Функции выборки товаров из базы:
function result_to_array($result) {
    $res_array = array();
    $count = 0;
    while( $row = $result->fetch_assoc() ) {
        $res_array[$count] = $row;
        $count++;
    }
    return $res_array;
}

function get_product() {
    connect_db();
    global $mysqli;
    $result = $mysqli->query("SELECT *FROM `product` ORDER by `id` DESC");
    $result = result_to_array($result);
    return $result;
}

На самой странице все товары выводятся циклом foreach:
$products = get_product();



html код



Проблема в том, что страница с товарами открывается ну очень долго, раньше просто
на этой же самой странице делал запрос и выводил все через while, по скорости было
ощутимо быстрее. 
Подскажите, что я делаю не так и почему так медленно работает скрипт?    


Ответы

Ответ 1



А много ли товаров? И можно оптимизировать конструкцию, убрав $count: while( $row = $result->fetch_assoc() ) { $res_array[] = $row; } Перед выполнением кода и после выполнения добавьте microtime(): function result_to_array($result) { $ra_start = microtime(); $res_array = array(); $count = 0; while( $row = $result->fetch_assoc() ) { $res_array[$count] = $row; $count++; } $ra_end = microtime(); echo $ra_end - $ra_start; return $res_array; } function get_product() { $gp_start = microtime(); connect_db(); global $mysqli; $result = $mysqli->query("SELECT *FROM `product` ORDER by `id` DESC"); $result = result_to_array($result); $gp_end = microtime(); echo $gp_end - $gp_start; return $result; } На самой странице все товары выводятся циклом foreach: $products = get_product(); html код: По результатам сможете увидеть, какой кусок у Вас долго выполняется.

Ответ 2



Идите поэтапно: 1) Смотрим, а не тупит ли mysql: $result = $mysqli->query("SELECT *FROM `product` ORDER by `id` DESC"); exit(); $result = result_to_array($result); 2) Как быстр while: while( $row = $result->fetch_assoc() ) { $res_array[$count] = $row; $count++; } exit(); return $res_array; 3) Как быстр вызов $products = get_product(); $products = get_product(); exit(); 4) Убираем и смотрим, как работает без него. Если долго выполняеться этап 1, оптимизируем mysql запрос (табличка возможно очень большая). Если долго выполняеться этап 2, используем ответ выше. Если долго выполняеться этап 3 (без двух предыдущих ) - что-то очень странное. Если долго выполняеться этап 4, то используем for вместо foreach (https://stackoverflow.com/questions/3430194/performance-of-for-vs-foreach-in-php). Если ничего не помогает и вызовы повторяються, то пихаем все в мемкэш (вплоть до html).

понедельник, 24 февраля 2020 г.

Когда выбирать реляционную БД, а когда не реляционную?

#mysql #база_данных #mysqli #nosql


Пишу сайт. Подразумевается большое количество записей разного размера. Подумал о
том, что при большом объёме записей БД будет долго обрабатывать запрос, поэтому надо
её раскинуть на несколько серверов. но вычитал, что реляционные БД плохо масштабируются
и для больших объемов данных используют не реляционные.



подВопрос:
Как поступить: пока не заморачиваться над этим и потом, при необходимости, перенести
данные в не реляционную БД ИЛИ выбрать реляционную/не реляционную БД ?
    


Ответы

Ответ 1



Подумал о том, что при большом объёме записей БД будет долго обрабатывать запрос Планирование не может начинаться с "подумал", боттлнеки всегда бывают в иных местах. Реляционки спокойно работают с миллионами записей. поэтому надо её раскинуть на несколько серверов. но вычитал, что реляционные БД плохо масштабируются Тут у меня опять претензия в ту же степь. Вы не знаете, что именно вы вычитали. Они действительно плохо масштабируются из-за того, что (по крайней мере у большинства) есть только одна модель master-slaves, которая упирается в мастер по скорости записи (поправьте, если есть популярные движки с многочисленными мастерами). В любом случае в базе данных не должно быть тяжелых подсчетов - если вы считаете количество записей в БД, то это должно рассчитываться внутри БД и кэшироваться на уровне приложения, если вы рассчитываете аналитику, то тут БД уже ничего считать не должна. В общем, у меня большие сомнения, что вам нужна масштабируемая система. Что до нереляционок, то их нельзя выбрать просто потому что "лучше масштабируются" - их только основных типов четыре штуки под свои задачи (вряд ли вы будете использовать key-value или графовую БД). Что до масштабирования, то есть row-column (т.е. хранение данных практически как в SQL) Cassandra, которая шардит данные по узлам и имеет практически линейную масштабируемость, т.е. добавление сервера в кластер из N серверов обеспечивает практически 100 * (1 / N + 1) процентов прироста производительности. Если есть знание кассандры, то делать проект на чем-то другом я не вижу смысла (единственная претензия - отсутствие готовых утилит для миграций, но это, надеюсь, изменится), классические реляционки как концепция по факту уже умерли - они, конечно, останутся, но в узком применении, и ближайшие N лет их популярность будет снижаться.

Ответ 2



Во первых, выбор типа базы данных мало зависит от количества записей, а от архитектуры приложения, специфики данных и многих других факторов. Во вторых, реляционные базы данных отлично масштабируются. Например Кассандра способна обрабатывать ~2к запросов в секунду и предоставляет горизонтальное масштабирование. По моим наблюдениям в крупных проектах(миллионы записей в день) отдают предпочтение как раз кассандре. В третьих, не все NoSQL базы данных подходят для работы с большим количеством объектов. Тот же RavenDB совсем не подходит для большого количества маленьких записей. Но если объединить большое количество этих маленьких записей в один документ, то эта база прекрасно справится. В целом сама постановка вопроса говорит о том, что вы плаваете в вопросе. Для работы с BigData не достаточно сделать правильного выбора базы данных, нужно еще грамотно реализовать архитектуру. В противном случае у вас будут проблемы даже при использовании самой подходящего инструмента для вашей задачи. Рекомендую почитать литературу посвященную проектированию баз данных.

Ответ 3



Если хочешь всех удивить возьми nosql Если хочешь блеснуть знанием старины возьми сетевую БД Если хочешь получить благодарность с того света от бабушки Ады - возьми иерархическую БД Ну и наконец, если хочешь написать что-нибудь стоящее возьми реляционную БД

воскресенье, 12 января 2020 г.

Зачем закрывать подключения?

#mysqli #php #mysql


Здравствуйте.
Разбираюсь с mysqli, в частности с этим примером:


И напросился вопрос: зачем нужно очищать результат набора, mysqli_free_result($result);,
закрывать подключения mysqli_close($link); и чем плохо, если этого не делать?    


Ответы

Ответ 1



Непонятно, откуда предыдущий оратор вообразил какой-то "таймаут". Соединение с БД закрывать не надо - оно закроется само по окончании работы скрипта. Сразу. БЕЗ каких-либо "таймаутов". Резалтсет в большинстве случаев очищать не нужно, поскольку как только закончит выполнение вызвавшая его функция, он так же обнулится.

Ответ 2



Незакрытые соединения прибьются по таймауту. Но если таких подключений будет очень много, система будет тормозить очень сильно, так как всё это потребляемые ресурсы. Поэтому "уходя, гасите свет". mysql_options php.su и оф. доки dev.mysql.com/doc/mysql-options Рекомендую для общего развития установить VirtualBox с *nix системой, установить веб-сервер+mysql и поиграться с настройками, чтобы наглядно увидеть, что и как работает. mysqli_free_result - очищает используемый блок памяти. Если простыми словами то - при выполнения кода, к примеру запрос съел 100мб памяти и не была применена команда mysqli_free_result то если дальше по коду есть ещё запросы то они будут ещё дожирать память в дополнение к уже используемой. А если выполним mysqli_free_result то память занятая запросом очистится и мы сможем повторно её использовать в этом же коде. Если у Вас в выполняемом коде один запрос, то mysqli_free_result необязателен, так как при завершении выполнении кода память всё равно очистится, но если по коду будет множество запросов то лучше очищать используемую память. Пример 1: php-код 1 sql-запрос # использовал 10мб памяти php-код 2 sql-запрос # использовал 5мб памяти В итоге получается что для выполнения кода потребовалось 15мб памяти, так как используемую память мы не очищали и она содержала первый запрос. Пример 2: php-код 1 sql-запрос # использовал 10мб памяти mysqli_free_result (очищает уже задействованные 10мб памяти в которые можно записать последующие запросы) php-код 2 sql-запрос # использовал 5мб памяти Для выполнения кода потребовалось 10мб памяти, так как мы очистили память от первого запроса и записали в неё второй.

Нестандартный вид вывода таблицы в php из MySql

#php #javascript #mysql #mysqli


Доброго времени суток, прошу помощи,
ситуация такая:
есть таблица в MySql:


задача: вывести ее на страницу в виде:


шапку таблицы создавал следующим образом:


                            
                             
                          connect_errno) {
    printf("Соединение не удалось: %s\n", $mysqli->connect_error);
    exit();
}
$sqltableclandate = "SELECT * FROM  `nametable` GROUP BY Date";
$sqltableclanResultdate = $mysqli->query($sqltableclandate);  
while($tableclandate = $sqltableclanResultdate->fetch_assoc()) 
{?>
                                
                                
                                
                            


А вот дальше затык....

Подскажите куда копать и где искать)

Забыл вчера добавить:
Добавление: если за какую-то дату нет информации, то в ячейке должен быть 0 или Н/Д

Добавление всего кода таблицы на данный момент:


Статистика:

query($query_dateclan)){ $rowclandate=$resultclandate->fetch_assoc(); $kol_rowsclandate = $resultclandate->num_rows; for($i=0; $i<$kol_rowsclandate; $i++){?>
Дата
вид базы: таблица: stat (как видно name5 появился позже)


Ответы

Ответ 1



Можно воспользоваться функцией GROUP_CONCAT(), которая позволяет вывести списки каждой из групп, получаемых GROUP BY. Обычно в GROUP_CONCAT() передают имя столбца, но тут потребуется два значения - дата и gold. Их можно объединить при помощи CONCAT() каким-нибудь уникальным разделителем, например, решеткой. SELECT GROUP_CONCAT(CONCAT(data, "#", gold)) AS data_gold FROM gold GROUP BY name +-------------------------------------------+ | data_gold | +-------------------------------------------+ | 2016-08-27#10,2016-08-28#15,2016-08-29#20 | | 2016-08-27#20,2016-08-28#25,2016-08-29#30 | | 2016-08-27#30,2016-08-28#35,2016-08-29#40 | | 2016-08-27#40,2016-08-28#45,2016-08-29#50 | +-------------------------------------------+ В PHP-коде можно сначала разбить строку по запятой (например, функцией explode()), а потом каждый элемент полученного массива еще раз разбить по #. GROUP_CONCAT допускает сортировку, а по полученной дате вы всегда сможете определить к какой ячейке должен относиться текущей элемент. Для формирования результирующей таблицы можно воспользоваться следующим PHP-кодом: PDO::ERRMODE_EXCEPTION)); function parse_gold($str) { $elements = explode(',', $str); $arr = array(); foreach($elements as $el) { list($date, $gold) = explode('#', $el); $arr[$date] = $gold; } return $arr; } $query = "SELECT name, GROUP_CONCAT(CONCAT(data, '#', gold)) AS data_gold FROM gold GROUP BY name"; $usr = $pdo->query($query); $users = array(); while($user = $usr->fetch()) { $users[$user['name']] = parse_gold($user['data_gold']); } $dates = array(); foreach($users as $user) { $dates += array_keys($user); } echo ''; // Шапка echo ''; foreach($dates as $date) { echo ""; } echo ''; // Содержимое таблицы foreach($users as $name => $golds) { echo ''; echo ""; foreach($dates as $date) { if(array_key_exists($date, $golds)) { echo ""; } else { echo ""; } } echo ''; } echo '
Имя$date
$name{$golds[$date]}-
'; } catch (PDOException $e) { echo "Ошибка выполнения запроса: " . $e->getMessage(); }

Ответ 2



вытяни данные с базы и сохрани в массив, а дальше через обычный foreach строишь таблицу это пример, если не знаешь как, кидай весь файл помогу

воскресенье, 29 декабря 2019 г.

Как настроить правильно кодировку для MySQL?

#mysqli #php #кодировка #mysql


Вот... Изучаю PHP. Дошла до соединения с БД.
И тут такая проблема.
В файле *.php прописываю соединение с БД. 
Затем прописываю запросы, вывожу результаты на экран. 
Все работает. Ошибок не выдает. НО! Проблема: 
русский текст не распознается. Выводит знаки ???
При этом английские буквы нормально выводит. 
Так понимаю, что проблемы с кодировкой.
Кодировку меняла и в самом php-файле, и в БД. 
На windows-1251 и на utf-8 и utf8_general_ci.
Видимо я пишу по старой версии PHP, и для PHP 5.х
этот метод не подходит. Но я учусь по книжке, там так
написано...
В сети нашла решение моей проблемы.
Но уже который час читаю, глаза уже красные,
не могу понять, что делаю не так. Если писать на основе
примеров, которые там написаны, то у меня сразу три ошибки
выдает. Вообще, как это использовать правильно?
Ничего не помогает. Что нужно сделать?
Подскажите, пожалуйста.
Код, который писала по книге такой:


Только просьба ко всем большая. Давайте не будем тут разговаривать на тему, зачем
девушке программирование. Уже общались по этому вопросу. Изучаю - значит надо. Спасибо
за понимание:)    


Ответы

Ответ 1



// Подключение mysql_connect("localhost","user","pass"); mysql_select_db("db"); mysql_set_charset("utf8") либо если используется mysqli $mysqli = new mysqli("localhost", "user", "pass", "bd"); $mysqli->set_charset("utf8") // Дальше работа с базой При создании базы так же использовать кодировку utf8_general_ci, либо перевести в нее текущию. Так же ставте заголовок charset=UTF-8 и переводите кодировку самого файла (где пишите код и вообще все файлы) в кодировку UTF-8. После понимания синтаксиса советую все делать в mysqli(нежели mysql) т.к. удобнее, есть поддержка, ну и ООП естественно, но это уже потом узнаете) Удачи.

Ответ 2



Возьмите за правило писать в кодировке UTF-8. Сохраните свои скрипты в кодировке utf-8 Отдавайте заголовки, что вы скрипт генерит контент в utf-8 После успешного соединения с БД выполните сразу же такой запрос: 'SET NAMES utf8'; использую перечисленные выше принципы, и проблемы с кодировкой нет.

Ответ 3



Ну вот "по-новому" написанный код: Соединение с БД MySQL Select вернул %d строк.\n", mysqli_num_rows($result)); $myrow = $result->fetch_array(MYSQLI_ASSOC); echo "
".$myrow['name']; echo "
".$myrow['surname']; mysqli_free_result($result); } mysqli_close($link); ?> Сейчас уже не выходит никаких ошибок. На скрине показано, что получается в результате. Еще, в самом начале в meta у меня прописано windows-1251. Если меняю на utf-8, то вообще все выходит кракозябрами. Это как можно изменить?

пятница, 20 декабря 2019 г.

Переменная PHP внутри запроса MySQLi [дубликат]

#php #mysql #mysqli


        
             
                
                    
                        
                            This question already has answers here:
                            
                        
                    
                
                        
                            Как вставить значение переменной внутрь строки?
                                
                                    (4 ответа)
                                
                        
                                Закрыт 3 года назад.
            
                    
Проблема заключается в выводе переменной $set_name внутри MySQLi запроса не в том
виде, как того хотелось бы.  

$set_name в данном примере равно 'name = "John"', но в MySQL запросе, вероятно, выводится
в другом виде и запрос, соответственно, не выполняется.
Если же переменную $set_name заменить на её значение (name = "John"), то запрос выполняется...

$name = 'John';
if(empty($name)) { 
  $set_name = '';
} else {
  $set_name = ' name = "'.$name.'"';
}
$update = $mysqli->query('UPDATE table SET $set_name WHERE id = 1');

    


Ответы

Ответ 1



вам нужно использовать двойные кавычки, вот так: $update = $mysqli->query("UPDATE table SET $set_name WHERE id = 1"); но так делать плохо, посмотрите например в сторону PDO prepare('SELECT name, colour, calories FROM fruit WHERE calories < :calories AND colour = :colour'); $sth->bindParam(':calories', $calories, PDO::PARAM_INT); $sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12); $sth->execute(); ?>

Ответ 2



Ваш вариант опасен внедрением SQL-инъекций. Переменные стОит вставлять в запрос только при условии экранирования значений и только тогда, когда другого варианта нет. В качестве "другого варианта" следует использовать плейсхолдеры и подготовленные запросы. Для используемого вами mysqli и в рамках вашего примера это будет выглядеть так: $stmt = $mysqli->prepare('UPDATE table SET name = ? WHERE id = 1'); $stmt->bind_param('s', 'John'); $result = $stmt->execute(); if (!$result) throw new Exception('SQL error: ' . $mysqli->error, $mysqli->errno);

Ответ 3



Если говорить не о мелкой проблеме с базовым синтаксисом РНР, а о реальной задаче, решаемой в данном случае, то её решение далеко не так просто. Для этого конкретного случая я порекомендую свою библиотеку Safemysql, которая расширяет возможности стандартного драйвера mysqli, и в том числе - как раз работу с с парами вида column='value'; $update = []; $name = 'John'; if(!empty($name)) { $update['name'] = $name; } $db->query('UPDATE table SET ?u WHERE id = ?i', $update, $id); Таким образом можно будет добавить в запрос любое количество полей. Поскольку для всех передаваемых в запрос данных используются плейсхолдеры, мы на 100% защищены от sql инъекций.

воскресенье, 15 декабря 2019 г.

Как реализовать быстрое подключение к БД для живого поиска?

#php #javascript #ajax #mysqli


Здравствуйте!

Я собираюсь сделать на сайте живой поиск (как в Google). Нашел достаточно много материалов
на эту тему. Лишь из-за одного момента я не могу приступить. Мой уровень знаний достаточно
мал и я не могу понять как мне решить следующую проблему:

Вот с сайта, при помощи Ajax поступает запрос на .php скрипт, который должен взять
из базы инфу. Вопрос - как можно быстро подключиться к бд или вообще сделать это заранее?
Объяснений я не нашел. Я так понял, что больше всего времени тратиться именно на подключение
к бд. Подключаться к ней после ввода каждого символа не получится. Все будет виснуть.
Во всех примерах, которые я видел просто писалось $mysqli->query(...). Строк подключения
там не было, словно оно уже как таковое имеется.

Я попытался вникнуть в систему "постоянных подключений", но так и не разобрался,
что к чему. К тому же там ограничения на кол-во одновременных соединений есть.

Очень прошу помочь с этим вопросом. Из-за него буквально стоит весь проект.
    


Ответы

Ответ 1



О том, как реализовать автодополнение наиболее оптимально. Просто поиск в базе однозначно неэффективен: на один ввод пользователя может прийтись до десятка запросов, каждый из которых лезет в базу, в которой теоретически могут лежать хоть миллионы записей, а всякие wildcard поиски обожают страдать, за ними надо внимательно следить, чтобы они действительно использовали индексы; кроме того, упоротый фронтенд, используя плечо в виде количества приходящих на сайт, может легко все положить, отправляя по запросу на каждую букву и мультиплицируя входящий трафик в два-три-пять раз. В общем, путь рабочий, и, более того, при должном усердии, работающий быстро, но не моментально и требующий поддержки; кроме того, каждый инсерт-апдейт будет кромсать индекс и быстродействие. Как решить эту проблему? Во-первых, можно по старой привычке запихнуть все в оперативную память, во-вторых, можно использовать эффективные алгоритмы. Если в случае с базой и индексом скорость поиска будет составлять n или log n в лучшем случае (n - количество записей, по которым производится поиск), то префиксное дерево / префиксный бор и суффиксный автомат позволяют свести скорость поиска до максимальной длины строки - то есть, до константы. Про суффиксный автомат я не могу сказать, так как у меня все нет времени дочитать и собрать воедино прочитанное, а префиксный бор (далее - Trie), по факту, представляет собой почти что обычное дерево: Trie строится для некоторых последовательностей, по которым нужно выполнить поиск (в нашем случае это строки, или последовательности символов). Узел в Trie представляет собой структуру, в которой хранятся значения, о которых будет сказано чуть позже, и ссылки на дочерние узлы в виде map (ассоциативного массива) - каждый дочерний узел сохраняется с ключом типа сущности из последовательности (в нашем случае ключом будет символ). В виде значений хранятся либо непосредственно искомые значения, либо ссылки на них (например, айдишники, хотя если с ними придется еще раз лезть в базу, то этоабсолютно неэффективно). При заполнении Trie последовательности преобразуются в ветви, идущие из начального узла, и в узел, соответствующий последней сущности из последовательности записывается значение, соответствующее этой последовательности. Если я реализую поиск по продуктам, и мне попадется запись "молоко", то я сооружу из нее ветвь из шести узлов (если они еще не существуют в этот момент), и в последний узел запишу сам продукт. Если после этого мне попадется "молоковод", то я сооружу ветвь из девяти узлов (первые шесть из которых уже существуют), и в "д" я запишу непосредственно молоковода. Зачем это все нужно? В случае, если а) загрузить это все в оперативную память, б) держать это там между запросами, чтобы не выгружать каждый раз (здесь с PHP будут проблемы, да, но демонов никто не отменял) и в) выгрузить вообще всю информацию, которая должна выдаваться при поиске (а это редко больше чем две-три строки), то поиск первых N результатов по такому дереву будет выполняться за миллисекунду даже на хреновом железе. Для поиска достаточно просто разложить строку на последовательность символов, найти соответствующий узел, и наполнять корзину поиска из текущего, а потом и дочерних узлов до тех пор, пока дочерние узлы не кончатся или корзина не заполнится доверху. В среднем по больнице сложность поиска будет составлять где-нибудь шестнадцать символов, что до невероятности безумно прекрасно по сравнению с полным проходом всего массива. Единственная проблема Trie - это то, что по ней осуществляется префиксный поиск, и для поиска подстроки придется скармливать ей суффиксы один за одним (эффективный нечеткий поиск вообще сложно представить); также для эффективного поиска обычно рядом надо держать обычный мап вида , где и хранить сами данные, а в Trie лишь id искомых данных. По поводу расхода памяти - если представить, что на каждую структуру расходуется даже один килобайт памяти (что обычно оверкилл), то на каждую тысячу элементов будет расходоваться один мегабайт, что, в общем-то, копейки для современных машин. Ну и, конечно, нужно не забывать скидывать внутрь последовательности (строки) с предварительным преобразованием - Trie не найдет вам строку, если в ее начале оказался лишний пробел, а для регистронезависимого поика нужно скидывать все и искать в одном регистре. И, конечно, Trie надо регулярно обновлять по мере обновления сущностей в базе - лучше всего реализовать одновременно и функционал полной перезагрузки раз в N минут, и функционал обновления отдельной сущности при добавлении / удалении / апдейте.

Ответ 2



Нашел ответ. На странице, на которой будет производиться живой поиск, необходимо создать постоянное соединение с помощью mysqli: /* Пермаментное подключение к БД */ $pMysqli = new mysqli('p:'.DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME); Для того, чтобы подключение было постоянным, необходимо добавить префикс "p:". Об этом я знал. После этого, в начале php скрипта, к которому обращается ajax, необходимо написать ту же строчку. Если соединение уже существует, то никакого подключения не состоится, зато у вас уже будет объект соединения. Пример: Страница, на которой есть форма живого поиска - somepage.php Обработчик Ajax запроса - handler.php query("SELECT Content FROM TestTable ORDER BY Content LIMIT 1"); /* Работа с полученными данными */ ?>

Как получить ответ на запрос sql к базе без сортировки результата? [дубликат]

#mysql #sql #база_данных #mysqli


        
             
                
                    
                        
                            This question already has an answer here:
                            
                        
                    
                
                        
                            MySQL: сортировка выборки в порядке, заданном в операторе IN
                                
                                    (1 ответ)
                                
                        
                                Closed 3 года назад.
            
                    
Делаю запрос к базе на выборку товаров по id, причем id следуют в определенном порядке.
Например, часть запроса FROM oc_product p WHERE p.product_id IN ('574','572','573'). 
В ответ получаю отсортированный массив. 
Мне надо получить массив с таким же порядком id, как и в запросе. Обработку на php
полученного массива уже реализовал. Интересует можно ли сохранить порядок следования
id именно запросом sql.
    


Ответы

Ответ 1



Можно воспользоваться функцией FIELD(), передав ей точно такую же последовательность, которую вы задаете в IN. Функция будет возвращать индекс значения в последовательности и по нему можно отсортировать выборку конструкцией ORDER BY. SELECT * FROM oc_product p WHERE p.product_id IN ('574','572','573') ORDER BY FIELD(p.product_id, '574','572','573')

пятница, 13 декабря 2019 г.

Вывод данных из бд с сортировкой

#php #mysql #sql #mysqli


Есть таблица db_weapons. 



Как составить подзапросы mysql для вывода из таблицы всех значений, но при этом сортируя
их по категориям которые записаны в ячейке quality

Что бы выведенный вид был таков: 

----------------------------
id: 3, quality: 'base_grade',
id: 5, quality: 'base_grade',
id: 4, quality: 'exotic',
id: 6, quality: 'exotic',
id: 2, quality: 'restricted',
id: 7, quality: 'restricted',
id: 1, quality: 'covert'
----------------------------


Это всё для того что бы не сортировать силами php
Но есть  кто-то знает решение на php которое займёт мало времени на обработку, то
я буду рад принять помощь и рассмотреть пример.
    


Ответы

Ответ 1



SELECT * FROM db_weapons WHERE quality IN ('base_grade','exotic','restricted','covert') ORDER BY FIELD(quality, 'base_grade','exotic','restricted','covert')

Ответ 2



Вам надо создать справочник групп, примерно такой: ord quality 1 exotic 2 base_grade 3 restricted 4 covert И запрос выборки из основной таблице сделать тогда таким: select A.* from db_weapons A, qualityOrd B where B.quality=A.quality order by B.ord В любом случае SQL надо явно сказать в каком порядке должна быть сортировка, волшебного оператора "отсортируй как мне хочется" к сожалению не предусмотрено. Но по хорошему, даже при отсутствии необходимости особой сортировки такой справочник нужен. Не стоит хранить в основной таблице повторяющиеся текстовые значения. При вводе текста человек может ошибиться буквой и это будет уже другая группа. И если захочется переименовать группу или придать группе еще и русское название - ему придется делать это во всех записях. Обычно в подобных ситуациях названия хранятся в справочнике, а в основной таблице только ID группы.

Ответ 3



Например ORDER BY + CASE SELECT * FROM db_weapons ORDER BY CASE quality WHEN quality = 'base_grade' THEN 1 WHEN quality = 'exotic' THEN 2 WHEN quality = 'restricted' THEN 3 WHEN quality = 'covert' THEN 4 ELSE 99 --неизвестные в конце END

Ответ 4



Покапай в сторону GROUP BY, а потом уже будешь вытаскивать группы по очередности которые нужны.

воскресенье, 1 декабря 2019 г.

Какой extention выбрать для работы с MySQL в PHP?

#php #mysql #mysqli


Насколько я знаю, существует mysql, mysqli и пр... Раньше я использовал mysql, но
это было лет 5 назад. Сейчас ситуация существенно поменялась, и многие статьи рекомендуют
использовать mysqli.

Уважаемые знатоки PHP программирования, помогите опытом, чем же так хорош mysqli
или может стоит использовать что-то другое?
    


Ответы

Ответ 1



Расширение mysql официально признано устаревшим. Это означает, что нет гарантий его дальнейшей поддержки (в том числе и с точки зрения безопасности). Поэтому это расширение нельзя использовать ни в одном новом проекте. Остается выбор между mysqli и PDO. Расширение mysqli mysqli - это наиболее простая замена mysql. Большинство функций и методов mysqli имеют синтаксис, схожий с синтаксисом расширения mysql. Это позволяет достаточно просто переключится с одного расширения на другое. Например: // mysql $link = mysql_connect(); $res = mysql_query('SELECT * FROM tbl', $link); var_dump(mysql_fetch_assoc($res)); // mysqli $link = mysqli_connect(); $res = mysqli_query($link, 'SELECT * FROM tbl'); var_dump(mysqli_fetch_assoc($res)); В тоже время, есть и ряд улучшений, связанных безопасностью (плейсхолдеры) и объектным подходом. Очевидный минус mysqli - привязка кода к работе с MySQL. В ряде случаев, это может затруднить переход к использованию других баз данных (если это конечно потребуется). Расширение PDO PDO представляет собой дополнительный уровень абстракции над базой данных. Теоретически, один и тот же PHP код может работать с любой SQL совместимой базой данных, если для нее есть соответствующий драйвер PDO. (На практике, проблема с различными БД все равно остается из за различий в синтаксисе SQL.) PDO проповедует объектный подход, поэтому и код будет существенно отличаться от кода с использованием mysql. Например: // mysql $link = mysql_connect('localhost', 'user', 'pass'); mysql_select_db('testdb', $link); $res = mysql_query('SELECT * FROM tbl', $link); var_dump(mysql_fetch_assoc($res)); // PDO $dbh = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'pass'); $stm = $dbh->prepare('SELECT * FROM tbl'); $stm->execute(); var_dump($stm->fetch(PDO::FETCH_ASSOC)); Помимо прочего, PDO предоставляет набор дополнительных возможностей, связанных с безопастностью (плейсхолдеры) и скоростью выполнения запроса (подготовленные запросы). Хотя этих возможностей нет mysql, часть из них реализована в mysqli. Резюмирую: в большинстве случаев, я бы рекомендовал использовать PDO, поскольку интерфейс работы с mysqli слишком низкоуровневый и часто требует создания собственного уровня абстракции над БД.

Ответ 2



Выбор очень прост. Если ты понимаешь необходимость использования дополнительного уровня абстракции над низкоуровневыми функциями доступа к БД, и в состоянии написать такой, то надо использовать mysqli. В противном случае единственно правильным выбором будет PDO. В любом случае, главой ошибкой будет, если сменив экстеншен ты сохранишь старый подход, добавляя переменные в запрос напрямую. Единственная причина, по которой надо переходить на новые экстеншены - это использование подготовленных выражений. Иначе смысла переходить нет - гуано-кодить можно продолжать и на старом экстеншене. В свете вышесказанного не надо обманываться видимой легкостью перехода на mysqli. Работа с подготовленными выражениями никакого аналога в mysql не имеет, и, следовательно, никакой "простоты" при переходе не предоставляет.

Ответ 3



Если у тебя весь проект реализован на классах я бы рекомендовал смотреть в сторону Doctrine 2 если нет тогда PDO. Есть мнение что mysqli это низкоуровневой расширения, которое должно использоваться для написания библиотек.

воскресенье, 24 ноября 2019 г.

Защищают ли подготовленные выражения/переменные полностью от SQL инъекций?


В мире уже давно используются mysqli и PDO. Многие очень активно их пропагандируют: есть подготовленные переменные, всё становится безопасно и прочее.

Вот, допустим, есть абстрактный код:

$dbh = new PDO("test");    
$stmt = $dbh->prepare('SELECT * FROM users where username = :username');
$stmt->execute(array(':username' => $_REQUEST['username']));


или

$dbh = new PDO("test");  
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit WHERE calories < ? AND color = ?');
$sth->execute(array($_POST['number'], $_POST['color']));


И всё...

Этого достаточно и ничего больше не надо делать? Никакие конструкции вида mysqli_real_escape_string (для mysqli) и прочие шаманства? Или все же нет?

Собственно, хотелось бы знать, если код выше не защищает, то как делать правильн
с запросами с PDO и mysqli (и почему тогда говорят про безопасность)? Какие есть наглядные примеры безопасного исполнения запроса, используя PDO и mysqli?  А Если и правда защищает, то... я в шоке))



P.S. Возможно данный вопрос уже рассматривался, не знаю, заранее извините.
    


Ответы

Ответ 1



Безопасность Если говорить о числовых и строковых литералах в запросе - то да, защищают. Этого достаточно и ничего больше не надо делать? В общем случае - ничего. В случае с ПДО также желательно еще выставлять кодировку соединения в DSN, но это в любом случае нужно делать. Идея подготовленных выражений не в том, чтобы отправить данные и запрос отдельно а в том, чтобы добавлением данных в запрос занимался не программист, а драйвер БД. А уж как оно у него там внутри реализовано - дело десятое. Примечание По настоянию уважаемого vp_arth я должен добавить, что существует теоретическая возможность так специально настроить свою систему, чтобы она пропускала инъекции в режиме эмуляции. Для этого потребуется две вещи: специальным образом настроить mysql, задав режим NO_BACKSLASH_ESCAPES использовать двойные кавычки вместо одинарных в качестве ограничителей строк Это был баг в mysql, который был исправлен только недавно. Подробнее можно почитать в этом посте на SO. Ограничения Куда интереснее здесь вопрос, что делать, когда подготовленные выражения использоват невозможно. Как говорилось выше, параметры в запросе можно использовать дла замены тольк строковых или числовых литералов. Но бывают случаи, когда в запрос надо подставить не данные, а имя столбца. Вот пример такой ситуации с разбором неправильных решений (по-английски): An SQL injection against which prepared statements won't help. Ситуация нечастая, но о ней надо знать и быть к ней готовым. Удобство В конце концов, подготовленные выражения просто удобнее. Сравним олд скул $name = $mysqli->real_escape_sring($_GET['name']); $price = $mysqli->real_escape_sring($_GET['price']); $color = $mysqli->real_escape_sring($_GET['color']); $sql = "SELECT * FROM goods WHERE name='$name' and color='$color' and price > '$price'"; $res = $mysqli->query($sql); и PDO prepared statements $stmt = $pdo->prepare("SELECT * FROM goods WHERE name = ? and color = ? and price > ?"); $stmt->execute([$_GET['name'],$_GET['price'],$_GET['color']]); -- компактно, аккуратно и безопасно. Мифы про экранирование Отдельное замечание по поводу "конструкций вида mysqli_real_escape_string". В том-т и штука, что в отличие от подготовленных выражений, эти конструкции никакого отношени к защите от SQL инъекций не имеют. Эта конструкция выполняет строго определенную и очень специализированную синтаксическую функцию. применение же её "для защиты от инъекций" гарантированно к такой инъекции и приведет. Пример такого нецелевого использования приведен по ссылке выше, но можно привести и другой, совсем уж дурацкий но от этого еще более наглядный: $id = $mysqli->real_escape_string($_GET['id']); $sql = "SELECT * FROM table WHERE id=$id"; Если думать, что функция служит "для защиты от инъекций", то этот код логичен. Однак в реальности он дает возможность приписать дальше практически любой запрос через UNION и получить классическую инъекцию. При этом не надо ударяться и в другую крайность - примененная по назначению, дл экранирования спецсимволов в строках, mysqli_real_escape_string прекрасно справляется с инъекциями, просто в качестве побочного эффекта.

Ответ 2



Кратко Механизм prepared statements в контексте безопасности только обеспечит корректну передачу самого запроса и значений параметров для него из кода в СУБД. Не меньше, но и не больше. Отстрелить ногу всё так же возможно. Но это становится сильно труднее, чем если пытаться подставлять данные сразу в запрос. Чтобы ответить на вопрос безопасности надо сначала понять, в чём опасность. Что такое sql-инъекции и как они происходят? $pdo->query("select * from users where login = '" . $login ."' and pass_md5 = '" . md5($pass) . "'"); Что здесь видит разработчик? Подстановку данных в строку. С точки зрения PHP здес ничего опасного нет. С точки зрения неопытного PHP-разработчика, проверяющего свой код - код работает, ему ведь не пришло в голову написать что-то странное в логин. Приключения начинаются, когда кто-то вместо логина вводит admin' or '1'='1. Здес необходимо напомнить, что SQL - штука изначально текстовая. Что после конкатенации на PHP получит СУБД? select * from users where login = 'admin' or '1'='1' and pass_md5 = 'какой-тоmd5' Это одна строка непрерывного текста. Как СУБД должна понять, что этот запрос отличаетс от задуманного? Это запрос, он синтаксически корректен, его можно выполнить - СУБД его и выполняет. Но запрос уже делает не то, что хотел сказать разработчик. Опасность и распространённость SQL-инъекции именно в текстовой сущности запроса Очень просто подставить в нужное место переменную с данными - но это путь к ошибке и так делать нельзя. Теперь защита. В самом SQL изначально предусмотрено, что для корректного представления строки литерало в запросе необходимо определённым образом кодировать определённые байты. В большинстве случаев, экранировать кавычки: select * from users where login = 'admin\' or \'1\'=\'1' and pass_md5 = 'какой-тоmd5' Теперь парсер понимает, где в строке логина кончаются данные. При этом критично важна согласованность кодировок клиента и сервера, иначе в некоторых случаях можно и пробить. С другой стороны к вопросу подходит механизм prepared statements: у нас есть конечно число запросов на приложении, но использующие разные данные. Поэтому для prepared statement решили явным образом разделить структуру запроса и данные. Теперь вместо ситуации "привет выполни вот этот запрос" приложение через специальный протокол говорит СУБД "привет приготовься выполнить вот такой запрос", а затем: "помнишь вот тот запрос готовили Теперь выполни его и используй в качестве первого параметра - эти следующие 8 байт, а для второго параметра - следующие 20 байт". Данные для запроса передаются физически отдельно от структуры запроса, поэтому СУБД в принципе не может перепутать данные с запросом, даже при том, что никакого экранирования не происходит. Важный момент - именно поэтому через механизм prepared statemenets невозможно изменить структуру запроса, даже банально изменить направление сортировки order by. Данные будут переданы как есть без искажений ни самих данных, ни структуры запроса, всё хорошо. Вот только достаточно ли этого? Сначала стоит сказать про по-умолчанию включенную эмуляцию подготовленных выражений Это не страшно, пока у вас корректно установлена кодировка соединения и даёт некоторые бонусы. А вот если кодировка стоит неверно - то атака на подобии указанной выше через кодировку вновь реальна. С PDO::ATTR_EMULATE_PREPARES в значении true за обработку подготовленных выражени отвечает сам PDO. В базу данных передаётся чистый запрос с уже подставленными и корректно экранированными данными (только в dsn не забывайте правильно charset указывать). PDO::ATTR_EMULATE_PREPARES в значении false именно использует штатный механизм СУБ для подготовки запроса и затем отдельным обращением передаёт данные для этого запроса. Т.е. нормальные, реальные подготовленные выражения. Из не очевидных моментов: для сложных запросов дико удобно использовать один и тот же именованный парамет несколько раз в запросе. Какой-нибудь where user_from = :id or user_to = :id и передат в списке параметров id только один раз. На самом деле зависит от реализации конкретного драйвера, например, в mysql такой запрос при выключенной эмуляции завершится ошибкой Invalid parameter number, но он же будет корректно исполнен в PostgreSQL. С включенной эмуляцией - соответственно можно использовать вне зависимости от СУБД. специфика конкретных СУБД. Какие-то СУБД из тех, что умеет PDO могут не уметь подготовленны выражения. Например, очень популярный PgBouncer (пул коннектов для PostgreSQL) не умеет обрабатывать подготовленные выражения. С эмуляцией выражений можно в коде проекта пользоваться удобствами API с prepare вопрос с тем, что подготовленный запрос разбирается и строит план один раз и зате только выполняется - на самом деле гораздо сложнее. Тот же mysql сохраняет запрос тольк в рамках соединения. Поэтому в типичном сценарии использования "подготовил, выполнил закрыл соединение" никаких плюсов реальное препарирование не даёт. А кэширование плана между соединениями - по-моему (сам с этой СУБД не работал), есть в Oracle и приносит некоторое количество головной боли, ведь оптимальный план запроса в немалой степени зависит от самих данных. Проиллюстрирую головную боль с кэшированием плана запроса на простой очереди. В колонк статус только десяток записей в статусе waiting, но несколько миллионов в статусе done. В базу приезжает запрос: select /**/ from tablename where status = ? order by id limit ? Спрашивается, как без самих данных база угадает оптимальный план? Если ей приеде запрос на status = waiting, оптимально идти по индексу status. Если придёт запрос в статусе done - есть ещё несколько вариантов: при малом лимите имеет смысл пойти по индексу id и просто выкидывать записи с неподходящим статусом. И вновь включаем голову Есть, например, запрос: $stmt = $pdo->prepare('update users_balance set balance = balance - :amount where user_id = :uid and balance > :amount'); $stmt->execute([ 'uid' => $userId, 'amount' => $_POST['payment'], ]); Безопасно ли так оставить? Нет. Передайте в payment отрицательное число и вы бе проблем получите начисление денег вместо списания. Логика запроса не нарушена, данные передаются как есть, не искажаются - но бизнес логика приложения сломана. Поэтому данные проверять вы всё равно обязаны. И никакой серебряной пули вам не буде - именно вы знаете, и именно в конкретном месте кода - должно ли дальше быть тольк отрицательное число. Или строка логина или ещё что-нибудь. А механизм prepared statements только обеспечит корректный транспорт самого запроса и значений из кода в СУБД и не более того. Здесь же стоит сказать про то же самое изменение сортировки запроса. Механизм prepare statements вам это сделать не позволит, но вы заранее знаете, что направлений сортировк есть только два: asc или desc, а так же заранее знаете, по каким полям выборку можно сортировать. Поэтому направление сортировки элементарно проверяется по белому списку возможных значений и проблемы не представляет. Ещё чуть-чуть о производительности Может показаться, что динамически собирать структуру запроса опасно и вообще нельзя к тому же медленно каждый раз запрос парсить заново. Это миф. В результате получается один большой запрос вроде такого SELECT first_name, last_name, subsidiary_id, employee_id FROM employees WHERE ( subsidiary_id = :sub_id OR :sub_id IS NULL ) AND ( employee_id = :emp_id OR :emp_id IS NULL ) AND ( UPPER(last_name) = :name OR :name IS NULL ) И чтобы не использовать какое-то из условий поиска достаточно его параметр прост указать как NULL. Да, на парсере запроса мы сэкономили, зато предельно усложнили жизн оптимизатору. Не имея реальный данных запроса оптимизатор вынужден использовать тольк последовательное чтение всей таблицы. Но если параметризованный запрос заменить литералами значений - то оптимизатор вполне хорошо понимает, что от него хотят и использует более внятный план. Т.е. таким запросом через prepared statements мы не улучшили производительность приложения, а убили её в корне.

Ответ 3



Если база поддерживает раздельную передачу запроса и данных, а драйвер базы данных использует эту фичу - вы в безопасности. Данные переданные отдельно от запроса никак не могут повлиять на исходный запрос. Однако, есть такое понятие, как "эмулированные подготовленные выражения"(Emulated Prepared Statements). Они используются, если база не поддерживает реальных prepared statements, либо есл мы сознательно включили эту "фичу"(в некоторых драйверах она может быть включена по умолчанию). В случае эмуляции, база данных не знает, где данные, а где запрос. За экранировани данных мы всецело полагаемся на реализацию этой эмуляции внутри конкретного драйвера(pdo_firebird, pdo_mysql). Так что, если вы ещё не убедились в наличии этого параметра подключения, убедитесь и отключите его явно: $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); Если вам необходимо использовать пользовательские данные не как данные, а скажем, как имя таблицы/поля - единственным способом обезопасить себя на 100% - белые списки. Никогда не используйте пользовательские данные в запросе напрямую, берите нужны вставки в запрос из списка разрешённых для этого вставок. $i = array_search($_GET['field'], User::$orderFields, true); $orderField = $i === false ? User::$defaultOrderField : User::$orderFields[$i]; $sql = "... ORDER BY $orderField"; Много лучше принимать от пользователя не имена полей/таблиц, а индексы в таких белых списках. Из обсуждения под соседним вопросом: @vp_arth: На самом деле, интереснее был бы пример со строками в двойных кавычках в запросе, в то время, как real_escape_string по умолчанию заточена под апострофы Имелся в виду вот этот пример: /* my.ini [mysqld] sql-mode="NO_BACKSLASH_ESCAPES"; Или вызов из php:*/ $db->query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); $input = '" OR id = "1'; $input = $db->real_escape_string($input); echo 'SELECT * FROM Users WHERE login = "'.$input.'"'; // SELECT * FROM Users WHERE login = "" OR id = "1" @vp_arth: защищающие не больше, чем вышеупомянутый real_escape_string Имелось в виду следующее утверждение из вопроса: @Ипатьев: Эта конструкция выполняет строго определенную и очень специализированную синтаксическую функцию В случае эмуляции подготовленных выражений, они также "выполняют строго определенную и очень специализированную синтаксическую функцию", которую в общем случае можно обойти синтетический пример В реальной жизни сталкивался с тем, что emulated prepared statements в pdo_firebird могут падать с Segmentation fault в зависимости от количества байт в utf-8 строках.

Ответ 4



Подготовленные запросы повышают безопасность, избавляют от необходимости экранировать кавычки и другие спецсимволы, избавляют от необходимости заключать вставляемые строковые значения в кавычки вообще, повышают быстродействие за счет кеширования плана выполнения запроса на стороне сервера, повышают читаемость кода. Поэтому я один из тех, кто агитирует за повсеместное использование этого подхода. Если говорить про шаманство, которое приходится выполнять вне зависимости от способ выполнения запроса, — то это устранение HTML-разметки из данных, поступающих от пользовател (http://www.php.net/Strip_tags). Не всегда это дейтсвительно необходимо, но в тех случаях, когда, например, сохраняются сообщения на форуме, игнорирование этой функции может привести к нарушению разметки всей страницы или к выполнению нежелательного js-кода у всех пользователей, открывших страницу. Но к выполнению запросов это напрямую не относится.

Ответ 5



Видел как на многих самописных движках берут и тупо из $_POST экранируют через PDO, и кидают в базу. Да, все окей вроде-бы. НО. Но тут нас ждет XSS! Строка вида Даже при экранировании через PDO так и попадет в базу, а потом мучайся, где и как накосячил.

mysql_fetch_array() expects parameter 1 to be resource (or mysqli_result), boolean given


Я пытаюсь получить данные из таблицы MySQL, но вылезает одна из этих ошибок:


  mysql_fetch_array() expects parameter 1 to be resource, boolean given


или


  mysqli_fetch_array() expects parameter 1 to be mysqli_result, boolean given


Вот мой код:

$username = $_POST['username'];
$password = $_POST['password'];
$result = mysql_query("SELECT * FROM Users WHERE UserName LIKE $username");

while($row = mysql_fetch_array($result))
{
    echo $row['FirstName'];
}


Оригинальный вопрос.
    


Ответы

Ответ 1



Как избежать такой ошибки Эта ошибка - вторичная. И в правильно спроектированном приложении возникать в принципе не должна. Она лишь сигнализирует о том, что предыдущая функция, которая выполняла SQL запрос, окончилась неудачей, но при этом о причине неудачи никакой информации не несёт. Чтобы таких ошибок в коде не возникало, необходимо проверять результат той само предыдущей функции, выполнявшей SQL запрос. Но делать это надо с умом, а не так, ка советуют неспециалисты, десятилетиями переписывая друг у друга один и тот же код, не понимая его смысла и не сталкиваясь с результатами его работы (весьма плачевными) на практике. Вызывая функцию mysql_query(), необходимо всегда проверять результат её работы. если функция вернула не корректный ресурс, а пустоту, то необходимо, во-первых, получит от mysql сообщение об ошибке, а во-вторых, транслировать его в ошибку РНР (это принципиальный момент, которого начинающие пользователи РНР не понимают поголовно). А в-третьих, очень полезно бывает добавить в сообщение об ошибке сам запрос. За первое отвечает функция mysql_error(), за второе - trigger_error(), а для третьег необходимо всегда сначала присваивать запрос переменной. Таким образом, любой вызов mysql_query() должен выглядеть так: $sql = "SELECT ..."; $res = mysql_query($sql) or trigger_error(mysql_error()." in ". $sql); Таким образом, при возникновении ошибки исполнения запроса, пользователь РНР буде немедленно проинформирован точно так же, как о любых других возникающих в работе скрипта ошибках. А до ошибки "expects parameter" дело уже не дойдет. В принципе, ещё лучше чем trigger_error(), было бы бросить исключение. Но поскольку throw new Exception() не подставишь так красивенько через or в ту же строку; начинающие пользователи РНР очень плохо представляют себе механизм исключений и тут же начинают использовать его неправильно; расширение mysql уже потеряло всякий смысл, а два оставшихся - mysqli и PDO умеют транслировать ошибки БД в исключения автоматически, то предлагать исключения для mysql_query() как-то глупо. Но в любом случае, как бы ни обрабатывалась ошибка, она должна следовать двум непреложным правилам: Никаких echo и die()!!! Ошибки базы данных должны всегда транслироваться в ошибк РНР и выводиться туда, куда выводятся все остальные. Если на сайте запрещен вывод ошибок в браузер, то ошибки БД не должны быть исключением из этого правила. Сообщение об ошибке обязательно должно содержать имя файла и строку, в которой произошла ошибка, а по возможности - ещё и трассировку вызовов. Как исправить ошибку. Надо прочитать сообщение об ошибке. Это звучит банальностью, но на удивление никто из неспециалистов раздающих совет никогда этого не упоминает! При том что прочтение текста ошибки помогает в сто раз лучше шаманских телодвижений типа "пересчитайте все кавычки": Во-первых, mysql сразу скажет, в чем суть ошибки. Если в базе нет таблицы, к которой мы обращаемся, или сервер весь целиком упал, то пересчитывать кавычки бесполезно. Во-вторых, если ошибка все-таки в синтаксисе, то mysql точно укажет место где е искать - она процитирует кусок запроса, начинающийся сразу за ошибкой. Как раз и навсегда избавиться от ошибок синтаксиса, вызванных данными. Если проблема всё-таки в синтаксисе, и при этом вызвана переданными в запрос данными то Самой Дурацкой Идеей будет "экранировать ваши значения с помощью mysql_real_escape_string()". И уж тем более глупостью будет применять эту функцию для защиты от SQL инъекций. Она не для этого предназначена. Для того, чтобы навсегда избавиться от любых проблем, связанных с передаваемыми запрос переменными, необходимо перестать вставлять их в строку запроса напрямую. А делать это только через посредника, называемого "плейсхолдер". Драйвер для работы с БД через плейсхолдеры можно написать на основе любого API будь это mysql, mysqli или PDO. Но поскольку, во-первых, для этого нужно обладать специальным знаниями, а во-вторых начинающие пользователи РНР до ужаса боятся любых готовых библиотек, предпочитая пользоваться лишь встроенными средствами языка, то у них остаётся только один выбор - PDO. Mysqli Чтобы транслировать ошибки базы данных в ошибки РНР, в mysqli не нужно проверят результат каждой функции. Вместо этого достаточно перед коннектом написать вот такую строчку: mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); и тогда все ошибки БД будут порождать исключения, которые по умолчанию становятся фатальными ошибками РНР и в таком виде станут доступными программисту.

Ответ 2



Откуда берётся ошибка Эта ошибка возникает, если запрос не может быть выполнен. Функции, которые могу к ней привести: mysql_fetch_array/mysqli_fetch_array() mysql_fetch_assoc()/mysqli_fetch_assoc() mysql_num_rows()/mysqli_num_rows() Если с запросом всё в порядке, и просто результат его выполнения пустой, то ошибка не возникает. К ошибке приводит только неверный синтаксис SQL. Как найти источник ошибки Убедитесь, что на сервере, на котором вы ведёте разработку, включено отображени всех ошибок. Вы можете включить его из PHP, выполнив error_reporting(-1); (можно разместить этот код в конфигурационном файле вашего сайта, например). Если при попытке выполнения запроса будут обнаружены ошибки в синтаксисе, то они будут отображены. Используйте mysql_error(). Эта функция вернёт строку с текстом ошибки, если таковая возникла при выполнении последнего запроса. Например: mysql_connect($host, $username, $password) or die("Ошибка подключения"); mysql_select_db($db_name) or die("Ошибка выбора БД"); $sql = "SELECT * FROM table_name"; $result = mysql_query($sql); if ($result === false) { echo mysql_error(); } Выполните ваш запрос в командной строке MySQL или из инструмента вроде phpMyAdmin. Если в запросе есть синтаксическая ошибка, то она будет отображена. Убедитесь, что в запросе верно расставлены кавычки. Это частая причина синтаксических ошибок. Убедитесь, что вы экранируете ваши значения. Если в строке присутствует кавычка это может привести к ошибке (а также сделать ваш код уязвимым к SQL-инъекциям). Используйте для этого mysql_real_escape_string(). Убедитесь, что вы не используете одновременно функции mysqli_* и mysql_*. Их использование нельзя смешивать. (Если вы не знаете, что выбрать, отдайте предпочтение mysqli_*.) Советы Не используйте функции mysql_* в новом коде. Разработчики PHP больше не поддерживаю и не развивают их, они отмечены как устаревшие, и в будущих версиях будут удалены. Ознакомьтес с понятием prepared statement и переходите на использование PDO (PHP Data Objects) или MySQLi (MySQL improved). Это избавит вас от проблем с экранированием значений и убережёт от SQL-инъекций. И MySQLi, и PDO поддерживают режим, в котором при ошибках выбрасываются исключения В новом коде следует использовать этот подход, потому что он помогает обрабатывать ошибки в одном месте, а не размазывать проверки по всему коду, и позволяет обработке ошибок не зависеть от внешних условий (от конфигурации). Сравнение возможностей PDO и MySQLi. Если вкратце, то PDO — это общий слой над базам данных, который позволяет относительно легко переключать СУБД; а MySQLi даёт доступ к некоторым дополнительным возможностям СУБД MySQL. Преимущественно перевод ответа.

Ответ 3



$username = $_POST['username']; $password = $_POST['password']; if ($result = mysql_query("SELECT * FROM Users WHERE UserName LIKE $username") and mysql_num_rows($result)){ while($row = mysql_fetch_assoc($result)) { echo $row['FirstName']; } }else{ if (!mysql_num_rows($result)){ echo "empty result"; }else{ echo mysql_error(); } }

понедельник, 8 июля 2019 г.

INSERT запрос без VALUES

Привет всем. Вот вопрос, который я имел в виду (не для SET):
Есть запрос такой:
mysqli_query($dataBase, "INSERT INTO `Users` VALUES ('','$email','$name','$pass','$gend','$startImg');");
Который хотелось бы записать примерно так:
INSERT INTO `Users` VALUES `id` = 2, `email` = '$email', `nickname` = '$name';
Как это сделать правильно. PDO желания использовать нет (имеенованые плейсхолдеры).


Ответ

INSERT INTO `Users` SET `id` = 2, `email` = '$email', `nickname` = '$name';
Ссылка на документацию, второй вариант синтаксиса.

воскресенье, 7 июля 2019 г.

Постоянное соединение с базой MySQL

Из-за повторных соединений с базой возникает существенная нагрузка на сервер MySQL, на эти операции приходится 64% от общей нагрузки, неизбежно приходиться смотреть в сторону постоянного подключения, подскажите как лучше это сделать? PDO или Myqli? Сейчас сайт сверстан на PDO. Читал в интернете, что при постоянном соединений не рекомендуется PDO. С радостью приму также дополнительные советы.
Вот статистика нагрузки на базу:


Ответ

Сначала, ссылка, просто прочитайте всё там - минут 10, зато многие вопросы сами собой отпадут.
Не понимаю, откуда взялась эта дурацкая легенда о том, что нужно обязательно "закрывать" соединение? Может так и было в php версии 4 и ниже, на устаревшем ныне расширении mysql, но в PDO совершенно точно это не так. Вот просто реально, все кто про это пишет - зайдите и еще раз внимательно прочитайте
При успешном подключении к базе данных в скрипт будет возвращен созданный PDO объект. Соединение остается активным на протяжении всего времени жизни объекта. Чтобы закрыть соединение, необходимо уничтожить объект путем удаления всех ссылок на него (этого можно добиться, присваивая NULL всем переменным, указывающим на объект). Если не сделать этого явно, PHP автоматически закроет соединение по окончании работы скрипта.
Как правильно пишут товарищи в комментариях - по-хорошему, за выполнение скрипта соединение должно создаваться один лишь раз. Как вы это реализуете - без разницы: можно использовать глобальную переменную, можно использовать статическую переменную функции, можно использовать статический метод класса (со статической переменной класса). Все кто пишут просто "статические методы - плохо", это, конечно, гениально, повторять за большинством, но я реально не вижу ни одной разумной причины, по которой, например, для каждого объекта люди почему-то создают новое соединение. И уж совершенно точно не нужно на каждый запрос открывать отдельное соединение. Раз уж у вас есть PDO - используйте его возможности на максимум:
а. используйте подготовленные запросы и никогда не вклеивайте переменные вручную в строку запроса.
б. где нужно именно выполнить несколько запросов в цикле - делаете PDO::prepare перед циклом, записываете в переменную, далее в цикле уже выполняете PDOStatement::execute и PDOStatement::fetchAll. Но всё же в большинстве случаев рекомендую подумать - а нельзя ли все необходимые данные вытянуть, всё же, одним запросом.
в. ознакомьтесь с константами PDO::FETCH_*. чаще всего необходимы:
PDO::FETCH_ASSOC - почти всегда (рекомендуется его вообще прописать в опциях соединения, как режим извлечения по-умолчанию) PDO::FETCH_COLUMN - когда нужно получить массив, в котором собраны все значения лишь по одному из столбцов в бд PDO::FETCH_KEY_PAIR - ключ - первое поле выборки, значение - второе поле выборки (например массив [ id_клиента => имя_клиента ]). PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC - когда нужно получить массив, ключом которого станет первое поле из выборки, а значением - массив данных. PDO::FETCH_GROUP | PDO::FETCH_ASSOC - вообще чудеснейшая комбинация, по первому полю выборка группируется и раскладывается на отдельные "подмассивы".
P.S. ну и, не относится напрямую к PDO, не забывайте про ключи и предподготовку статистических данных (судя по тому, что у Вас всего три апдейта за сутки - это должно сократить время выборок в разы).

среда, 12 июня 2019 г.

Добавление посещения учеников в одном запросе

Задача: Одним нажатием занести в БД посещения учеников за текущий день. Вывожу всех учеников, которые должны прийти сегодня. Создаю checkbox со значением "1" при посещении и "0" при пропуске урока. Не знаю, как сделать так, чтобы при нажатии на кнопку "добавить", все эти значения добавились в отдельные поля в БД, одним запросом. Вот так:


Ответ

Сам запрос может выглядеть приблизительно так:
UPDATE YOUR_TABLE SET visited = 1 WHERE id IN (1, 2, 3, 4, 5 ...);
Если использовать PDO можно сделать так.
Допустим у Вас есть массив айдишников;
$ids = array(1, 2, 3, 4);
создаём сроку из количества вопросов (?)
$place_holders = implode(',', array_fill(0, count($ids), '?'));
Создаём стороку запроса
$sql = "UPDATE YOUR_TABLE SET visited = 1 WHERE id IN ($place_holders)";
Выполняем запрос
$sth = $dbh->prepare($sql)->execute($ids);

четверг, 6 июня 2019 г.

Скрипт вывода товаров, очень долго грузится

Функции выборки товаров из базы: function result_to_array($result) { $res_array = array(); $count = 0; while( $row = $result->fetch_assoc() ) { $res_array[$count] = $row; $count++; } return $res_array; }
function get_product() { connect_db(); global $mysqli; $result = $mysqli->query("SELECT *FROM `product` ORDER by `id` DESC"); $result = result_to_array($result); return $result; } На самой странице все товары выводятся циклом foreach: $products = get_product();

html код
Проблема в том, что страница с товарами открывается ну очень долго, раньше просто на этой же самой странице делал запрос и выводил все через while, по скорости было ощутимо быстрее. Подскажите, что я делаю не так и почему так медленно работает скрипт?


Ответ

А много ли товаров? И можно оптимизировать конструкцию, убрав $count while( $row = $result->fetch_assoc() ) { $res_array[] = $row; } Перед выполнением кода и после выполнения добавьте microtime() function result_to_array($result) { $ra_start = microtime();
$res_array = array(); $count = 0; while( $row = $result->fetch_assoc() ) { $res_array[$count] = $row; $count++; }
$ra_end = microtime(); echo $ra_end - $ra_start; return $res_array; }
function get_product() { $gp_start = microtime();
connect_db(); global $mysqli; $result = $mysqli->query("SELECT *FROM `product` ORDER by `id` DESC"); $result = result_to_array($result);
$gp_end = microtime(); echo $gp_end - $gp_start; return $result; } На самой странице все товары выводятся циклом foreach: $products = get_product();
html код: По результатам сможете увидеть, какой кусок у Вас долго выполняется.

воскресенье, 12 мая 2019 г.

Когда выбирать реляционную БД, а когда не реляционную?

Пишу сайт. Подразумевается большое количество записей разного размера. Подумал о том, что при большом объёме записей БД будет долго обрабатывать запрос, поэтому надо её раскинуть на несколько серверов. но вычитал, что реляционные БД плохо масштабируются и для больших объемов данных используют не реляционные.

подВопрос: Как поступить: пока не заморачиваться над этим и потом, при необходимости, перенести данные в не реляционную БД ИЛИ выбрать реляционную/не реляционную БД ?


Ответ

Подумал о том, что при большом объёме записей БД будет долго обрабатывать запрос
Планирование не может начинаться с "подумал", боттлнеки всегда бывают в иных местах. Реляционки спокойно работают с миллионами записей.
поэтому надо её раскинуть на несколько серверов. но вычитал, что реляционные БД плохо масштабируются
Тут у меня опять претензия в ту же степь. Вы не знаете, что именно вы вычитали. Они действительно плохо масштабируются из-за того, что (по крайней мере у большинства) есть только одна модель master-slaves, которая упирается в мастер по скорости записи (поправьте, если есть популярные движки с многочисленными мастерами). В любом случае в базе данных не должно быть тяжелых подсчетов - если вы считаете количество записей в БД, то это должно рассчитываться внутри БД и кэшироваться на уровне приложения, если вы рассчитываете аналитику, то тут БД уже ничего считать не должна.
В общем, у меня большие сомнения, что вам нужна масштабируемая система. Что до нереляционок, то их нельзя выбрать просто потому что "лучше масштабируются" - их только основных типов четыре штуки под свои задачи (вряд ли вы будете использовать key-value или графовую БД). Что до масштабирования, то есть row-column (т.е. хранение данных практически как в SQL) Cassandra, которая шардит данные по узлам и имеет практически линейную масштабируемость, т.е. добавление сервера в кластер из N серверов обеспечивает практически 100 * (1 / N + 1) процентов прироста производительности. Если есть знание кассандры, то делать проект на чем-то другом я не вижу смысла (единственная претензия - отсутствие готовых утилит для миграций, но это, надеюсь, изменится), классические реляционки как концепция по факту уже умерли - они, конечно, останутся, но в узком применении, и ближайшие N лет их популярность будет снижаться.

четверг, 28 марта 2019 г.

Как настроить правильно кодировку для MySQL?

Вот... Изучаю PHP. Дошла до соединения с БД. И тут такая проблема. В файле *.php прописываю соединение с БД. Затем прописываю запросы, вывожу результаты на экран. Все работает. Ошибок не выдает. НО! Проблема: русский текст не распознается. Выводит знаки ??? При этом английские буквы нормально выводит. Так понимаю, что проблемы с кодировкой. Кодировку меняла и в самом php-файле, и в БД. На windows-1251 и на utf-8 и utf8_general_ci. Видимо я пишу по старой версии PHP, и для PHP 5.х этот метод не подходит. Но я учусь по книжке, там так написано... В сети нашла решение моей проблемы. Но уже который час читаю, глаза уже красные, не могу понять, что делаю не так. Если писать на основе примеров, которые там написаны, то у меня сразу три ошибки выдает. Вообще, как это использовать правильно? Ничего не помогает. Что нужно сделать? Подскажите, пожалуйста. Код, который писала по книге такой: $result=mysql_query("SELECT * FROM firma",$db); $myrow=mysql_fetch_array($result);
echo $myrow["id_firma"]; echo " - ".$myrow["name"]; echo " ".$myrow["surname"]; echo " - ".$myrow["doljnost"]; ?> Только просьба ко всем большая. Давайте не будем тут разговаривать на тему, зачем девушке программирование. Уже общались по этому вопросу. Изучаю - значит надо. Спасибо за понимание:)


Ответ

// Подключение mysql_connect("localhost","user","pass"); mysql_select_db("db"); mysql_set_charset("utf8")
либо если используется mysqli
$mysqli = new mysqli("localhost", "user", "pass", "bd"); $mysqli->set_charset("utf8")
// Дальше работа с базой
При создании базы так же использовать кодировку utf8_general_ci, либо перевести в нее текущию. Так же ставте заголовок charset=UTF-8 и переводите кодировку самого файла (где пишите код и вообще все файлы) в кодировку UTF-8.
После понимания синтаксиса советую все делать в mysqli(нежели mysql) т.к. удобнее, есть поддержка, ну и ООП естественно, но это уже потом узнаете) Удачи.

суббота, 9 марта 2019 г.

Зачем закрывать подключения?

Здравствуйте. Разбираюсь с mysqli, в частности с этим примером: /* проверка подключения */ if (mysqli_connect_errno()) { printf("Не удалось подключиться: %s
", mysqli_connect_error()); exit(); }
$query = "SELECT Name, CountryCode FROM City ORDER by ID DESC LIMIT 50,5";
if ($result = mysqli_query($link, $query)) {
/* выборка данных и помещение их в массив */ while ($row = mysqli_fetch_row($result)) { printf ("%s (%s)
", $row[0], $row[1]); }
/* очищаем результирующий набор */ mysqli_free_result($result); }
/* закрываем подключение */ mysqli_close($link); ?> И напросился вопрос: зачем нужно очищать результат набора, mysqli_free_result($result);, закрывать подключения mysqli_close($link); и чем плохо, если этого не делать?


Ответ

Непонятно, откуда предыдущий оратор вообразил какой-то "таймаут".
Соединение с БД закрывать не надо - оно закроется само по окончании работы скрипта. Сразу. БЕЗ каких-либо "таймаутов".
Резалтсет в большинстве случаев очищать не нужно, поскольку как только закончит выполнение вызвавшая его функция, он так же обнулится.

вторник, 5 марта 2019 г.

Нестандартный вид вывода таблицы в php из MySql

Доброго времени суток, прошу помощи, ситуация такая: есть таблица в MySql:
задача: вывести ее на страницу в виде:
шапку таблицы создавал следующим образом:
connect_errno) { printf("Соединение не удалось: %s
", $mysqli->connect_error); exit(); } $sqltableclandate = "SELECT * FROM `nametable` GROUP BY Date"; $sqltableclanResultdate = $mysqli->query($sqltableclandate); while($tableclandate = $sqltableclanResultdate->fetch_assoc()) {?>
А вот дальше затык....
Подскажите куда копать и где искать)
Забыл вчера добавить: Добавление: если за какую-то дату нет информации, то в ячейке должен быть 0 или Н/Д
Добавление всего кода таблицы на данный момент:

Статистика:

query($query_dateclan)){ $rowclandate=$resultclandate->fetch_assoc(); $kol_rowsclandate = $resultclandate->num_rows; for($i=0; $i<$kol_rowsclandate; $i++){?>
Дата


вид базы: таблица: stat (как видно name5 появился позже)


Ответ

Можно воспользоваться функцией GROUP_CONCAT(), которая позволяет вывести списки каждой из групп, получаемых GROUP BY. Обычно в GROUP_CONCAT() передают имя столбца, но тут потребуется два значения - дата и gold. Их можно объединить при помощи CONCAT() каким-нибудь уникальным разделителем, например, решеткой.
SELECT GROUP_CONCAT(CONCAT(data, "#", gold)) AS data_gold FROM gold GROUP BY name +-------------------------------------------+ | data_gold | +-------------------------------------------+ | 2016-08-27#10,2016-08-28#15,2016-08-29#20 | | 2016-08-27#20,2016-08-28#25,2016-08-29#30 | | 2016-08-27#30,2016-08-28#35,2016-08-29#40 | | 2016-08-27#40,2016-08-28#45,2016-08-29#50 | +-------------------------------------------+
В PHP-коде можно сначала разбить строку по запятой (например, функцией explode()), а потом каждый элемент полученного массива еще раз разбить по #. GROUP_CONCAT допускает сортировку, а по полученной дате вы всегда сможете определить к какой ячейке должен относиться текущей элемент.
Для формирования результирующей таблицы можно воспользоваться следующим PHP-кодом:
PDO::ERRMODE_EXCEPTION));
function parse_gold($str) { $elements = explode(',', $str); $arr = array(); foreach($elements as $el) { list($date, $gold) = explode('#', $el); $arr[$date] = $gold; } return $arr; }
$query = "SELECT name, GROUP_CONCAT(CONCAT(data, '#', gold)) AS data_gold FROM gold GROUP BY name"; $usr = $pdo->query($query);
$users = array(); while($user = $usr->fetch()) { $users[$user['name']] = parse_gold($user['data_gold']); } $dates = array(); foreach($users as $user) { $dates += array_keys($user); } echo '

'; // Шапка echo ''; foreach($dates as $date) { echo ""; } echo ''; // Содержимое таблицы foreach($users as $name => $golds) { echo ''; echo ""; foreach($dates as $date) { if(array_key_exists($date, $golds)) { echo ""; } else { echo ""; } } echo ''; } echo '
Имя$date
$name{$golds[$date]}-
'; } catch (PDOException $e) { echo "Ошибка выполнения запроса: " . $e->getMessage(); }