MySQL вставить в несколько таблиц? (Нормализация базы данных?)


136

Я пытался найти insertинформацию в нескольких таблицах в одном запросе, но обнаружил, что это невозможно? Так что я хочу insert, просто используя несколько запросов, т.е.

INSERT INTO users (username, password) VALUES('test', 'test')
INSERT INTO profiles (userid, bio, homepage) VALUES('[id of the user here?]','Hello world!', 'http://www.stackoverflow.com')

Но как я могу дать автоинкремент idот " usersдо" useridдля profileтаблицы?


8
Вы хотите узнать о транзакциях.
Вичле

Ответы:


241

Нет, вы не можете вставить в несколько таблиц в одной команде MySQL. Однако вы можете использовать транзакции.

BEGIN;
INSERT INTO users (username, password)
  VALUES('test', 'test');
INSERT INTO profiles (userid, bio, homepage) 
  VALUES(LAST_INSERT_ID(),'Hello world!', 'http://www.stackoverflow.com');
COMMIT;

Обратите внимание на LAST_INSERT_ID()повторное использование значений автоинкремента.

Редактировать: вы сказали: « После всего этого времени, пытаясь выяснить это, он все еще не работает. Разве я не могу просто поместить только что сгенерированный идентификатор в $ var и поместить этот $ var во все команды MySQL? »

Позвольте мне уточнить: есть 3 возможных пути здесь:

  1. В коде вы видите выше. Это делает все это в MySQL, и LAST_INSERT_ID()во втором операторе автоматически будет значение столбца автоинкремента, который был вставлен в первый оператор.

    К сожалению, когда второй оператор сам вставит строки в таблицу со столбцом автоинкремента, LAST_INSERT_ID()он будет обновлен до таблицы 2, а не таблицы 1. Если вам все еще понадобится таблица 1, мы должны будем ее сохранить. в переменной. Это приводит нас к способам 2 и 3:

  2. Будет храниться LAST_INSERT_ID()в переменной MySQL:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
  3. Запишем LAST_INSERT_ID()в переменную php (или любой язык, который может подключаться к базе данных, по вашему выбору):

    • INSERT ...
    • Используйте ваш язык для извлечения LAST_INSERT_ID(), либо выполнив этот буквальный оператор в MySQL, либо используя, например, php, mysql_insert_id()который сделает это за вас
    • INSERT [use your php variable here]

ПРЕДУПРЕЖДЕНИЕ

Какой бы способ решения этой проблемы вы ни выбрали, вы должны решить, что должно произойти, если выполнение будет прервано между запросами (например, сбой вашего сервера базы данных). Если вы можете жить с «некоторые закончили, другие нет», не читайте дальше.

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

Прокомментируйте еще раз, если вам нужно больше информации :)


3
А что, если я хочу вставить более двух таблиц, и я хочу, чтобы все остальные имели уникальный идентификатор и идентификатор пользователя? Это возможно?
Джей Вит

Вы можете поместить last_insert_id из исходной таблицы в переменную MySQL и использовать эту переменную во всех других ваших таблицах. Предложение f00 об использовании хранимой процедуры имеет еще больший смысл, если вы собираетесь манипулировать большим количеством таблиц за один раз.
Конерак

3
@ Jay Wit: я обновил ответ. «Способ 3» объясняет, что вы действительно можете поместить идентификатор в переменную и повторно использовать его во всех командах MySQL, но вам следует прочитать о транзакциях, если вы хотите, чтобы ваша база данных была согласованной в случае сбоя.
Конерак

3
Конечно, они принимаются операторы MySQL, как SELECT, UPDATE, INSERT и DELETE. Сначала сделайте небольшой тестовый сценарий, и, если все работает нормально, все готово.
Конерак

2
@ Konerak какие-либо советы о том, как использовать эти несколько операторов SQL @mysql_variablesвместе с подготовленными операторами?
Фернандо Сильва

17

довольно просто, если вы используете хранимые процедуры:

call insert_user_and_profile('f00','http://www.f00.com');

полный скрипт:

drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) unique not null
)
engine=innodb;

drop table if exists user_profile;
create table user_profile
(
profile_id int unsigned not null auto_increment primary key,
user_id int unsigned not null,
homepage varchar(255) not null,
key (user_id)
)
engine=innodb;

drop procedure if exists insert_user_and_profile;

delimiter #

create procedure insert_user_and_profile
(
in p_username varchar(32),
in p_homepage varchar(255)
)
begin
declare v_user_id int unsigned default 0;

