MySQL wstawić do wielu tabel? (Normalizacja bazy danych?)

136

Próbowałem szukać insertinformacji w wielu tabelach w tym samym zapytaniu, ale okazało się, że jest to niemożliwe? Więc chcę to insertzrobić, używając po prostu wielu zapytań tj;

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')

Ale jak mogę dać automatycznego przyrostu idprzeprowadzoną usersdo „ręcznego” useridna profilestole?

Jay Wit
źródło
8
Chcesz dowiedzieć się o transakcjach.
vichle
3
możliwy duplikat sql - wstaw do wielu tabel w jednym zapytaniu
Limited Atonement,

Odpowiedzi:

241

Nie, nie możesz wstawiać do wielu tabel za pomocą jednego polecenia MySQL. Możesz jednak korzystać z transakcji.

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;

Zobacz, LAST_INSERT_ID()jak ponownie wykorzystać wartości autoincrement.

Edycja: powiedziałeś " Po tylu próbach rozwiązania tego problemu nadal nie działa. Czy nie mogę po prostu umieścić właśnie wygenerowanego identyfikatora w zmiennej $ var i wstawić go do wszystkich poleceń MySQL? "

Pozwól mi rozwinąć: są tutaj 3 możliwe sposoby:

  1. W kodzie, który widzisz powyżej. Robi to wszystko w MySQL, a LAST_INSERT_ID()w drugiej instrukcji automatycznie będzie wartość kolumny autoincrement, która została wstawiona w pierwszej instrukcji.

    Niestety, gdy druga instrukcja sama wstawia wiersze w tabeli z kolumną autoinkrementacji, LAST_INSERT_ID()zostanie zaktualizowana do tabeli 2, a nie do tabeli 1. Jeśli nadal będziesz potrzebować tego z tabeli 1, będziemy musieli ją zapisać w zmiennej. To prowadzi nas do dróg 2 i 3:

  2. Będzie LAST_INSERT_ID()przechowywać w zmiennej MySQL:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
    
  3. Będzie LAST_INSERT_ID()przechowywać w zmiennej php (lub w dowolnym języku, który może łączyć się z bazą danych, według twojego wyboru):

    • INSERT ...
    • Użyj swojego języka, aby pobrać LAST_INSERT_ID(), albo wykonując tę ​​dosłowną instrukcję w MySQL, albo używając na przykład php, mysql_insert_id()który robi to za Ciebie
    • INSERT [use your php variable here]

OSTRZEŻENIE

Niezależnie od wybranego sposobu rozwiązania tego problemu, musisz zdecydować, co powinno się stać w przypadku przerwania wykonywania między zapytaniami (na przykład awarii serwera bazy danych). Jeśli możesz żyć z „niektórzy skończyli, inni nie”, nie czytaj dalej.

Jeśli jednak zdecydujesz „albo zakończono wszystkie zapytania, albo żadne - nie chcę wierszy w niektórych tabelach, ale nie chcę pasujących wierszy w innych, zawsze chcę, aby moje tabele bazy danych były spójne”, musisz zawrzeć wszystkie instrukcje w transakcji. Dlatego użyłem BEGINi COMMITtutaj.

Skomentuj ponownie, jeśli potrzebujesz więcej informacji :)

Konerak
źródło
3
A co, jeśli chcę wstawić do więcej niż 2 tabel, a wszystkie pozostałe powinny mieć unikalny identyfikator i identyfikator użytkownika? Czy to jest możliwe?
Jay Wit,
Możesz umieścić last_insert_id z oryginalnej tabeli w zmiennej MySQL i użyć tej zmiennej we wszystkich innych tabelach. Sugestia f00 dotycząca używania procedury składowanej ma jeszcze większy sens, jeśli zamierzasz manipulować wieloma tabelami w jednym czasie.
Konerak
3
@Jay Wit: Zaktualizowałem odpowiedź. „Sposób 3” wyjaśnia, że ​​rzeczywiście możesz umieścić identyfikator w zmiennej i użyć go ponownie we wszystkich poleceniach MySQL, ale powinieneś przeczytać o transakcjach, jeśli chcesz, aby Twoja baza danych była spójna w przypadku awarii.
Konerak
3
Jasne, są to akceptowane instrukcje MySQL, podobnie jak SELECT, UPDATE, INSERT i DELETE. Najpierw zrób mały skrypt testowy i jeśli wszystko działa dobrze, jesteś gotowy.
Konerak
2
@Konerak masz jakieś wskazówki, jak używać tych wielu instrukcji SQL @mysql_variablesw połączeniu z przygotowanymi instrukcjami?
Fernando Silva,
17

