За последние 24 часа нас посетили 15812 программистов и 1668 роботов. Сейчас ищут 797 программистов ...

Зачем нужен prepared statements API в PHP mysqli extension?

Тема в разделе "PHP и базы данных", создана пользователем Костян, 1 мар 2010.

  1. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    какие проблемы? ;) никаких проблем!
    да ладно, я могу много чего представить ))
    неужели предыдущий пост так навязывает такое мнение? Просто эти все разговоры переходят в такой флейм, фактов нету. Конечно то что ты высказал свое мнение за это тебе плюс. Аргументы эти вполне имеют место. Но вот я попросил большего и всё, начинаются какие-то разборки с непонятными обвинениями о представлениях, а фактов опять же нет. Такое ощущение что я тут на всех кидаюсь и готов горло грызть за свою статью. На самом деле я хочу узнать правду. Я не могу проверить всё под нагрузкой, которая запрашивается вашими требованиями. У меня нет просто возможности либо я не знаю как. Я не знаю где прочитать про этот таинственный кешь подготовленных запросов, потому что там где я мог прочитать я это сделал, другие места я не нашел.
     
  2. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Ну я и не говорил что это сложность или невозможно. Но кол-во кода, которое нужно написать, что бы динамически составляемый запрос через prepared statments правильно составлялся далеко превышает собой те 5-7 строк линейного кода. А вам ведь нужно же ещё контролировать что есть int, что есть строка что бы данные правильно составлялись. Полагаться на типы переменных вы не можете по той простой причине, что из большинства внешних источников всё приходит как string, т.е. разметка на инты, флоаты и строки делается руками в любом случае. Я просто не вижу смысла городить кучу кода, когда это решается конкатенацией в пару строчек. Чем проще, тем лучше. KISS помните такой? Это как раз тот случай, когда идёт противоречие этому принципу. Можно конечно сейчас заморочиться, сделать пример с и без prepared statments, долго спорить. Только вот вопрос: зачем? Мне кажется у вас уже достаточно опыта что бы понимать ту очевидность, о которой я вам говорю. В конце концов если уж совсем не понимаете, то попробуйте на практике что-ли. Я попробовал. Получается дольше, громоздко и местами совсем не удобно.

    Это не голословное утверждение, это факт из документации по MySQL.
     
  3. Simpliest

    Simpliest Активный пользователь

    С нами с:
    24 сен 2009
    Сообщения:
    4.511
    Симпатии:
    2
    Адрес:
    Донецк
    Они просто перпендикулярны сборке запроса. Сборка сама по себе, параметризированные запросы сами по себе.
    Именно поэтому данный факт - не аргумент.

    Костян
    Я не могу однозначно утверждать, что параметризированные запросы однозначно и всегда лучше. У меня просто нет этой информации, но я четко знаю - что все совсем не просто, и ты в своих тестах упустил многие важные моменты. Я более чем допускаю что в запросах вида getById никакой PS сильно не нужен и, возможно, во многом даже снижает производительность.
    Но вот у меня сейчас стоит на повестке дня вытащит данные из цепочки в 5ть таблиц. Да можно сделать 5ть разных запросов, а можно собрать и более сложную конструкцию и вот ни разу не очевидно - что будет быстрее в этом случае.

    Ты хочешь чего-то большего? Большего - это взять и провести тесты вместо тебя. Извини, но времени у меня на это пока нет.

    Да в честь чего?
    Для сборки нет никакой разницы между prepared statments и обязательным mysql_real_escape_string.
    Оверхедом будет биндинг массива параметров в размере целых 3 строк, включая закрывающую скобку цикла.

    Если оно прошло валидацию, то мне плевать в каком типе и под каким соусом оно пойдет в запрос.
    Дарю рабочий вариант
    [sql]SELECT * FROM offer WHERE offer_price > '1700.00';[/sql]
    1700.00 - это decimal если что.

    И я пробовал и пришел к тому, о чем сейчас говорю - Сборка сама по себе, параметризированные запросы сами по себе. - они никак не зависят друг от друга.
    А разница в том, что пробовали мы сильно разные вещи.
    Вы просто не работали с действительно сложной логикой. Все ваши запросы это максимум join 2-3 таблиц для получения дерева. И можно сколько угодно рвать на груди тельняшку с хайлоадом... Но когда у вас десяток параметров в запросе (да и их число может быть переменным), то конкатенация это далеко не KISS.

    Поэтому я тоже во многом раньше не видел смысла, но со временем начинаешь смотреть на все несколько иначе, ну или не начинаешь...

    P.S. флоппик все же не зря поднимал тему Учимся думать
     
  4. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    какую задачу вы решаете когда используете пс и динамическую сборку запроса?
     
  5. Simpliest

    Simpliest Активный пользователь

    С нами с:
    24 сен 2009
    Сообщения:
    4.511
    Симпатии:
    2
    Адрес:
    Донецк
    Что значит какую задачу решаю?

    Динамическая сборка запроса у меня служит для быстрого прототипирования, т.е. когда запросы еще не устоявшиеся и могут часто меняться.

    А до уровня PS я обычно не опускаюсь, это делается просто раз и навсегда в одном месте DBAL. Необходимости сверхоптимизации моих запросов у меня просто не возникало (если бы возникало, то у меня на руках уже были бы тесты с результатами с PS и без них в различных конфигурациях).
     
  6. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    а что плохо, когда допустим у меня запросы собираются динамически, лучше сделать перебор всех возможных вариантов?
    или как...
     
  7. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    не знаю чё вы завелись, я например и там и там использую плейсхолдеры... разница лишь в vsprintf и call_user_func_array, а то, что запрос динамически или не динамически собирается какая разница. В любом случае прейсхолдеры позволяют автоматом обработать параметры.
     
  8. Simpliest

    Simpliest Активный пользователь

    С нами с:
    24 сен 2009
    Сообщения:
    4.511
    Симпатии:
    2
    Адрес:
    Донецк
    Что значит плохо?
    Что значит перебор всех возможных вариантов?

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

    Как видишь, некоторым удобнее конкатенировать строки. Что впрочем вполне логично, для запроса с 1-2 параметрами. Но это не является истиной в последней инстанции, как тут пытаются донести.
     
  9. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Simpliest
    ну тогда не надно говорить что параметризация живёт отдельно от запроса =)
    а значит нужно как-то решать эту задачу в целом а не разбивать на контексты
     
  10. Simpliest

    Simpliest Активный пользователь

    С нами с:
    24 сен 2009
    Сообщения:
    4.511
    Симпатии:
    2
    Адрес:
    Донецк
    Называется слышал звон, да не знаю где он.

    Перечитай еще раз то, о чем я писал. Специально повторю

    Сборка сама по себе, параметризированные запросы сами по себе
     
  11. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    не зря же я рога тебе нарисовал =)
    умеешь ведь из очевидных вещей развести палемику...
     
  12. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Simpliest
    Вам говорят о запросах, которые динамически генерируются и для которых использование prepared statments получется усложнённым.
    Простой пример:

    SELECT * FROM table ORDER BY id DESC LIMIT 0, 20

    Если передали ID, то в запрос добавляется WHERE

    SELECT * FROM table WHERE id = 25 ORDER BY id DESC LIMIT 0, 20


    Как это будет выглядеть с prepared statments? С конкатенацией легко и просто.
     
  13. Тему может и поднимал не зря, зато результаты близкие к нулевым.
    Psih, прости, но твой пример выдуман. Это разные запросы, и скорее всего будут выводится в разных местах. Это во первых. А во вторых - запрос будет собиратся так же. только вместо непосредственно значения 25 там будет плейсхолдер.
    Именно об этом и говорил Сергей, когда говорил что процесс сборки запроса и подстановки происходят паралельно.
     
  14. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Psih
    ты внимательно почитай что он пишет
     
  15. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    ну так правильно. Сборка никак не связана с парамтризированными запросами. Можно собирать запрос с параметризированными запросами и без них. Обычная ситуация когда запрос имеет составляющие сборки: тип запроса, поля выборки, источник, соединения, условия, группировка, сортировака. Каждый кусок даёт свою часть строки запроса. В каждом куске могут быть переменные запроса, которые могут вставляться как через PS, так и обычной конкатенацией. Или вы конкатенацию производите в месте самой переменной? оО

    Дело не в этом, дело в том, что мы ищем до чего бы доебаться, а не правду ;)
     
  16. угу. Я уже жалею, что влез. Молчать обычно у меня получается лучше.
     
  17. Simpliest

    Simpliest Активный пользователь

    С нами с:
    24 сен 2009
    Сообщения:
    4.511
    Симпатии:
    2
    Адрес:
    Донецк
    Psih
    PHP:
    1. <?php
    2. $_POST = array(
    3.     'brand' => 'KU\KU',
    4.     'lamp' => "Don't"
    5.     );
    6. // ключ - id входных данных с формы
    7. // значение - условие
    8. $allowed = array(
    9.       'brand'         => 'brand = ?',
    10.       'lamp'         => 'lamp = ?',
    11.       'status'       => 'status = ?',
    12.       'supplier_id'   => 'supplier_id = ?',
    13.       'product_id'   => 'product_id NOT LIKE ?',
    14.       'location_id'   => 'location_id LIKE ?',
    15.       'company_id'   => 'company_id LIKE ?'
    16. );
    17. $sql = 'SELECT * FROM table1';
    18. // создаем массив условий только для тех значений которые у нас есть и во входных данных, и в массиве с критериями
    19. var_dump(_makeQuery($sql, $_POST, $allowed));
    20.  
    21. function _makeQuery($sql, $values, $allowed) {
    22.     $clause = array();
    23.     foreach ($values as $key => $val) {
    24.         if (array_key_exists($key, $allowed)) {
    25.             if (null != $val) {
    26.                 $clause[$allowed[$key]] = $val;
    27.             }
    28.         }
    29.     }
    30.  
    31.     if ($clause) {
    32.         $sql .= ' WHERE ';
    33.         // эскейпим и подставляем значения в плейсхолдеры
    34.         array_walk($clause,
    35.                 create_function('&$value, $condition',
    36.                 '$value = str_replace("?", "\'" . mysql_real_escape_string($value) . "\'", $condition);
    37.                    return true;'));
    38.         // итоговое условие
    39.         $sql .= implode(' AND ',     $clause);
    40.     }
    41.     return $sql;
    42. }
    43.  
    С PS это будет выглядеть немного иначе.

    PHP:
    1.  
    2. <?php
    3. $_POST = array(
    4.     'brand' => 'KU\KU',
    5.     'lamp' => "Don't"
    6.     );
    7. // ключ - id входных данных с формы
    8. // значение - условие
    9. $allowed = array(
    10.       'brand'         => 'brand = ?',
    11.       'lamp'         => 'lamp = ?',
    12.       'status'       => 'status = ?',
    13.       'supplier_id'   => 'supplier_id = ?',
    14.       'product_id'   => 'product_id NOT LIKE ?',
    15.       'location_id'   => 'location_id LIKE ?',
    16.       'company_id'   => 'company_id LIKE ?'
    17. );
    18. $sql = 'SELECT * FROM table1';
    19.  
    20. var_dump(_buildStmt($sql, $_POST, $allowed));
    21.  
    22. function _buildStmt($sql, $data, $allowed) {
    23.     $mysqli = new mysqli('host', 'user', 'password', 'database');
    24.     $valuesToBind = array('typedefstring' => null);
    25.     $clause = array();
    26.  
    27.     foreach ($data as $key => &$val) {
    28.         if (array_key_exists($key, $allowed)) {
    29.             if (null != $val) {
    30.                 $clause[]       = $allowed[$key];
    31.                 $valuesToBind['typedefstring'] .= 's';
    32.                 $valuesToBind[] = &$val;
    33.             }
    34.         }
    35.     }
    36.  
    37.     if ($clause) {
    38.         $sql .= ' WHERE ';
    39.         $sql .= implode(' AND ',     $clause);
    40.     }
    41.  
    42.     if(($stmt = $mysqli->prepare($sql))) {
    43.         if ($clause) {
    44.             call_user_func_array(array($stmt, 'bind_param'),$valuesToBind);
    45.         }
    46.     }
    47.     return $stmt;
    48. }
     
  18. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    PHP:
    1. <?
    2. $db->Query("SELECT * FROM tbl WHERE ?arr",array_intersect_key($_POST,$allowed));
    3. ?>
    ну или
    PHP:
    1. <?
    2. function sqlBind($arr,$del="AND"){
    3.      $res=array();
    4.       foreach($arr as $k=>$v){
    5.               $res[]="`".$k."`='".mysql_real_escape_string($v)."'";
    6.       }
    7.       return implode(" ".$del,$res);
    8. }
    9. $db->Query("SELECT * FROM tbl WHERE ".sqlBind(array_intersect_key($_POST,$allowed)));
    10. ?>
     
  19. Simpliest

    Simpliest Активный пользователь

    С нами с:
    24 сен 2009
    Сообщения:
    4.511
    Симпатии:
    2
    Адрес:
    Донецк
    Mr.M.I.T.
    На
    PHP:
    1. <?php
    2. $_POST = array('id' => '');
    3. $allowed = array('id' => 'id = ?');
    Исправляй ошибку у себя.
     
  20. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Simpliest
    это я к примеру. у меня для таких целей используется идея макросов Котерова
    хм, ты о какой ошибке?
     
  21. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    А вот и не выдумано. Это обычный фильтр, который либо есть, либо нету. Но запрос один :)
     
  22. Simpliest

    Simpliest Активный пользователь

    С нами с:
    24 сен 2009
    Сообщения:
    4.511
    Симпатии:
    2
    Адрес:
    Донецк
    PHP:
    1. <form method="post" action="">
    2.     <input type="text" name="id"/>
    3.     <input type="submit"/>
    4. </form>
    5. <?php
    6. var_dump($_POST);
    Какого рода выполнится запрос у тебя?
    А форма поиска(например) была ведь пустая.
     
  23. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    ой, ды ты издеваешься? =)