insert into users (username) values (p_username);
set v_user_id = last_insert_id(); -- save the newly created user_id

insert into user_profile (user_id, homepage) values (v_user_id, p_homepage);

end#

delimiter ;

call insert_user_and_profile('f00','http://www.f00.com');

select * from users;
select * from user_profile;

2
Извините, но это PHP? Я никогда не видел ничего подобного. Я использую PHP и MySQL, какие-нибудь советы?
Джей Вит

1
выглядит как длинный набор SQL-операторов
Melbourne2991

1
@JayWit: Нет, это все операторы SQL. Создайте процедуры, и вы сможете использовать их в любое время. См. Dev.mysql.com/doc/refman/5.7/en/create-procedure.html
Пьер-Оливье Варес,

7

Что произойдет, если вы захотите создать много таких записей (зарегистрировать 10 пользователей, а не только одного)? Я нахожу следующее решение (всего 5 запросов):

Шаг I: Создать временную таблицу для хранения новых данных.

CREATE TEMPORARY TABLE tmp (id bigint(20) NOT NULL, ...)...;

Затем заполните эту таблицу значениями.

INSERT INTO tmp (username, password, bio, homepage) VALUES $ALL_VAL

Здесь вместо $ALL_VALвас разместите список значений: ('test1', 'test1', 'bio1', 'home1'), ..., ('testn', 'testn', 'bion', 'homen')

Шаг II: Отправьте данные в таблицу «user».

INSERT IGNORE INTO users (username, password)
SELECT username, password FROM tmp;

Здесь можно использовать «IGNORE», если вы разрешаете некоторым пользователям уже находиться внутри. При желании вы можете использовать ОБНОВЛЕНИЕ, аналогичное шагу III, перед этим шагом, чтобы найти, какие пользователи уже находятся внутри (и отметить их в таблице tmp). Здесь мы предполагаем, что имя пользователя объявлено как PRIMARYв таблице пользователей.

Шаг III: применить обновление, чтобы прочитать все идентификаторы пользователей от пользователей в таблицу tmp. ЭТО СУЩЕСТВЕННЫЙ ШАГ.

UPDATE tmp JOIN users ON tmp.username=users.username SET tmp.id=users.id

Шаг IV: создайте другую таблицу, используя чтение идентификатора для пользователей

INSERT INTO profiles (userid, bio, homepage) 
SELECT id, bio, homepage FROM tmp

1
это 6 запросов - не забудьте DROP TEMPORARY
Zigulik

3

попробуй это

