[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Немного о PDO
Arh
Хотелось бы поговорить про PDO (советы ,хитрые фишки, баги, недостатки, костыли)
Как всем известно mysql_query объявлен deprecated, непроизвольно задумываешься "как теперь подключать к базе данных и делать запросы?"
Покопавшись в сети можно наткнуться на несколько альтернатив, а именно:
1) PDO
2) Mysqlnd
3) Mysqli
Выбор пал на PDO из за поддержки нескольких баз данных. Не сильно важный момент, это скорее подстраховка сомнительного будущего, "а вдруг придётся запускать на разных бд?"

Где то даже пишут что все это работает быстрее чем mysql.
Сразу скажу, что mysql работает быстрее своих младших братьев, хоть разница заметна не сильно.

Для сравнения запускал запросы в цикле for <= 100 и подсчитывал microtime, скорость mysql_fetch_assoc составила примерно 0.039 скорость PDO fetch_assoc примерно 0.042, проверял много раз, цифры понятное дело всегда разные, но на 2 – 3 тысячные mysql выигрывает, что собственно не столь важно.

Как подключить PDO?
Создаём массив с настройками.
#Настройка базы данных
$DB = array (
'type' => 'mysql', //Тип базы данных
'host' => 'localhost', //Адрес базы
'user' => 'root', //Имя пользователя
'pass' => 'qwerty', //Пароль пользователя
'name' => 'dbname', //Имя базы
'coding' => 'utf8' //Кодировка базы
);


Почему массив? – Чтобы было удобнее сделать unset($DB), чтобы данные о подключении не болтались, да и смотрится симпатичней =)



#Подключаем PDO
try {$db = new PDO($DB['type'].":dbname=".$DB['name'].";host=".$DB['host'], $DB['user'], $DB['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES ".$DB['coding'],PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC));}
catch(PDOException $e) {
echo $e->getMessage(); exit;
}


Как сделать запрос?
$query = $db->query("SELECT `value` FROM table");
while($row = $query->fetch()){
echo $row[‘value’];
}


Или если нужно вернуть только 1 строку, делаем так
$row = $db->query("SELECT `value` FROM table")->fetch();
echo $row[‘value’];


Можно обойтись без while , с помощь fetchAll()
$query = $db->query("SELECT `value` FROM table")->fetchAll();


Это вернёт многомерный массив, который можно разобрать с помощью foreach
Это удобно только для проверки результата запроса
If($query['0']) значит чтото вернулось.
Всё бы ничего, но использовать данный метод не рекомендую изза его скорости.

Вот скорости теста:
$query = $db->query("SELECT * FROM table")->fetchall();

Без разбора через foreach и других манипуляций, просто запрос, средняя скорость составила 0.432
Теперь через while с обычным fetch
$query = $db->query("SELECT * FROM table");
while ($row = $query->fetch()) {
}

Средняя скорость 0.266 чувствуется разница.
"А если мне нужен именно многомерный массив, я не хочу его разбирать, мне просто нужен массив? =)"
$query = $db->query("SELECT * FROM table");
while ($row = $query->fetch()) {
$array[$row['id']] = $row;
}

Средняя скорость while 0.326 а у fetchAll() 0.432
Делайте выводы.

Задаём тип FETCH по умолчанию, это тоже не мало важно.
Что вы чаще всего возвращаете? Объект? Массив? Какой? Ассоционный? Я да, assoc это удобно и быстро.
По этому при подключении к PDO я задал fetch по умолчанию
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC


Почему это важно ?
Во первых чтобы постоянно не указывать тип
Например так
$query = $db->query("SELECT * FROM table");
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {}

или так
$query = $db->query("SELECT * FROM table");
$query->setFetchMode(PDO::FETCH_ASSOC);
while ($row = $query->fetch()) {}

ну или так
$query = $db->query("SELECT * FROM table") ->fetch(PDO::FETCH_ASSOC);

Во вторых по умолчанию стоит FETCH_BOTH, он отрабатывает очень долго, a FETCH_CLASS и FETCH_OBJ по моему еще тормознутей, самый быстрый FETCH_NUM.
В третьих каждый раз когда вы меняете тип fetch, вы тормозите скрипт.
Можно проверить дабы не голословить.
$query = $db->query("SELECT * FROM table");
while ($row = $query->fetch()) {
}

Средняя скорость 0.266

$query = $db->query("SELECT * FROM table");
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
}

Средняя скорость 0.273

$query = $db->query("SELECT * FROM table");
$query->setFetchMode(PDO::FETCH_ASSOC);
while ($row = $query->fetch()) {
}

Средняя скорость 0.269

Видно что постоянно задавать тип менее удобно и менее быстро.
Если уж приспичило сменить тип fetch, используйте
$query->setFetchMode(PDO::FETCH_ASSOC);

Ну либо на прямую
$row = $db->query("SELECT * FROM table")->fetch(PDO::FETCH_ASSOC);
если выводите всего 1 запись.

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

Можно конечно послать еще 1 запрос как рекомендуют в сети, но что если запрос очень сложный, у меня бывают запросы со сложными правилами в которых по 6 LEFT_JOIN и очень много OR AND, и нужно знать пришло ли мне что ни будь основываясь именно на этих правилах, поэтому послать маленький запросик типа
SELECT id FROM table WHERE и тут 1 , 2 условия 
не будет иметь смысл.

Собственно сам while по сути и есть проверка, ведь while проверяет $row на пустоту, и если $row == '' то дальше цикл не пойдёт
Тоесть
$query = $db->query("SELECT * FROM table");
$sql = false;
while ($row = $query->fetch()) {
$sql = true;
$array[$row['id']] = $row;

}
If($sql) {
echo 'Запрос успешно отработан, получите ваши данные';
Print_r($array);
} else {
echo 'Такой записи не существует =(';
}


Подведём итоги.
1) PDO по скорости уступает mysql, но не стоит обращать на это внимания.
2) Сразу задавайте тип fetch который вы используете чаще
3) Не пользуйте fetchAll а потом fоreach – это почти в два раза медленней чем while
4) Еще раз сравним скорость задавая тип fetch постоянно, при этом используя fetchAll и foreach
И скорость с заданным fetch и через while в первом случае 0,437 во втором 0,264




