Добавление массива в БД в цикле с помощью подготовленного запроса

littus

Новичок
Всем доброго дня!
Есть кусок рабочего кода, который берет данные из одной таблицы БД mySQL и должен их положить в другую таблицу этой же БД.
Нижеприведенный кусок кода работоспособен.
Код:
// Выборка нужных записей и формирование массива данных
       $sql = "SELECT id, name, email, theme, text, datetime FROM $name_table WHERE id IN ($id_string)";
       $result_sql= $this->_db->query($sql);
       $result_set-> fetch_all(MYSQLI_ASSOC);
    
     // Преобразование рез.выборки ($result_set) и пересохранение в корзину (trashboxes)
       $string= array(); // инициал.массив для строки с склеинными запятой переменными

       foreach ($result_set as $result) { // main foreach
         $value= array(); // промежуточный массив для значения переменных

         foreach ($result as $item) {  $value[]= "'".$item."'"; }
        
         $string[]= '('.implode("," ,$value). ')';
       } // main foreach END
      
       $sql = "INSERT INTO trashboxes(id, name, email, theme, text, datetime) VALUES".implode(',',$string);
       $result2_sql= $this->_db->query($sql);

Хочу сделать SQL-запрос на вставку данных с помощью синтаксиса метода подготовленного запроса.
Код:
       $sql = "SELECT id, name, email, theme, text, datetime FROM $name_table WHERE id IN ($id_string)";
       $result_sql= $this->_db->query($sql);
       $result_set-> fetch_all(MYSQLI_ASSOC);
  
     // Преобразование рез.выборки ($result_set) и пересохранение в корзину (trashboxes)
       $string= array(); // инициал.массив для строки с склеинными запятой переменными

       foreach ($result_set as $result) { // main foreach
         $value= array(); // промежуточный массив для значения переменных

         foreach ($result as $item) {  $value[]= "'".$item."'"; }
      
         $string[]= '('.implode("," ,$value). ')';
       } // main foreach END
    
       $sql = "INSERT INTO trashboxes(id, name, email, theme, text, datetime) VALUES".implode(',',$string);
         $stmt->prepare($sql);
    foreach($result_set as $item){
                $stmt->bind_param("i", $item['id']);
                $stmt->bind_param("s", $item['name']);
                $stmt->bind_param("s", $item['email']);
                $stmt->bind_param("s", $item['theme']);
                $stmt->bind_param("s", $item['text']);
                $stmt->bind_param("i", $item['datetime']);
    }
            $stmt->execute(); /* выполняем запрос */
Как это правильно прописать в данном случае?
Спасибо.
 
Последнее редактирование:

Фанат

oncle terrible
Команда форума
1. Начнем с того, что никто обычно ничего никуда не переливает. А тупо добавляется флаг "письмо удалено".
2. продолжим тем, что для переливки из одной таблицы в другую, пхп вообще не нужен - пишется запрос вида INSERT INTO table SELECT from table 2 WHERE...
3. Если уж все-таки интересуют подготовленные выражения, то про них надо сначала ПРОЧИТАТЬ. А потом выполнить какой-нибудь запрос. Не переписать готовый, а выполнить совсем новый, для практики. чтобы освоить в принципе работу с данной технологией, понять принцип. Ты сейчас скажешь, что тебе и нужен готовый код, чтобы понять принцип. Ну так в мануале есть полный рабочий пример! Его и надо сначала разобрать. Контрольный вопрос- ЧТО должно быть в строек запроса вместо того что там сейчас?
4. mysqli в принципе нельзя использовать в коде приложения. Это полуфабрикат, конструктор. Его нельзя есть сырым, а только в приготовленном виде. Если очень хочется использовать встроенное в пхп решение - то только PDO
 

littus

Новичок
Я как раз и пытаюсь понять как оно должно работать. Просто сейчас старался делать по аналогии с тем чем уже пользовался. Вот, у меня до этого в Классе был один из методов на вставку данных в БД из пользовательской формы, я писал так:

Код:
// f_saveNews- Добавление новой записи(письма) в табл.(outboxes) БД
    public function f_saveMail($email_to, $theme, $text_email, $dt) {
        $stmt= $this->_db->stmt_init(); //выделяем память и инициализируем объект запроса

        try{
        //заполняем табл. "outboxes"
            $sql = "INSERT INTO outboxes(email, theme, text, datetime) VALUES(?,?,?,?)"; // строка запроса для подготовленного запроса
            $stmt->prepare($sql); /* создаем подготавливаемый запрос */
            $stmt->bind_param("sssi", $email_to,$theme,$text_email,$dt); /* привязываем переменные к параметрам */
            $stmt->execute(); /* выполняем запрос */
            if(!$stmt) { throw new Exception($this->_db->error); } // если в($stmt) ничего не пришло,возбуждаем Exception
                return TRUE; // если все ОК и в($stmt) все пришло,-возвращаем TRUE
        }
        catch(Exception $e){ // сценарий если есть отловленные ошибки
           return FALSE;
        }     
            $stmt->close(); // очищаем результурующий набор
            $this->_db->close(); // закрываем соединение         
    } // END f_saveMail
