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
Ви хочете дізнатися про транзакції.
vichle

Відповіді:


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 і поставити цей $ вар у всі команди MySQL? "

Дозвольте пояснити: тут є три можливі способи:

  1. У коді, який ви бачите вище. Це робиться все в MySQL, і LAST_INSERT_ID()в другому операторі автоматично буде значення стовпця autoincrement, який було вставлено в перший оператор.

    На жаль, коли друге твердження самостійно вставляє рядки в таблицю з стовпцем з автоматичним збільшенням, 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
А що робити, якщо я хочу вставити більше 2 таблиць, і я хочу, щоб усі інші мали унікальний ідентифікатор та userid? Це можливо?
Jay Wit

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

3
@Jay Wit: Я оновив відповідь. "Шлях 3" пояснює, що ви дійсно можете помістити ідентифікатор в змінну і повторно використовувати його у всіх командах MySQL, але вам слід прочитати про транзакції, якщо ви хочете, щоб ваш db був узгоджений у випадку збою.
Конерак

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, якісь поради?
Jay Wit

1
виглядає як довгий набір заяв sql
Melbourne2991

1
@JayWit: Ні, це всі заяви SQL. Створіть процедури, тоді ви зможете використовувати будь-який час, коли захочете. Дивіться dev.mysql.com/doc/refman/5.7/uk/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: Надішліть дані до таблиці "користувач".

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

Тут "IGNORE" можна використовувати, якщо ви дозволите деяким користувачам вже бути всередині. Додатково ви можете використовувати UPDATE, подібний до етапу 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 запитів - не забудьте
ЗРОБИТИ ТЕМУ

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

@Vichle то який найкращий спосіб ??
diEcho

2
@vichle додайте свою корупційну транзакцію до цього коду та припиніть цю нісенітницю
Ваш здоровий глузд

3
@Konerak Я використовую сценарії PHP більше 10 років. Жоден з них не має звички зупинятися між лініями. вам краще подумати над покращенням якості вашого сервера, щоб не виходити з ладу НЕ часто. Це веб, чувак. це не Федеральний резерв. Немає нічого поганого в одній зірваній реєстрації користувачів у 100 000 000 успішних.
Твій здоровий глузд

2
У цьому прикладі ви можете мати рацію. Мій код завжди використовується для бухгалтерського обліку - думка про вилучення грошей в А, а не додавання їх у В, поки слід, нестерпна. Крім того, навіть якщо це просто Інтернет, транзакція не є такою важкою / дорогою? ІМХО, вони роблять хороші звички.
Конерак

3

подивіться на mysql_insert_id ()

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


1
Ця функція є дияволом послідовності баз даних.
vichle

домовились - використання транзакції може бути кращим рішенням
Даніель Кутік

@vichle: це так? Я часто не використовую php, але я зрозумів, що це їх ярлик для виклику LAST_INSERT_ID()програміста?
Конерак

1
Усі запити - це транзакції. За замовчуванням у більшості мов програмування відбувається автоматичне здійснення транзакцій, оскільки більшість транзакцій - це лише один запит. Функція mysql_insert_id () призначена для того, коли ви вимкнули автоматичне здійснення, але дуже часто використовується в неправильному контексті людьми, не знайомими з концепцією транзакцій.
vichle

3
@dnl Ви можете використовувати цю функцію І транзакцію все в порядку. це зауваження про транзакцію не має значення для питання.
Твій здоровий глузд

1

Це те, що я зробив це для проекту uni, працює добре, не захищаючи

$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

@vichle мої таблиці типу myisam. Заради повнотекстового пошуку, спадщини та звички. Я ризикую, так. Яка страшна (теоретично) небезпека мене чекає.
Твій здоровий глузд

6
Чому ти такий агресивний? Я просто констатую, що транзакції - це спосіб пройти сюди. Ви робите це так, як хочете, все-таки факт, що транзакції були створені для подібної ситуації.
vichle

@vichle yup, я був дуже суворий, прошу вибачення. Саме це That function is the devil of database consistency.зробили, а не угоди. Я нічого поганого в операціях не бачу.
Твій здоровий глузд

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

-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;

Це саме те, що я шукаю.
Subroto Biswas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.