Собственно к чему я это? :)
Хочу узнать (советы ,хитрые фишки, баги, недостатки, костыли) из вашего опыта.



Спустя 6 минут, 5 секунд (19.09.2012 - 12:32) redreem написал(а):
из моего опыта: не использовать PDO.

Цитата
Выбор пал на PDO из за поддержки нескольких баз данных. Не сильно важный момент, это скорее подстраховка сомнительного будущего, "а вдруг придётся запускать на разных бд?"


так вы годами будете юзать эту махину в ожидании ситуации с разными базами.

Спустя 6 минут, 57 секунд (19.09.2012 - 12:39) Игорь_Vasinsky написал(а):
тема имеет право на существование

тут как раз всплыла моя про обучную mysql
http://phpforum.ru/index.php?showtopic=51151&hl=

так что, молодец.

Спустя 15 минут, 53 секунды (19.09.2012 - 12:55) m4a1fox написал(а):
Arh
Коли уж зацепили такую тему.... то.
Итак, долго пытался найти у вас в коде bindValue. Есть и такая штука. Вопрос заключается в следующем. Использовать ли ее или нет. Я откровенно говоря, слышал от некоторых форумчан, что bindValue нада-нада-нада. После попытался понять что сим есть, понял что она якобы обрабатывает запрос на всякого рода "инъекции". Попросил показать мне ее пользу. То есть реальный пример, вот с bindValue, а вот без оного...... но как то не получилось. Итак вопрос. А вы, уважаемый ТС, сталкивались в свое работе с bindValue? Ежели да - покажите нормальный пример его выполнение, и результаты, запроса с ним и без него?! Спасибо.

Спустя 4 минуты, 48 секунд (19.09.2012 - 12:59) Игорь_Vasinsky написал(а):
на скока мне понятно - то просто 3м параметром можно указать одну из предоставленных констант "типов" - дя облегчения валидации

http://php.net/manual/ru/pdostatement.bindvalue.php

Спустя 1 минута, 35 секунд (19.09.2012 - 13:01) Invis1ble написал(а):
Arh
Напиши, как тестировал производительность (код тестов в студию),
ибо у меня что-то сомнения закрадываются по-поводу "Средняя скорость while 0.326 а у fetchAll() 0.432", но возможно я не совсем правильно понял, что ты имел в виду.
А вообще, молодец, что написал статью smile.gif



Спустя 1 минута, 44 секунды Invis1ble написал(а):
m4a1fox
Подготовленные запросы (биндинг) имеет смысл юзать в больших запросах. По идее увеличивается производительность, хотя я не проверял.

