SQL-запросы и разбор параметров.
Хочу сделать обертку для SQL-запросов.
И возник вопрос, как играться с логикой (AND, OR), и как лучше составлять запросы.
Нужен ваш совет и, по возможности, примеры.
Есть ряд параметров, для который составляется исключительно логика AND, т.е срогое совпадение по всем параметрам, а есть часть параметров, для которых логика задана OR, т.е. любое из вхождений.
Например, у нас ювелирный магазин, и мы хотим отобрать изделия стоимостью от 2000 до 5000, с камнем, в разделах кольца, подвески и серьги.
из формы передаем
$price_to=5000;
$diamond=1;//true
$category=array('Кольца','Подвески','Серьги');
foreach($category as $key=>$val) {
$category[$key]="category='".$val."'";
}
$sql_cat="AND (".implode(' OR ', $category).")";
$sql="select * from items where (price>='$price_from' AND price<='$price_to' AND diamond='$diamond'".$sql_cat.")";
и запрос получается:
Вопрос:
как более грамотно и точно:
1.Собрать переменные для собирания "where"
2.Если в одном поле моет быть несколько совпадения, чтобы у нас получилось нужное количество логических операторов, т.е. чтобы не вышло "$cat=a1 or $cat=a2 or"... т.е. чтобы их небыло лишних в начале или конце... Я пока остановился на implode... но мож что другое посоветуете?
Буду благодарен за примеры и за ссылки на краткие, но очень доходчивые примеры и статьи.
$price_to=5000;
$diamond=1;//true
$category=array('Кольца','Подвески','Серьги');
$sql="select * from items where (price>='$price_from' AND price<='$price_to' AND diamond='$diamond' AND category in (".implode(',',$category)."))";
Будет так
Вот только твой код выдает:
select * from items where (price>='2000' AND price<='5000' AND diamond='1' AND category in (Кольца,Подвески,Серьги))
т.е. значения категорий без одинарных кавычек...
Или обрабатывать массив foreach'ем, добавляя одинарные кавычки, или... ?
При полной нормализации категории должны браться из справочника категорий и быть числовыми кодами.
Почти без скобок и кавычек.
Почти без скобок и кавычек.
Ну собственно, да, они и будут числовыми значениями...
Мускуль нормально воспример два AND без кавычек, не будет капать дальше???
?
Как лучше разбирать форму поиска по базе?
например, есть критерии price_from,price_to, category, diamond. (сразу говорю, не пишу интернет-магазин, персонажи вымышлены)
но передаются не все из них.
как бы более гибко описать чтение $_POST, чтобы он находя непустые поля, добавлял их к запросу автоматически?
Моя соображалка пока не готова родить функцию для этого.
Например переданы только price_to и diamond.
И чтобы скрипт сжирал их из ПОСТа и добавлял к запросу.
Вопрос у УАСу:
ты писал про то что переопределение переменных - зло.
как-то без "foreach" можно пройтись по ПОСТу и обработать каждое значение тримами и эскейп_стрингами?
Или единсвенный вариант - писать функцию валидности значения для каждого переданного параметра и перезаписи ПОСТа з функции?
В MSSQL динамическое построение запроса выглядит следующим образом (и я думаю такой же подход реализуем в MySql):
Формируем строку вида
FROM dbo.MyTable
WHERE 1 != 0
AND x < @x
AND y > @y
AND z = @z
ORDER BY a,b,c,d
AND условия просто цепляются к строке при наличии тех или иных параметров.
Выполнение запроса происходит c передачей параметров, которые есть в запросе (синтаксис T-SQL):
Такой же подход можно реализовать на клиенте. И что самое главное - никаких фактических значений мы не пихаем в строку - только идентификаторы.
Полный пример:
SET @query = N'
SELECT a,b,c,d
FROM dbo.MyTable
WHERE 1 != 0';
IF(@x IS NOT NULL)
SET @query = @query + N' AND x < @x';
IF(@y IS NOT NULL)
SET @query = @query + N' AND y > @y';
IF(@z IS NOT NULL)
SET @query = @query + N' AND z = @z';
SET @query = @query + N' ORDER BY a,b,c,d';
EXEC(@query, @x = @x, @y = @y, @x = @z);
В MSSQL динамическое построение запроса выглядит следующим образом (и я думаю такой же подход реализуем в MySql):
Тримы и эскейпы для того, чтобы нам в запрос не передали какую-то лабуду + пробелы с числом + SQL-инъекции и т.д.
В принципе, я рассматривал вопрос "WHERE 1" и потом в цикле перед парой "ключ-значение" ставить AND... Насколько это корректно и грамотно?
В принципе, я рассматривал вопрос "WHERE 1" и потом в цикле перед парой "ключ-значение" ставить AND... Насколько это корректно и грамотно?
Я ведь привел пример. Передавать значение нужно через параметр (имя "переменной") sql-запроса, а в запросе только имя переменной указывать.
Пока меня интересует оптимальный алгоритм разбора переданных данных и формирования однострочного SQL-запроса в проекции MySQL...
Формировать можно и перебеором, и без него. зависит, сколько лишних дынных в post-е
Формировать можно и перебеором, и без него. зависит, сколько лишних дынных в post-е
Нет... В любом случае нужно предусматривать логику AND\OR... при чем я ХЗ как написать умную функцию, которая была бы способна работать с хитрыми конструкциями типа
(col=val AND col2=val2) OR (col=val2 AND col2=val)
Ну и ясное дело, с автоматическим разбором параметров...
В целов, в голове у меня уже созрела функция...
Сегодня вечером начну, и если есть потребность у читателей топика, выложу вариант решения моих потребностей...
ты писал про то что переопределение переменных - зло.
как-то без "foreach" можно пройтись по ПОСТу и обработать каждое значение тримами и эскейп_стрингами?
Или единсвенный вариант - писать функцию валидности значения для каждого переданного параметра и перезаписи ПОСТа з функции?
Ну вроде как есть функция array_map, т.е. делаешь $_POST = array_map("trim", $_POST); и получаешь от-trim-енный массив.
Посчет эскейпов - я делаю ескейпы уже на этапе вставки в БД, если это статический запрос (т.е. без множества слйчайных AND, OR ...). А в твоем случае - ну я обычно просто пишу WHERE 1 и далее уже типа:
if( $_POST["ololo"] == "1" || $_POST["ololo"] == "10" )
$sql .= " AND `ololo` = ".$_POST["olollo"];
Посчёт того, использовать isset($_POST[...]) или нет, то я обычно устанавливаю всем переменным значения по-умолчанию. И если $_POST["ololo"] не существует, то создаю такой элемент и присваиваю ему, допустим, пустую строку.
По поводу поста хардкейса - нет, в mysql нет такой возможности, максимум добиться "параметризированного" запроса можно только с помощью sprintf, vsprintf ну или написать свою функцию с параметрезированным вводом (что, обычно, лень =))
Ну а по поводу умной функции для AND OR - тут сложнее, всё-таки, ибо надо углядеть кучу параметров и т.д. Проще сделать на if, или же создавать какую-то структуру из массивов/классов, передавать их в функцию, которая и будет строить условия, т.е. типа:
class Conclusion {
public String $key; // поле, например, price
public Object $value; // значение, например 10, или объект класса AND или OR
}
class AND {
// массив значений для Conclusion, которые будут объединятся по AND, могут тоже быть AND, OR, Conclusion
public Object[] $values = array();
}
class OR {
public Object[] $values = array(); // аналогично AND
}
Вот в этом псевдо-php-java коде видно, как примерно строить условия. Т.е. строится такая иерархия, которая передается в функция, а та уже строит строку. Но, имхо, это такой маразм и тормознутость получится, что легче все на if сваять - проще, быстрее, очевиднее, надежнее.
Мы не ищем лёгких путей :))))
2 UAS
за array_map - спасибо... Можно любую функцию (включая пользовательскую) подвесить на это дело.
со сложным разбором AND\OR я тоже заморачиваться пока не стану... как и с объектами...
У меня БД действительно не сложная, и я ограничусь лишь функциями по предварительному разбору переданных данных и фомированием SQL-запросов для поиска по базе.. остальное, вероятно, будет на ИФах....