dość proste, jeśli używasz procedur składowanych:

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

pełny skrypt:

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;
Jon Black
źródło
2
Przepraszam, ale czy to PHP? Nigdy czegoś takiego nie widziałem. Używam PHP i MySQL, jakieś wskazówki?
Jay Wit
1
wygląda jak długi zestaw instrukcji sql
Melbourne2991
1
@JayWit: Nie, to wszystko są instrukcje SQL. Utwórz procedury, a następnie możesz ich używać w dowolnym momencie. Zobacz dev.mysql.com/doc/refman/5.7/en/create-procedure.html
Pierre-Olivier Vares
7

Co by się stało, gdybyś chciał stworzyć wiele takich rekordów (aby zarejestrować 10 użytkowników, a nie tylko jednego)? Znajduję następujące rozwiązanie (tylko 5 zapytań):

Krok I: Utwórz tabelę tymczasową do przechowywania nowych danych.

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

Następnie wypełnij tę tabelę wartościami.

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

Tutaj zamiast $ALL_VALCiebie umieść listę wartości: ('test1', 'test1', 'bio1', 'home1'), ..., ('testn', 'testn', 'bion', 'homen')

Krok II: Wyślij dane do tabeli „user”.

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

Tutaj można użyć "IGNORE", jeśli pozwolisz niektórym użytkownikom być już w środku. Opcjonalnie możesz użyć UPDATE podobnie jak w kroku III, przed tym krokiem, aby znaleźć użytkowników, którzy są już w środku (i zaznaczyć ich w tabeli tmp). Tutaj zakładamy, że nazwa użytkownika jest zadeklarowana tak, jak PRIMARYw tabeli użytkowników.

Krok III: Zastosuj aktualizację, aby odczytać wszystkie identyfikatory użytkowników od użytkowników do tabeli tmp. TO NIEZBĘDNY KROK.

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

Krok IV: Utwórz kolejną tabelę, używając identyfikatora odczytu dla użytkowników

INSERT INTO profiles (userid, bio, homepage) 
SELECT id, bio, homepage FROM tmp
Zasega Anonimno
źródło
1
to jest 6 zapytań - nie zapomnij DROP TEMPORARY
Zigulik
3

Spróbuj tego

$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);
};

Referencje
PHP
MYSQL

diEcho
źródło
2
Najprawdopodobniej spowodowałoby to niepożądane zachowanie, jeśli serwer ulegnie awarii po utworzeniu użytkownika, ale przed utworzeniem profilu.
vichle
@Vichle to jaki jest najlepszy sposób?
diEcho
2
@vichle dodaj swoją transakcję cholerną do tego kodu i zakończ te bzdury już teraz
Your Common Sense
3
@Konerak Skrypty PHP uruchamiam od ponad 10 lat. Żaden z nich nie ma zwyczaju zatrzymywania się między liniami. lepiej pomyśleć o poprawie jakości serwera, aby nie zawieszał się tak często. To sieć, stary. to nie jest Rezerwa Federalna. Nie ma nic złego w jednej zerwanej rejestracji 100 000 000 udanych użytkowników.
Twój zdrowy rozsądek
2
W tym przykładzie możesz mieć rację. Mój kod jest jednak zawsze używany do księgowania - myśl o usunięciu pieniędzy w A i nie dodawaniu ich w B, gdy powinno, jest nie do przyjęcia. Poza tym, nawet jeśli to tylko sieć, transakcja nie jest taka trudna / droga? IMHO, oni mają dobre nawyki.
Konerak
3