Спустя 2 минуты, 8 секунд (19.09.2012 - 13:03) Игорь_Vasinsky написал(а):
да, тока название топика или заголовок более дружелюбное к SEO придумать нужно.

Спустя 3 минуты, 29 секунд (19.09.2012 - 13:07) Arh написал(а):
Цитата (Invis1ble @ 19.09.2012 - 10:01)
Arh
Напиши, как тестировал производительность (код тестов в студию),
ибо у меня что-то сомнения закрадываются по-поводу "Средняя скорость while 0.326 а у fetchAll() 0.432", но возможно я не совсем правильно понял, что ты имел в виду.
А вообще, молодец, что написал статью :)



Спустя 1 минута, 44 секунды Invis1ble написал(а):
m4a1fox
Подготовленные запросы (биндинг) имеет смысл юзать в больших запросах. По идее увеличивается производительность, хотя я не проверял.

$time = microtime(true);
for ($i=0;$i<=100;$i++) {

#код =)

}
echo (microtime(true) - $time);

Спустя 1 минута, 14 секунд (19.09.2012 - 13:08) Arh написал(а):
Цитата (Игорь_Vasinsky @ 19.09.2012 - 10:03)
да, тока название топика или заголовок более дружелюбное к SEO придумать нужно.

Это на усмотрение модераторов =)

Спустя 1 минута, 39 секунд (19.09.2012 - 13:09) Invis1ble написал(а):
Цитата (Arh @ 19.09.2012 - 13:07)
Цитата (Invis1ble @ 19.09.2012 - 10:01)
Arh
Напиши, как тестировал производительность (код тестов в студию),
ибо у меня что-то сомнения закрадываются по-поводу "Средняя скорость while 0.326 а у fetchAll() 0.432", но возможно я не совсем правильно понял, что ты имел в виду.
А вообще, молодец, что написал статью :)



Спустя 1 минута, 44 секунды Invis1ble написал(а):
m4a1fox
Подготовленные запросы (биндинг) имеет смысл юзать в больших запросах. По идее увеличивается производительность, хотя я не проверял.

$time = microtime(true);
for ($i=0;$i<=100;$i++) {

#код =)

}
echo (microtime(true) - $time);

может в "#код =)" есть упущения? покажи полностью код теста

Спустя 16 минут, 10 секунд (19.09.2012 - 13:26) johniek_comp написал(а):
Мне кажется ТС сам не очень понимает PDO, много не понятных слов, точнее "скользких" без конкретики. Одно
Цитата
assoc это удобно и быстро.
чего стоит.

ИМХО, pdo смысла использовать вообще нет, если не работаешь с разными бд

Спустя 6 минут, 26 секунд (19.09.2012 - 13:32) Invis1ble написал(а):
Цитата
Одно
Цитата
assoc это удобно и быстро.
чего стоит.

и чего же?

Спустя 8 минут, 21 секунда (19.09.2012 - 13:40) Игорь_Vasinsky написал(а):
а тут и не поднимался вопрос о том что стоит юзать, а что нет.
Многие используют или хотят использовать PDO, пускай учатся.

Спустя 4 минуты, 46 секунд (19.09.2012 - 13:45) Arh написал(а):
Цитата (m4a1fox @ 19.09.2012 - 09:55)
Arh
Коли уж зацепили такую тему.... то.
Итак, долго пытался найти у вас в коде dindValue. Есть и такая штука. Вопрос заключается в следующем. Использовать ли ее или нет. Я откровенно говоря, слышал от некоторых форумчан, что bindValue нада-нада-нада. После попытался понять что сим есть, понял что она якобы обрабатывает запрос на всякого рода "инъекции". Попросил показать мне ее пользу. То есть реальный пример, вот с bindValue, а вот без оного...... но как то не получилось. Итак вопрос. А вы, уважаемый ТС, сталкивались в свое работе с bindValue? Ежели да - покажите нормальный пример его выполнение, и результаты, запроса с ним и без него?! Спасибо.

По поводу dind не могу сказать, считаю это лишней, не удобной плюшкой.
Ведь задать шаблон можно прямо в execute
Что касается использования prepare и execute
Использовать стоит, это поможет защитить от sql инъекций, что то вроде mysql_real_escape_string() и тому подобного вместе взятым.

Как работает?