PDO не дочитал еще это правда, читаю.
Хотелось бы и в таком подходе разобраться. Неужели mysqli с методом подготовленных запросов такой неприемлемый? Это связано с уязвимостью? Но тут же тоже напрямую данные не передаются как и с PDO.

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

P/S: Спасибо, что подсказали, про то, что можно в данном случае не использовать php, а сделать все в одной выборке SQL. Если я правильно понял, я, имея 2 запроса с одним критерием на выборку и вставку,
$sql = "SELECT id, name, email, theme, text, datetime FROM $name_table WHERE id IN ($id_string)";
$sql = "INSERT INTO trashboxes(id, name, email, theme, text, datetime) VALUES
а также при условии, что поля у таблиц одинаковые
я могу написать так:
Код:
$sql = "INSERT INTO trashboxes SELECT id, name, email, theme, text, datetime FROM $name_table WHERE id IN ($id_string)";
а $id_string - это у меня массив чекбоксов с значениями айдишников/

Так будет правильно, корректный запрос?
 
Последнее редактирование:

AnrDaemon

Продвинутый новичок
Тебе уже сказали, не надо ничего копировать. Это лишние затраты времени на всех стадиях.
Добавь колонку "удалено" и проставляй чекбоксы.
 

Фанат

oncle terrible
Команда форума
просто для информации:
твоя функция, почищенная от ненужного и вредного мусора:

PHP:
    public function f_saveMail($email_to, $theme, $text_email, $dt) {
            $sql = "INSERT INTO outboxes(email, theme, text, datetime) VALUES(?,?,?,?)";
            $stmt->prepare($sql);
            $stmt->bind_param("sssi", $email_to,$theme,$text_email,$dt);
            $stmt->execute();
    }
Можешь задать вопросы, если какие-то удаления кажутся тебе лишними.
 
Последнее редактирование:

Фанат

oncle terrible
Команда форума
Неужели mysqli с методом подготовленных запросов такой неприемлемый?
Когда ты попытаешься написать функцию, которая принимает переменное число параметров в запрос, ты сразу поймешь.
 

ksnk

прохожий
Вообще говоря, разница не Mysqli и PDO, и а именованных параметрах и плейсхолдерах-вопросиках.
Можно себе вообразить совершенно правильный большой запрос, написанный пару (хм... пару может быть мало, тройку :) ) лет назад. Выполнен с вопросиками-плейсхолдерами. Используется он в паре десятков мест по всему коду. И вот теперь, в результате подвижек земной коры, или изменении структуры базы, в этом запросе нужно преставить местами пару вопросиков-параметров.
Требуется вообразить объем работ. Требуется оценить объем работ, в случае PDO с именоваными плейсхолдерами..
 

Фанат

oncle terrible
Команда форума
Вообще говоря, разница не Mysqli и PDO, и а именованных параметрах и плейсхолдерах-вопросиках.
Можно себе вообразить совершенно правильный большой запрос, написанный пару (хм... пару может быть мало, тройку :) ) лет назад. Выполнен с вопросиками-плейсхолдерами. Используется он в паре десятков мест по всему коду. И вот теперь, в результате подвижек земной коры, или изменении структуры базы, в этом запросе нужно преставить местами пару вопросиков-параметров.
Требуется вообразить объем работ. Требуется оценить объем работ, в случае PDO с именоваными плейсхолдерами..
Это очень тупой комментарий.
Понятное дело, что если у тебя в паре десятков мест продублирован один и тот же код, то проблема СОВСЕМ не в вопросиках.
Ценность именовынных плейсхолдеров сильно рпеувеличена. Малозначительный, хотя и приятный бонус. Ипоганеный самими же авторами либы.
 

ksnk

прохожий
Тогда мне тоже не ясно отличие PDO от Mysqli. И тот факт, что предпочтение (в этом топике) отдано PDO. Если не именованные плейсхолдеры, то что?
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
PDO подразумевает некую абстракцию над БД, а mysqli - нет. Лично мне так видится, Фанат, наверное не согласится =D
 

Фанат

oncle terrible
Команда форума
PDO подразумевает некую абстракцию над БД
Именно это я и пытаюсь втолковать в дурные головы. Но чтобы это понять, надо понимать что такое абстракция и уметь пользоваться ей. А с этим как раз у большинства проблемы.
 
Последнее редактирование:

Фанат

oncle terrible
Команда форума
Тогда мне тоже не ясно отличие PDO от Mysqli. И тот факт, что предпочтение (в этом топике) отдано PDO. Если не именованные плейсхолдеры, то что?
Вообще, выше я привел один из примеров: попробуй выполнить в mysqli запрос с динамическим числом переменных, а потом отойди в сторонку и посмотри свежим взглядом, какую ароматную кучу ты наложил в своем коде.

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

ksnk