spójrz na mysql_insert_id ()

tutaj dokumentacja: http://in.php.net/manual/en/function.mysql-insert-id.php

Daniel Kutik
źródło
1
Ta funkcja jest diabłem spójności bazy danych.
vichle
zgodził się - skorzystanie z transakcji może być lepszym rozwiązaniem
Daniel Kutik
@vichle: czy tak jest? Nie używam php tak często, ale pomyślałem, że to ich skrót do wywołania LAST_INSERT_ID()programisty?
Konerak
1
Wszystkie zapytania są transakcjami. Domyślnie w większości języków programowania transakcje są zatwierdzane automatycznie, ponieważ większość transakcji to tylko jedno zapytanie. Funkcja mysql_insert_id () jest przeznaczona do sytuacji, gdy wyłączyłeś automatyczne zatwierdzanie, ale bardzo często jest używana w złym kontekście przez osoby niezaznajomione z koncepcją transakcji.
vichle
3
@dnl, możesz użyć tej funkcji ORAZ transakcji w porządku. ta uwaga dotycząca transakcji nie ma związku z pytaniem.
Twój zdrowy rozsądek
1

To jest sposób, w jaki zrobiłem to dla projektu uni, działa dobrze, prawdopodobnie nie jest bezpieczny

$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);
}

}

SebastianZdroana
źródło
-1

Tylko uwaga na temat twojego słowa

Cześć, próbowałem wyszukać sposób wstawiania informacji w wielu tabelach w tym samym zapytaniu

Czy jesz wszystkie dania na lunch zmieszane z napojami w tej samej misce?
Przypuszczam - nie.

To samo tutaj.
Są rzeczy, które robimy osobno.
2 zapytania wprowadzające to 2 zapytania wprowadzające. W porządku. Nie ma w tym nic złego. Nie ma potrzeby mieszania go w jednym.
To samo dotyczy wyboru. Zapytanie musi być rozsądne i działać. To jedyne powody. Liczba zapytań nie jest.

Jeśli chodzi o transakcje - możesz z nich korzystać, ale nie jest to taka wielka sprawa dla przeciętnej witryny internetowej. Jeśli zdarzyło się raz w roku (jeśli w ogóle), że jedna rejestracja użytkownika została zerwana, bez wątpienia będziesz w stanie to naprawić.
istnieją setki tysięcy witryn z mysql bez sterownika obsługującego transakcje. Czy słyszałeś o strasznych katastrofach, które rozdzielały te strony? Ja też nie.

A mysql_insert_id () nie ma nic wspólnego z transakcjami. możesz w pełni włączyć do transakcji. to tylko inne sprawy. Ktoś zadał to pytanie nie wiadomo skąd.

Twój zdrowy rozsądek
źródło
Dlaczego miałbyś ryzykować konieczność naprawiania takich rzeczy, skoro można tego łatwo uniknąć?
vichle
@vichle moje tabele są typu myisam. Ze względu na wyszukiwanie pełnotekstowe, dziedzictwo i nawyk. Ryzykuję, tak. Jakie straszne (w teorii) niebezpieczeństwo mnie czeka.
Twój zdrowy rozsądek
6
Dlaczego jesteś taki agresywny? Po prostu stwierdzam, że najlepszym sposobem na to są transakcje. Robisz to na swój sposób, jeśli chcesz, faktem jest, że transakcje zostały utworzone dla tego rodzaju sytuacji.
vichle
@vichle yup, byłem dość szorstki, przepraszam. To ty to That function is the devil of database consistency.zrobiłeś, a nie transakcje. Nie widzę nic złego w transakcjach.
Twój zdrowy rozsądek
Przeprosiny przyjęte. Chodziło mi tylko o to, że funkcja jest często nadużywana przez osoby, które nie są świadome istnienia transakcji. Osoby te są również dość nadreprezentowane w społeczności php.
vichle
-4

W przypadku PDO możesz to zrobić

$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;
ocnet
źródło
Właśnie tego szukam.
Subroto Biswas