$_POST['name'] = 'Алексей';
$_POST['surname'] = 'Арх';
$query = $db->prepare("SELECT * FROM users WHERE `name` = :name OR `surname` = :surname");
$query->execute(array('name'=>$_POST['name'],'surname'=>$_POST['surname']));
while ($row = $query->fetch()) {
}


На сколько я могу предполагать, во первых execute обработает входные данные, и уберёт возможность sql инъекций, без построение всяких велосипедов. Во вторых prepere предварительно отправляет шаблон запроса на сервер, как бы проверка на соответствие, и только потом туда подставляются значения из execute как строку (обработанную).

Так же есть вариант использовать $db->quote(); которые обрамит кавычками, экранирует что нужно, собственно предотвратить инъекцию.

Касаемо скорости.
Обычный не обработанный запрос.
$query = $db->query("SELECT * FROM users WHERE `name` = '".$_POST['name']."' OR `surname` = '".$_POST['surname']."'");
while ($row = $query->fetch()) {
}

Средняя скорость 0.0277
Опасно так делать =)

Чуть медленней через $db->quote()
$query = $db->query("SELECT * FROM users WHERE `name` = ".$db->quote($_POST['name'])." OR `surname` = ".$db->quote($_POST['surname'])."");
while ($row = $query->fetch()) {
}

Средняя скорость 0.0285

Еще медленней execute
$query = $db->prepare("SELECT * FROM users WHERE `name` = :name OR `surname` = :surname");
$query->execute(array('name'=>$_POST['name'],'surname'=>$_POST['surname']));
while ($row = $query->fetch()) {
}

Средняя скорость 0.0300

В теории ни одна инъекцию не пройдёт, тесты лично не проводил.
Но я считаю всё равно необходимо обрабатывать всё что идёт в запрос по так называемому "белому списку"

Например мы знаем что у нас придёт число не больше 10 и другого здесь быть не может.
$var = (int)$_GET['id'];
if($var > '10') {
#фиксируем попытку взлома и шлём на ..
exit;
}

или делаем обычный запрос без quote и execute, мы теперь точно уверены что в переменной число и ничего больше.


Цитата
так вы годами будете юзать эту махину в ожидании ситуации с разными базами.

Ну я и говорю "сомнительное будущее" =)
Но скажем если выбирать между mysqli и PDO
Опять же PDO на мой взгляд выиграет из за "шаблонов execute"
В mysqli подготовленные запросы менее читабельны
то есть
Mysqli
"SELECT * FROM users WHERE `name` = ? OR `surname` = ?"


PDO
"SELECT * FROM users WHERE `name` = :name OR `surname` = :surname"


Запрос читается более понятно, а если он будет состоять из 10 строк, крыша поедет от знаком вопроса "?"

К тому же я предпочёл PDO по соображениям, что если он поддерживает много баз, то и больше разработчиков будет его использовать, на нём будет больше примеров в решении каких либо задач, может быть это очередная сомнительная подстраховка на будущее :)

Спустя 5 минут, 13 секунд (19.09.2012 - 13:50) Arh написал(а):
bbcode заглючили sad.gif

Спустя 1 минута, 7 секунд (19.09.2012 - 13:52) Arh написал(а):
Цитата (Invis1ble @ 19.09.2012 - 10:09)
Цитата (Arh @ 19.09.2012 - 13:07)
Цитата (Invis1ble @ 19.09.2012 - 10:01)
Arh
Напиши, как тестировал производительность (код тестов в студию),
ибо у меня что-то сомнения закрадываются по-поводу "Средняя скорость while 0.326 а у fetchAll() 0.432", но возможно я не совсем правильно понял, что ты имел в виду.
А вообще, молодец, что написал статью :)



Спустя 1 минута, 44 секунды Invis1ble написал(а):
m4a1fox
Подготовленные запросы (биндинг) имеет смысл юзать в больших запросах. По идее увеличивается производительность, хотя я не проверял.

$time = microtime(true);
for ($i=0;$i<=100;$i++) {

#код =)

}
echo (microtime(true) - $time);

может в "#код =)" есть упущения? покажи полностью код теста

Нет, упущений нет =)
"#код "
выложен в примерах в первом посте.

Спустя 3 часа, 36 минут, 35 секунд (19.09.2012 - 17:28) inpost написал(а):
Для меня подготовленные не удобно, потому что это не прозрачный запрос. Я привык видеть такой запрос, какой он есть на самом деле, чтобы секундой взгляда увидеть всё и понять ошибку.