прохожий
А что, PDO можно пользоваться без минимальной, хотя бы, обертки, которая хранит текущее открытое соединение, и заботится об открытии, если что...? Если без обертки не обойтись - это что, готовый продукт? А mysqli, который вполне себе самодостаточен - полуфабрикат?
Преимущество PDO по работе с несколькими базами, в этом топике не играет, тут уже есть mysql сервер. Хотя согласен, переносимость кода - важно.
попробуй выполнить в mysqli запрос с динамическим числом переменных
Фантазии у меня маловато...
Такой? Динамическое (в зависимости от переданного массива) количество условий.
Код:
SELECT value FROM  table
WHERE param1=?
    AND  param2=?
...
Дополнительные параметры - ключи массива, значения - значения. Реализация этого будет отличаться для PDO и mysqli?
 

ksnk

прохожий
Найдите 10 отличий...
PHP:
$param=['param1'=>1,'param2'=>2,'param3'=>4,'param4'=>4,'param5'=>'Hello!',];

$sql_tpl='select * from table where ';

// 4PDO
$stmt = $dbh->prepare($sql_tpl.' `'.implode("`=? and `",array_keys($param)),'`=?');
$cnt=0;
foreach($param as $key=>&$val){
    $stmt->bindParam($cnt++, $val, PDO::PARAM_STR);
}
$stmt->execute();

// 4mysqli
mysqli_stmt_prepare($stmt,$sql_tpl.' `'.implode("`=? and `",array_keys($param)),'`=?');
foreach($param as $key=>$val){
    mysqli_stmt_bind_param($stmt,'s', $val);
}
mysqli_stmt_execute($stmt);
 
Последнее редактирование:

Фанат

oncle terrible
Команда форума
А что, PDO можно пользоваться без минимальной, хотя бы, обертки, которая хранит текущее открытое соединение, и заботится об открытии
В этом смысле пдо и мускли равны, т.е. эту твою претензию можно сократить.
Найдите 10 отличий...
Для ПДО твой код слишком раздут. То есть, ты не умеешь пользоваться даже им.
А для мускли ты его не запускал.
Непонятно, зачем ты впрягся за мускли, если никогда в жизни их не видел
 
Последнее редактирование:

ksnk

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

Фанат

oncle terrible
Команда форума
Исправил mysqli часть
Давай ты больше не будешь сюда постить код, не имея ни малейшего представления о том, работает ли он вообще.
Согласись - этот цикл начинает утомлять.

Рекомендуя написать код, я имел в виду, разумеется, не просто каля-маля от балды, а рабочий код.
В этом весь смысл моего предложения. Вся суть и мякотка.
Суть в том, что пространные объяснения не нужны. Как говорил Линус, talk is cheap, geve me code.
И вот после того как ты заставишь, наконец, этот код работать, то посмотрев на него, ты СРАЗУ, САМ, без ненужных объяснений поймешь, почему ПДО лучше мускли

Особенно после того, как сократишь пдо вариант в два раза
 
Последнее редактирование:

ksnk

прохожий
Добрался до сервера и смастерил рабочий код. Для mysqli в этом случае действительно получается срань господня. Моему представлению о разумности мира нанесен сильный удар... Кто бы подумал, что bind_param можно вызывать только один раз? :confused:
PHP:
function refValues($arr){
    if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+
    {
        $refs = array();
        foreach($arr as $key => $value)
            $refs[$key] = &$arr[$key];
        return $refs;
    }
    return $arr;
}

$param=['param1'=>1,'param2'=>2,'param3'=>4,'param4'=>4,'param5'=>'Hello!'];
$sql_tpl='select * from `table` where ';

$smtp=$mysqli->prepare($sql_tpl.' `'.implode("`=? and `",array_keys($param)).'`=?');

array_unshift($param,str_repeat('s',count($param)));
call_user_func_array(
    array($smtp, "bind_param"),refValues($param)
);

$smtp->execute();
$result=$smtp->get_result();
var_dump($result->fetch_all ());
Для PDO, кроме исправления синтаксических ошибок, никаких принципиальных улучшений нет.
PHP:
$param=['param1'=>1,'param2'=>2,'param3'=>4,'param4'=>4,'param5'=>'Hello!'];
$sql_tpl='select * from `table` where ';

// 4PDO
$stmt = $dbh->prepare($sql_tpl.' `'.implode("`=? and `",array_keys($param)).'`=?');
$cnt=0;
foreach($param as $key=>$val){
    $stmt->bindParam(++$cnt, $param[$key], PDO::PARAM_STR);
}
$stmt->execute();
var_dump($stmt->fetchAll ());
Там точно можно что-то улучшить?
 

Фанат

oncle terrible
Команда форума
Моему представлению о разумности мира нанесен сильный удар...
Нормальный код, у всех бывают заморочки.
Все равно этот код пишется один раз, и потом вызывается из враппера.
Там точно можно что-то улучшить?
PHP:
$stmt->execute(array_values($param));
же
 
Сверху