Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > сформировать запрос по значениям для трех полей


Автор: Denwer 3.12.2009, 01:10
Есть табличка с тремя полями (f1,f2,f3)

Есть 3 раскрывающих списка на сайте, которые содержат такие же значения как и в таблице.

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

$query = "SELECT * FROM ".TABLE_USERS." WHERE f1='$z1' AND f2='$z2' AND f2='$z3'";

если скажем я выберу значение только в первом списке то запрос таков
$query = "SELECT * FROM ".TABLE_USERS." WHERE f1='$z1''";

Вариантов выбора получается 9. Не делать же мне кучу проверок на то какие списки я выбрал и следовательно к ним запрос. А если будет 5 полей, то головоломка увеличиться на 5 в квадрате

Может кто сталкивался или знает как решить проблему, или знает того кто знает

ПОМОГИТЕ !!! 


Автор: Pitlord 3.12.2009, 01:14
http://forum.vingrad.ru/forum/topic-281107/anchor-entry2027781/0.html

Автор: Aumn 5.12.2009, 05:12
Цитата(Pitlord @  3.12.2009,  01:14 Найти цитируемый пост)
http://forum.vingrad.ru/forum/topic-281107...y2027781/0.html


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

Код

//обнуляем переменную, если она уже была созданна раннее
$q = "";
//проверяем все переменные на заполненность
for($i = 1;$i <= 3;$i++) {
if (!empty(${f.$i}) {
  if(!empty($q)) {
    $q .= " AND `{f.$i}`='${z.$i}'";
  }
  else {
    $q .= "`{f.$i}`='${z.$i}'";
}
//формируем запрос из условия - заполненно хотя бы 1 поле. В случае, если поля не заполнены, выводим все данные таблицы
if(!empty($q)) $query = "SELECT * FROM ".TABLE_USERS." WHERE ".$q; else $query = "SELECT * FROM ".TABLE_USERS;


Вместо тройки вставляете необходимое Вам значение кол-ва полей.

Автор: MaXL 5.12.2009, 06:37
Я так понял необходимые поля в БД текстовые.
А что если не выбранным полям присвоить значение %. И тогда просто сформировать запрос следующим образом:
Код

$query = "SELECT * FROM ".TABLE_USERS." WHERE f1='$z1' AND f2='$z2' AND f2='$z3'";

Автор: Aumn 5.12.2009, 07:04
К сожалению, я тут помочь Вам не могу, так как не знаю структуру БД и поля (тип, вводимая информация и т.д.) Я проверил свой код, он работает для любого числа полей и типа вводимой информации. Поэтому если вы дадите полные данные, возможно я свогу Вам помочь.

Автор: Ипатьев 5.12.2009, 09:48
MaXL, боюсь, такой запрос не вернет ничего.

Aumn, у вас в таблице поля всегда так называются - буква с цифрой? Если нет - то к чему изобретать такой заведомо нежизнеспособный код?
Разрешите дать вам один совет. Как человеку, который решил отвечать на вопросы оптом, даже не глядя на дату последнего сообщения. 
Пишите код только в том случае, если вы уже использовали его раньше, в сходной ситации. А не придумывая его на ходу.

Автор: Simpliest 5.12.2009, 12:06
Вариант номер раз.

Код

    $_POST = array('brand' => 'KU\KU',
                   'lamp' => "Don't");
    // ключ - id входных данных с формы
    // значение - условие
    $allowed =  array('brand'         => 'brand = :?',
                          'lamp'      => 'lamp = :?',
                          'status'        => 'status = :?',
                          'supplier_id'   => 'supplier_id = :?',
                          'product_id'    => 'product_id NOT LIKE :?',
                          'location_id'   => 'location_id LIKE :?',
                          'company_id'    => 'company_id LIKE :?'
    );
    // создаем массив условий только для тех значений которые у нас есть и во входных данных, и в массиве с критериями
    $clause = _makeClause($_POST, $allowed);
    // эскейпим и подставляем значения в плейсхолдеры
    array_walk(&$clause,
            create_function('&$value, $condition',
            '$value = str_replace(":?", "\'" . mysql_real_escape_string($value) . "\'", $condition);
            return true;'));
    // итоговое условие
    $clause = implode(' ',     $clause);
    echo $clause;

    protected function _makeClause($values, $allowed)
    {
        $clause = array();
        foreach ($values as $key => $val) {
            if (array_key_exists($key, $allowed)) {
                if (null != $val) {
                    $clause[$allowed[$key]] = $val;
                }
            }
        }
        return $clause;
    }


Извращение номер два'с
Код

// эталон условия 
echo "c = 1 and (d > 2.78e02 or c in (1,2,3) and not (t ='2.78d02'  or (c = 4))) or (d = 1 and (c = 2 or d = 2)) or (c = Mech's are not\listed) and (d = Mech's\twork)";
// оно же в виде массива
$c = array(
    'c =:?' => 1,
    'and' => array(
        'd >:? or ' => '2.78e02',
        'c in' => array(1,2,3),
        'and not' => array(
            't =:?' => '2.78d02',
            'or' => array('c =:?' => 4)
        )
        ),
        array (
            'or' => array (
                'd =:? ' => 1,
                'and' => array(
                    'c =:? or' => 2,
                    array('d =:?' => 2)
                )
            )
        ),
        array(
            'or' => array('c =:?' =>  "Mech's are not\listed")
        ),
     array('and' => array('d =:?' => "Mech's\twork"))
);
echo '<hr>';
echo whereBuilder($c);
die();

function whereBuilder(array $condition) {
    $where = '';
    foreach ($condition as $criteria => $value) {
        if (substr(strtolower($criteria), -3) == ' in') {
            $where .= ' ' . $criteria . " (" . implode(",", array_map(create_function('$a','return is_numeric($a) ? $a : "\'" . $a . "\'";'), array_map('mysql_real_escape_string',(array) $value))) . ")";
        } elseif (is_array($value)) {
            // если вместо значения массив то обрабатываем вложенные условия
            // если критерий/условие целое число, отбрасываем его. Сделано для избежания коллизий ключей для множественных равнозначных условий
            is_int($criteria) ?
            $where .= ' ' . whereBuilder($value) . '' :
            $where .= ' ' . $criteria . ' (' . whereBuilder($value) . ')' ;
        } elseif(false !== strpos($criteria, ':?')) {
            // эскейпим и подставляем значение в условие
            $where .= ' ' . str_replace(':?', is_numeric($value = mysql_real_escape_string($value)) ? $value : '\'' . $value . '\'', $criteria) . ' ';
        } else {
            // хардкод критерии випа D = "1" всегда. $value - необязателен
            $where .= ' ' . $criteria . (is_numeric($value) ? $value:'\'' . ($value) . '\'') ;
        }
    }
    return $where;
}


А вообще это все глупости smile нужно использовать биндинг и не заниматься подстановкой в запросы.

Автор: Ипатьев 5.12.2009, 12:13
в итоге код получился немного больше, чем в варианте Pitlord-а. и в разы сложнее. затрудняя отладку и чтение
а можно посмотреть пример, как биндинг решает данную проблему?

Автор: Simpliest 5.12.2009, 13:16
Цитата(Ипатьев @  5.12.2009,  11:13 Найти цитируемый пост)
а можно посмотреть пример, как биндинг решает данную проблему?

Отладку и чтение? smile

Код

protected function _buildStmt($sql, $data, $allowedCriteria) {
    $valuesToBind = array();

    $sql .= '1=1 AND ';

    foreach ($data as $key => $val) {
        if (array_key_exists($key, $allowedCriteria)) {
            if (null != $val) {
                $sql.= ' ' . $allowedCriteria[$key];
                $valuesToBind[] = $val;
            }
        }
    }
    $sql = substr($sql, -5);

    if($stmt = $this->prepare($sql)) {
        foreach($valuesToBind as &$param) {
            $stmt->bind_param('s', $param);
        }
    }
    return $stmt;
}


Добавлено через 13 минут и 11 секунд
Цитата(Ипатьев @  5.12.2009,  11:13 Найти цитируемый пост)
в итоге код получился немного больше, чем в варианте Pitlord-а

Если имеется в виду Извращение номер два'с
то это именно извращение, которое никогда никому не потребуется и не может быть применено. Хотя и рабочее smile

Первый же вариант за исключением части с array_walk & mysql_real_escape_string я использовал раньше очень часто.

Он легко справляется и с десятками полей smile Читаться будет так же как и сейчас.
В варианте Pitlord-а придется по-копипастить. Да и не люблю я тридацать раз писать одни и те же if smile
мне массив приятнее.


Автор: Ипатьев 5.12.2009, 14:20
Цитата(Simpliest @  5.12.2009,  13:16 Найти цитируемый пост)
Отладку и чтение?

Нет. проблему, в связи с короторй биндинг был заявлен: не заниматься подстановкой в зпросы.
В это связи я не очень понимаю, чем является выражение  $sql.= ' ' . $allowedCriteria[$key]
$sql - это не запрос?

я всегда считал, что имена полей, условия, и - тем более - функции mysql в подстановках исползовать нельзя.

Цитата(Simpliest @  5.12.2009,  13:16 Найти цитируемый пост)
В варианте Pitlord-а придется по-копипастить. Да и не люблю я тридацать раз писать одни и те же if 

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

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

Автор: Simpliest 5.12.2009, 15:21
Цитата(Ипатьев @  5.12.2009,  13:20 Найти цитируемый пост)
не заниматься подстановкой в зпросы.

Дык.
Сравнить эту лажу
Цитата(Simpliest @  5.12.2009,  11:06 Найти цитируемый пост)
 str_replace(':?', is_numeric($value = mysql_real_escape_string($value)) ? $value : '\'' . $value . '\'', $criteria)

и биндинг
Цитата(Simpliest @  5.12.2009,  12:16 Найти цитируемый пост)
$stmt->bind_param('s', $param);

И все станет очевидно.

Цитата(Ипатьев @  5.12.2009,  13:20 Найти цитируемый пост)
В это связи я не очень понимаю, чем является выражение  $sql.= ' ' . $allowedCriteria[$key]

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

Цитата(Ипатьев @  5.12.2009,  13:20 Найти цитируемый пост)
проблема в том, что в реальной жизни этот иф чаще всего совсем не один и тот же

? в честь чего? Валидация и фильтрация лежат несколько в отдельном месте. А проверка на наличие/разрешенность поля... будет одинаковой.

Цитата(Ипатьев @  5.12.2009,  13:20 Найти цитируемый пост)
и копипастить придется совсем немного. 
при этом код будет понятным и читаемым. 

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

Цитата(Ипатьев @  5.12.2009,  13:20 Найти цитируемый пост)
стремление к чрезмерной абстракции столь же губительно, как и преждевременная оптимизация. 

Я так понимаю пример конкретной системы, конкретной чрезмерной абстракции и конкретной ее реализации, привести не затруднит?

Автор: Ипатьев 5.12.2009, 15:37
Мне кажется, что вы говорите о чем угодно, но только не о конкретной проблеме составления запроса по множесту критериев.
Какая разница, как мы подставляем в запрос само значение, если в данном случае мы подставляем не значение, а целиком условие?
как bind_param поможет составить выражение вида
date(start) > '2009-11-11'
где 2009-11-11 берется из формы?
зачем городить сложную систему, распознающую миллиард различных комбинаций условий, если эти условия можно просто написать вручную?

Цитата(Simpliest @  5.12.2009,  15:21 Найти цитируемый пост)
Я так понимаю пример конкретной системы, конкретной чрезмерной абстракции

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

Автор: Simpliest 5.12.2009, 16:14
Цитата(Ипатьев @  5.12.2009,  14:37 Найти цитируемый пост)
Мне кажется, что вы говорите о чем угодно, но только не о конкретной проблеме составления запроса по множесту критериев.
Какая разница, как мы подставляем в запрос само значение, если в данном случае мы подставляем не значение, а целиком условие?

Брр. Не путайте теплое с мягким.

Построение запроса и подстановку/биндинг параметров (последний вообще возможен только к готовому запросу, а не куску условия)

Опыт работы с ОРМ или хотя бы с DBAL проектов отличных от сайтов-визиток хоть какой-нибудь вообще имеется? Чтобы я просто понимал ваш уровень в этом вопросе.

Цитата(Ипатьев @  5.12.2009,  14:37 Найти цитируемый пост)
зачем городить сложную систему, распознающую миллиард различных комбинаций условий, если эти условия можно просто написать вручную?

Не воюйте с ветряными мельницами - это неперспективно. Вы выдумали себе мельницу и доблестно пытаетесь ее разбить.

Вот это еще не ###код, но крайне близкое к нему решение.
Цитата(Pitlord @  22.11.2009,  21:02 Найти цитируемый пост)
Код

if( $name != '' )
    $fields[] = '`name` = "' . mysql_real_escape_string($name) . '"';
...



Вот это откровенный ###код.
Цитата(Simpliest @  5.12.2009,  11:06 Найти цитируемый пост)
Код

echo whereBuilder($c);
die();

function whereBuilder(array $condition) {
...


Связывать построение запроса и подстановку/биндинг данных в линейном коде - не самое лучшее решение для проекта (для разового скрипта подойдет и лапша).
Чрезмерно усложнить решение простой задачи - тоже глупость.

Цитата(Ипатьев @  5.12.2009,  14:37 Найти цитируемый пост)
Во-первых, у меня его, по очевидным причинам, нету

Вы не пишите вовсе? Или пишите исключительно "лапшу"? Это печально.
Смею уверить, если бы вы хоть что-то сложное писали, то в конкретном коде у вас можно найти с полдесятка ошибок.
И возможно даже тех, о которых вы знаете, но не исправляете smile

Автор: Pitlord 5.12.2009, 18:06
Прямо-таки война концепций. Такую серьёзную проблему надо решать в разделе "PHP: Для профи", точно вам говорю.

Автор: Ипатьев 5.12.2009, 18:35
Цитата(Simpliest @  5.12.2009,  12:06 Найти цитируемый пост)
А вообще это все глупости smile нужно использовать биндинг и не заниматься подстановкой в запросы. 

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

вернемся к вопросу.
Учитывая, что код в самом первом варианте практически не отличается от объявления массива, но при этом не требует дополнительной обработки, и является более гибким, стоит признать, что и "лапша" вполне имеет право на жизнь. 


Автор: Aumn 5.12.2009, 19:12
Цитата(Ипатьев @  5.12.2009,  09:48 Найти цитируемый пост)
Aumn, у вас в таблице поля всегда так называются - буква с цифрой? Если нет - то к чему изобретать такой заведомо нежизнеспособный код?Разрешите дать вам один совет. Как человеку, который решил отвечать на вопросы оптом, даже не глядя на дату последнего сообщения. Пишите код только в том случае, если вы уже использовали его раньше, в сходной ситации. А не придумывая его на ходу.


Ипатьев, тут я могу Вам только ответить, что никогда не делаю таких полей. Если Вы обратите внимание на первый пост, то заметите, что в запросе поля именно так и сформулированны. Тот код, что я написал, я никогда не использовал (кроме тестирования перед выкладкой сюда) и не собираюсь, так как с моей точки зрения поля в таблице должны иметь более менее читабельный вид. На счет "вопросы оптом". Это что - нарушение правил? Я просто помогаю людям и ничего от этого не требую. Я не ставлю никаких рекламных ссылок и прочей ерунды. Какое из моих решений навредило?

P.S. Для всех, чтоб не возникало больше вопросов - данный код подходит только для данного случая и упрощает процесс реализации (как и объем кода)

Автор: Ипатьев 5.12.2009, 19:38
Задавая вопрос, автор часто упрощает его, думая, что так будет полезнее.

Автор: Aumn 5.12.2009, 19:41
Цитата(Ипатьев @  5.12.2009,  19:38 Найти цитируемый пост)
Задавая вопрос, автор часто упрощает его, думая, что так будет полезнее.


Благодарю. Приму к сведению. Я тут не так-то и давно и не знал, что на этом форуме такое практикуется.

Автор: NLspieler 5.12.2009, 20:17
Не знаю, почему такую элементарную задачу, обсуждают уже вторую страницу.
Ну раз уж пошла такая тема, то предложу своё решение.

Код

$felder = array ("feld1" , "feld2") ;  //Названия $_POST полей, которые используются для поиска и могут быть заполнены или нет
$where = '' ; //Приставка, к запросу с условиями

//Тут можно добавить дополнительные проверки и преобразования пост полей

foreach ($felder as $feld)
{
    if (isset($_POST[$feld]) and $_POST[$feld] != '')
    {
         $where .= $feld . ' = ' . $_POST[$feld] .  'AND ' ;
    }
}
$where .= '1=1' ; 
$sql = "SELECT * FROM TABLE_NAME WHERE $where" ;



Автор: Simpliest 5.12.2009, 21:22
Цитата(Ипатьев @  5.12.2009,  17:35 Найти цитируемый пост)
при этом не требует дополнительной обработки, и является более гибким

Эм? smile а в чем "более" гибкость? smile 
В том что после появления 4го поля он уже будет несколько неудобным, после 8го мы начнем подозревать что, что-то пошло не так.
К 20му запросу написанному "таким" спосбом, мы проклянем того кто нам его посоветовал, сотрем все к черту и перепишем заново? smile
Если для вас гибкость заключается в этом  - то мне нечего возразить smile


Цитата(Ипатьев @  5.12.2009,  17:35 Найти цитируемый пост)
и "лапша" вполне имеет право на жизнь. 

В маленьких скриптах - вполне.
Она проще и быстрее пишется, над ней нужно меньше думать.

как только скрипт начинает расти и меняться - лапшу начинаешь тихо ненавидеть.

Добавлено через 2 минуты и 39 секунд
Цитата(Aumn @  5.12.2009,  18:12 Найти цитируемый пост)
 Это что - нарушение правил?

Поднятие тем более чем 2-3 месячной давности называется некрофилией smile
А так - ради бога smile Кто-то любит мальчиков, кто-то девочек, а кто-то слегка расползающихся под пальцами smile Дело-то житейское smile

Автор: Denwer 6.12.2009, 11:23
понятное дело что поля я так не называл, и типы данных там могут быть любые, написал для краткости и понятности вопроса. Все же спасибо всем кто откликнулся, были действительно достойные ответы, от ребят поопытнее. Вот так и рождается истина в спорах и в разногласиях. Всем хорошего дня!!!

Автор: Aumn 7.12.2009, 04:10
Цитата(Simpliest @  5.12.2009,  21:22 Найти цитируемый пост)
Поднятие тем более чем 2-3 месячной давности называется некрофилией А так - ради бога  Кто-то любит мальчиков, кто-то девочек, а кто-то слегка расползающихся под пальцами  Дело-то житейское 


Что-то я не понял. Какое это отношение имеет к моим словам?

Автор: Simpliest 7.12.2009, 06:57
Цитата(Aumn @  7.12.2009,  03:10 Найти цитируемый пост)
Что-то я не понял. Какое это отношение имеет к моим словам? 

Это имеет отношение к твоим действиям. Когда ты отвечал на старые/мертвые темы.

Автор: Aumn 7.12.2009, 14:59
Цитата(Simpliest @  7.12.2009,  06:57 Найти цитируемый пост)
Это имеет отношение к твоим действиям. Когда ты отвечал на старые/мертвые темы.


Они полюбому попали/попадут в архив + в индекс поисковика. Потом по какому-либо запросу, возможно, выведится страница форуму, где я отвечал и, также скажу - возможно, кому-то и пригодится мой ответ. Поэтому даже постинг в старых/мертвых темах иногда полезен.  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)