Спустя 1 час, 38 минут, 31 секунда (19.09.2012 - 19:07) Arh написал(а):
Цитата (inpost @ 19.09.2012 - 14:28)
Для меня подготовленные не удобно, потому что это не прозрачный запрос. Я привык видеть такой запрос, какой он есть на самом деле, чтобы секундой взгляда увидеть всё и понять ошибку.

Дело привычки на самом деле.
Единственное это подсветка синтаксиса. :name на подсветится как переменная $name =)
Хотя ко всему привыкаешь.

Спустя 29 минут, 54 секунды (19.09.2012 - 19:37) Placido написал(а):
Я на время отладки включаю лог MySQL (SET GLOBAL `general_log` = 1) и в файле лога смотрю, какой запрос приходит в базу. После отладки отключаю лог (SET GLOBAL `general_log` = 0) и чищу файл.

Спустя 55 минут, 53 секунды (19.09.2012 - 20:32) killer8080 написал(а):
Цитата (Arh @ 19.09.2012 - 12:26)
Как всем известно mysql_query объявлен deprecated,

Опережаешь события , пока еще не объявлен, конечно все к тому идет, но идти еще может долго, не один год. Но в целом ты прав, сейчас уже нужно готовиться к переходу на альтернативные апи.
Цитата (Arh @ 19.09.2012 - 12:26)
Покопавшись в сети можно наткнуться на несколько альтернатив, а именно:
1) PDO
2) Mysqlnd
3) Mysqli
Выбор пал на PDO из за поддержки нескольких баз данных. Не сильно важный момент, это скорее подстраховка сомнительного будущего, "а вдруг придётся запускать на разных бд?"

Второй пункт выкинь, mysqlnd не является апи доступа к бд на уровне php кода, это драйвер, который используют в своей работе mysqli и pdo.
Пруфлинк
Цитата
Обзор

Чем он не является

Хотя встроенный драйвер MySQL написан как расширение PHP, важно понимать, что он не предоставляет программисту PHP нового API. API к базе данных MySQL для программиста предоставляются расширениями MySQL, mysqli и PDO MYSQL. Эти расширения могут использовать возможности встроенного драйвера MySQL для общения с сервером MySQL. Таким образом, вы не должны думать о встроенном драйвере MySQL как об API.

Насчет поддержки разных СУБД, тут важно понимать, что pdo не является абстракцией бд, он является абстракцией интерфейса доступа к бд. Это значит что рабочий софт нельзя переключить на другую СУБД, одной опцией в конфиге. У разных бд есть существенные различия в SQL синтаксисе, которые не позволят коду так просто мигрировать на другую субд. То есть тут единственная "польза" только в том, что программисту для освоения новой для него, бд не придется изучать новое апи, а воспользуется привычным функционалом, вот и все. Нужно ли это? Ну тут каждый для себя решает сам.
Цитата (Arh @ 19.09.2012 - 13:45)
Во вторых prepere предварительно отправляет шаблон запроса на сервер, как бы проверка на соответствие, и только потом туда подставляются значения из execute как строку (обработанную).

ошибочное мнение, prepare никуда запрос не отсылает, хотя бы по той простой причине, что MySQL не поддерживает именованных плейс холдеров, по этой причине их нет и в mysqli.

Спустя 1 час, 34 минуты, 57 секунд (19.09.2012 - 22:07) Placido написал(а):
Цитата (killer8080 @ 19.09.2012 - 20:32)
ошибочное мнение, prepare никуда запрос не отсылает, хотя бы по той простой причине, что MySQL не поддерживает именованных плейс холдеров, по этой причине их нет и в mysqli.

Если атрибут PDO::ATTR_EMULATE_PREPARES установлен в "false", то отсылает (для синтаксического разбора и составления плана запроса), сам как-то проверял. При чем, если в запросе используются именованные плейсхолдеры, они автоматически переводятся в нумерованные. А при вызове execute() отправляются сами данные, и MySQL выполняет запрос с подставленными данными. То есть всего два запроса.

Если же PDO::ATTR_EMULATE_PREPARES установлен в "true" (по умолчанию), то PDO эмулирует подготовленный запрос - подставляет значения, а потом отправляет, да, один запрос с подставленными значениями на сервер MySQL.


_____________
Промокод предоставляет скидку на заказ домена и/или хостинга reg.ru
BFCC-3895-8804-9ED2
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.