$sql= " INSERT INTO users (username, password) VALUES('test', 'test') ";
mysql_query($sql);
$user_id= mysql_insert_id();
if(!empty($user_id) {

$sql=INSERT INTO profiles (userid, bio, homepage) VALUES($user_id,'Hello world!', 'http://www.stackoverflow.com');
/* or 
 $sql=INSERT INTO profiles (userid, bio, homepage) VALUES(LAST_INSERT_ID(),'Hello   world!', 'http://www.stackoverflow.com'); */
 mysql_query($sql);
};

Рекомендации
PHP
MYSQL


2
Это, скорее всего, приведет к некоторому нежелательному поведению, если произойдет сбой сервера после создания пользователя, но до создания профиля.
Вичле

@ Vichle тогда, что лучше?
DiEcho

2
@vichle добавь свою проклятую транзакцию в этот код и прекрати эту ерунду уже
Твой здравый смысл

3
@Konerak Я использую PHP-скрипты уже более 10 лет. Ни один из них не имеет привычки останавливаться между строк. вам лучше подумать об улучшении качества вашего сервера, чтобы он не ломался так часто. Это сеть, чувак. это не Федеральная резервная система. Ничего плохого в том, что один сломанный пользователь зарегистрировал 100 000 000 успешных.
Ваш здравый смысл

2
В этом примере вы можете быть правы. Мой код всегда используется для учета - мысль об удалении денег в A, а не добавлении их в B, как это и должно быть, недопустима. Кроме того, даже если это просто веб, транзакция не так уж сложна / дорога? ИМХО, они делают хорошие привычки.
Конерак

3

взгляните на mysql_insert_id ()

вот документация: http://in.php.net/manual/en/function.mysql-insert-id.php


1
Эта функция - дьявол согласованности базы данных.
Вичле

согласился - лучше всего использовать транзакцию
Даниэль Кутик

@vichle: это так? Я не так часто использую php, но я решил, что это был их путь к вызову LAST_INSERT_ID()программиста?
Конерак

1
Все запросы являются транзакциями. В большинстве языков программирования по умолчанию используется автоматическая фиксация транзакций, поскольку большинство транзакций - это всего лишь один запрос. Функция mysql_insert_id () предназначена для тех случаев, когда вы отключили автоматическую фиксацию, но очень часто используется в неправильном контексте людьми, не знакомыми с концепцией транзакции.
Вичле

3
@ dnl, вы можете использовать эту функцию и транзакцию в порядке. это замечание по сделке не имеет отношения к вопросу.
Ваш здравый смысл

1

Это способ, которым я сделал это для универа, работает нормально, но не безопасно

$dbhost = 'localhost';
$dbuser = 'root';
$dbpass = '';
$conn = mysql_connect($dbhost, $dbuser, $dbpass);

$title =    $_POST['title'];            
$name =     $_POST['name'];         
$surname =  $_POST['surname'];                  
$email =    $_POST['email'];            
$pass =     $_POST['password'];     
$cpass =    $_POST['cpassword'];        

$check = 1;

if (){
}
else{
    $check = 1;
}   
if ($check == 1){

require_once('website_data_collecting/db.php');

$sel_user = "SELECT * FROM users WHERE user_email='$email'";
$run_user = mysqli_query($con, $sel_user);
$check_user = mysqli_num_rows($run_user);

if ($check_user > 0){
    echo    '<div style="margin: 0 0 10px 20px;">Email already exists!</br>
             <a href="recover.php">Recover Password</a></div>';
}
else{
    $users_tb = "INSERT INTO users ". 
           "(user_name, user_email, user_password) ". 
        "VALUES('$name','$email','$pass')";

    $users_info_tb = "INSERT INTO users_info".
           "(user_title, user_surname)".
        "VALUES('$title', '$surname')";

    mysql_select_db('dropbox');
    $run_users_tb = mysql_query( $users_tb, $conn );
    $run_users_info_tb = mysql_query( $users_info_tb, $conn );

    if(!$run_users_tb || !$run_users_info_tb){
        die('Could not enter data: ' . mysql_error());
    }
    else{
        echo "Entered data successfully\n";
    }

    mysql_close($conn);
}

}


-1

Просто замечание о вашей поговорке

Привет, я попытался найти способ вставить информацию в несколько таблиц в одном запросе

Вы едите все свои обеденные блюда, смешанные с напитками в одной миске?
Я полагаю - нет.

Тоже самое.
Есть вещи, которые мы делаем отдельно.
2 запроса на вставку - это 2 запроса на вставку. Все хорошо. Ничего плохого в этом нет. Не нужно пюре в одном.
То же самое для выбора. Запрос должен быть разумным и делать свое дело. Это единственные причины. Количество запросов нет.

Что касается транзакций - вы можете их использовать, но это не так уж важно для обычного веб-сайта. Если случится один раз в год (если вообще когда-либо), что одна регистрация пользователя будет прервана, вы, несомненно, сможете это исправить.
Есть сотни тысяч сайтов, работающих под управлением MySQL без драйвера поддержки транзакций. Вы слышали о страшных бедствиях, разрушающих эти места? И я нет.

И mysql_insert_id () не имеет ничего общего с транзакциями. Вы можете включить в транзакцию все в порядке. это просто разные вопросы. Кто-то поднял этот вопрос из ниоткуда.


Почему вы хотите рискнуть исправить подобные вещи, если их легко избежать?
Вичле

@vichle мои таблицы имеют тип myisam. Ради полнотекстового поиска, наследия и привычки. Я рискую, ага. Какая ужасная (теоретически) опасность ждет меня.
Твой здравый смысл

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

@vichle Да, я был довольно резок, я прошу прощения. Это That function is the devil of database consistency.сделал ваш , а не транзакции. Я не вижу ничего плохого в сделках.
Ваш здравый смысл

Извинения приняты. Я имел в виду только то, что эта функция часто используется людьми, которые не знают о существовании транзакций. Эти люди также очень перепредставлены в сообществе php.
Вичле

-4

Для PDO Вы можете сделать это

$stmt1 = "INSERT INTO users (username, password) VALUES('test', 'test')"; 
$stmt2 = "INSERT INTO profiles (userid, bio, homepage) VALUES('LAST_INSERT_ID(),'Hello world!', 'http://www.stackoverflow.com')";

$sth1 = $dbh->prepare($stmt1);
$sth2 = $dbh->prepare($stmt2);

BEGIN;
$sth1->execute (array ('test','test'));
$sth2->execute (array ('Hello world!','http://www.stackoverflow.com'));
COMMIT;

Это именно то, что я ищу.
Суброто Бисвас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.