Задний план
Привет, я разрабатываю экспериментальный / образовательный инструмент в PHP и MySQL. Я новичок в SQL, но я хочу делать все правильно с самого начала. Я использую подготовленные заявления PDO для всех замещений переменных и везде, где это возможно, (поэтому, насколько я понимаю, он не будет переносимым для баз данных, отличных от MySQL). Что касается моей проблемы, у меня есть идея, как идти дальше, но мне потребуется несколько часов для реализации (я новичок даже в синтаксисе SQL), поэтому я подумал, что сначала создаю вопрос в случае, если кто-то может кричать: «Это не способ сделать это!» и сэкономить мне силы.
проблема
Я хотел бы создать интерфейс, в котором пользователь будет выбирать из выпадающих меню:
A
, Ax
и Ay
, B
, Bz
и By
, и после представления код будет выполнять внутреннее соединение, соответствующее каждому полю соответственно, например Ax = Bz
, Ay = By
и т. д., и возвращать все согласованные строки.
Мой план состоит в том, чтобы генерировать оператор INNER JOIN
SQL, перебирать поля и вставлять заполнители ( ?
), Связывать соответствующие параметры и, наконец, выполнять инструкцию.
Есть ли более простой способ сделать это? Есть ли лучший способ сделать это? Будет ли это каким-то образом использовано?
Заранее большое спасибо. Если никто не ответит к тому времени, когда я закончу (сомнительно), я опубликую свое решение.
Разный
Предположим, что я подтвержу
A
и B
, и что имена полей не обязательно должны быть одинаковыми: они будут соответствовать по порядку. (Отметьте любые другие детали, которые я, возможно, не знаю!)
В конце концов, цель состоит в том, чтобы эти выборы были сохранены в таблице настроек. По сути, пользователи создают «представления», которые они хотели бы видеть каждый раз, когда они возвращаются.
Вы делаете так правильно, что я действительно чувствую себя виноватым, указывая, что вы делаете что-то неправильно! 🙂
Вы можете использовать только подготовленные операторы для параметризации значений полей, а не для идентификаторов SQL, таких как имена столбцов или таблиц. Поэтому вы не сможете передавать Ax
, Bz
и т. Д. В свои критерии JOIN
с помощью подготовленных параметров оператора: вместо этого вы должны делать то, что кажется ужасно неправильным, и напрямую связать их с вашей строкой SQL.
Однако все не потеряно. В каком-то неопределенном порядке предпочтения вы можете:
Представьте пользователю список опций, из которого вы впоследствии собрали SQL:
<select name="join_a"> <option value="1">x</option> <option value="2">y</option> </select> <select name="join_b"> <option value="1">z</option> <option value="2">y</option> </select>
Затем ваш обработчик формы:
switch ($_POST['join_a']) { case 1: $acol = 'x'; break; case 2: $acol = 'y'; break; default: die('Invalid input'); } switch ($_POST['join_b']) { case 1: $bcol = 'z'; break; case 2: $bcol = 'y'; break; default: die('Invalid input'); } $sql .= "FROM A JOIN B ON A.$acol = B.$bcol";
Преимущество такого подхода заключается в том, что, не считая компрометации PHP (в этом случае у вас будет гораздо больше проблем, чем SQL-инъекция), произвольный SQL абсолютно не может найти свой путь в вашей РСУБД.
Убедитесь, что пользовательский ввод соответствует одному из ожидаемых значений:
<select name="join_a"> <option>x</option> <option>y</option> </select> <select name="join_b"> <option>z</option> <option>y</option> </select>
Затем ваш обработчик формы:
if (!in_array($_POST['join_a'], ['x', 'y']) or !in_array($_POST['join_b'], ['z', 'y'])) die('Invalid input'); $sql .= "FROM A JOIN B ON A.$_POST[join_a] = B.$_POST[join_b]";
Этот подход использует функцию in_array
PHP для обеспечения безопасности (а также предоставляет пользователю имена ваших базовых столбцов, но, учитывая ваше приложение, я сомневаюсь, что это проблема).
Выполните очистку ввода, например:
mb_regex_encoding($charset); // charset of database connection $sql .= 'FROM A JOIN B ON A.`' . mb_ereg_replace('`', '``', $_POST['join_a']) . '`' . ' = B.`' . mb_ereg_replace('`', '``', $_POST['join_b']) . '`'
В то время как мы здесь приводим пользовательский ввод и заменяем любую попытку пользователя выйти из этой цитаты, этот подход может быть наполнен всеми видами недостатков и уязвимостей (в функции PHP mb_ereg_replace
или в обработке MySQL специально созданных строк в цитированном идентификаторе) ,
Это намного лучше, если вообще можно использовать один из вышеперечисленных методов, чтобы избежать вставки пользовательских строк в свой SQL в целом.
Предполагая, что вход пользователя ограничен только выбором таблиц и полей (т. Е. Никаких дополнительных условий), вы должны быть в порядке с вашим подходом; звучит интересно 🙂
Я хотел бы добавить, что некоторые соединения лучше других. Например, объединение двух таблиц с использованием их первичных ключей (или других индексов) будет работать лучше, чем два несвязанных столбца, для которых требуется полное сканирование таблицы.
Все это зависит от того, насколько велики таблицы в первую очередь; для менее чем нескольких тысяч записей, вы должны быть в порядке; что-то вне серьезного созерцания на месте 🙂