MySQL In mehrere Tabellen einfügen? (Datenbanknormalisierung?)

136

Ich habe versucht, einen Weg zu insertInformationen in mehreren Tabellen in derselben Abfrage zu finden, aber herausgefunden, dass dies unmöglich ist. Ich möchte insertes also einfach mit mehreren Abfragen tun, z.

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

Aber wie kann ich das automatische Inkrementieren idvon userszum "Handbuch" useridfür die profileTabelle geben?

Jay Wit
quelle
8
Sie möchten mehr über Transaktionen erfahren.
Vichle
3
Mögliches Duplikat von SQL - Einfügen in mehrere Tabellen in einer Abfrage
Eingeschränktes Sühnopfer

Antworten:

240

Nein, Sie können nicht mit einem MySQL-Befehl in mehrere Tabellen einfügen. Sie können jedoch Transaktionen verwenden.

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;

Schauen Sie sich an LAST_INSERT_ID(), um Autoincrement-Werte wiederzuverwenden.

Bearbeiten: Sie sagten: " Nach all der Zeit, in der Sie versucht haben, es herauszufinden, funktioniert es immer noch nicht. Kann ich die einfach generierte ID nicht einfach in eine $ var und diese $ var in alle MySQL-Befehle einfügen? "

Lassen Sie mich näher darauf eingehen: Hier gibt es drei Möglichkeiten:

  1. In dem Code, den Sie oben sehen. Dies macht alles in MySQL, und die LAST_INSERT_ID()in der zweiten Anweisung ist automatisch der Wert der Autoincrement-Spalte, die in die erste Anweisung eingefügt wurde.

    Wenn die zweite Anweisung selbst Zeilen in eine Tabelle mit einer Spalte für die automatische Inkrementierung einfügt, LAST_INSERT_ID()wird diese leider auf die von Tabelle 2 und nicht auf Tabelle 1 aktualisiert. Wenn Sie die von Tabelle 1 danach noch benötigen, müssen wir sie speichern in einer Variablen. Dies führt uns zu den Wegen 2 und 3:

  2. Wird das LAST_INSERT_ID()in einer MySQL-Variablen speichern:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
  3. Wird die LAST_INSERT_ID()in einer PHP-Variablen (oder einer beliebigen Sprache, die eine Verbindung zu einer Datenbank Ihrer Wahl herstellen kann) auf Lager haben :

    • INSERT ...
    • Verwenden Sie Ihre Sprache, um das abzurufen LAST_INSERT_ID(), indem Sie entweder diese Literalanweisung in MySQL ausführen oder beispielsweise PHPs verwenden, mysql_insert_id()die dies für Sie tun
    • INSERT [use your php variable here]

WARNUNG

Unabhängig davon, wie Sie dieses Problem lösen möchten, müssen Sie entscheiden, was passieren soll, wenn die Ausführung zwischen Abfragen unterbrochen wird (z. B. wenn Ihr Datenbankserver abstürzt). Wenn Sie mit "einige sind fertig, andere nicht" leben können, lesen Sie nicht weiter.

Wenn Sie jedoch entscheiden, dass "entweder alle Abfragen beendet werden oder keine abgeschlossen werden - ich möchte keine Zeilen in einigen Tabellen, aber keine übereinstimmenden Zeilen in anderen, ich möchte immer, dass meine Datenbanktabellen konsistent sind", müssen Sie alle Anweisungen in eine Transaktion einschließen. Deshalb habe ich das BEGINund COMMIThier benutzt.

Nochmals kommentieren, wenn Sie weitere Informationen benötigen :)

Konerak
quelle
3
Und was ist, wenn ich in mehr als 2 Tabellen einfügen möchte und alle anderen eine eindeutige ID und die Benutzer-ID haben sollen? Ist das möglich?
Jay Wit
Sie können die last_insert_id aus der ursprünglichen Tabelle in eine MySQL-Variable einfügen und diese Variable in allen anderen Tabellen verwenden. Der Vorschlag von f00 für die Verwendung einer gespeicherten Prozedur ist noch sinnvoller, wenn Sie viele Tabellen gleichzeitig bearbeiten möchten.
Konerak
3
@ Jay Wit: Ich habe die Antwort aktualisiert. 'Way 3' erklärt, dass Sie die ID zwar in eine Variable einfügen und in allen MySQL-Befehlen wiederverwenden können. Sie sollten jedoch Informationen zu Transaktionen lesen, wenn Ihre Datenbank im Falle eines Absturzes konsistent sein soll.
Konerak
3
Sicher, sie werden als MySQL-Anweisungen akzeptiert, genau wie SELECT, UPDATE, INSERT und DELETE. Machen Sie zuerst ein kleines Testskript, und wenn alles gut funktioniert, können Sie loslegen.
Konerak
2
@Konerak irgendwelche Tipps zur Verwendung dieser mehreren SQL-Anweisungen @mysql_variablesin Verbindung mit vorbereiteten Anweisungen?
Fernando Silva
17

ziemlich einfach, wenn Sie gespeicherte Prozeduren verwenden:

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

vollständiges Skript:

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
quelle
2
Sorry, aber ist das PHP? Ich habe so etwas noch nie gesehen. Ich benutze PHP und MySQL, irgendwelche Tipps?
Jay Wit
1
sieht aus wie eine lange Reihe von SQL-Anweisungen
Melbourne2991
1
@ JayWit: Nein, das sind alles SQL-Anweisungen. Erstellen Sie die Prozeduren, und Sie können sie jederzeit verwenden. Siehe dev.mysql.com/doc/refman/5.7/en/create-procedure.html
Pierre-Olivier Vares
7

Was würde passieren, wenn Sie viele solcher Datensätze erstellen möchten (um 10 Benutzer zu registrieren, nicht nur einen)? Ich finde die folgende Lösung (nur 5 Abfragen):

Schritt I: Erstellen Sie eine temporäre Tabelle zum Speichern neuer Daten.

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

Füllen Sie als Nächstes diese Tabelle mit Werten.

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

Hier platzieren Sie anstelle $ALL_VALIhrer Werte eine Werteliste: ('test1', 'test1', 'bio1', 'home1'), ..., ('testn', 'testn', 'bion', 'homen')

Schritt II: Senden Sie Daten an die 'Benutzer'-Tabelle.

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

Hier kann "IGNORE" verwendet werden, wenn Sie zulassen, dass sich einige Benutzer bereits im Inneren befinden. Optional können Sie UPDATE ähnlich wie in Schritt III vor diesem Schritt verwenden, um herauszufinden, wer sich bereits im Benutzer befindet (und sie in der tmp-Tabelle markieren). Hier nehmen wir an, dass der Benutzername wie PRIMARYin der Benutzertabelle deklariert ist .

Schritt III: Wenden Sie das Update an, um alle Benutzer-IDs von Benutzern in die tmp-Tabelle zu lesen. Dies ist ein wesentlicher Schritt.

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

Schritt IV: Erstellen Sie eine weitere Tabelle unter Verwendung der Lese-ID für Benutzer

INSERT INTO profiles (userid, bio, homepage) 
SELECT id, bio, homepage FROM tmp
Zasega Anonimno
quelle
1
Es sind 6 Abfragen - vergessen Sie nicht, TEMPORARY
Zigulik zu TROPFEN
3

Versuche dies

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

Referenzen
PHP
MYSQL

diEcho
quelle
2
Dies würde höchstwahrscheinlich zu unerwünschtem Verhalten führen, wenn der Server nach der Benutzererstellung, jedoch vor der Profilerstellung abstürzt.
Vichle
@ Vichle was ist dann der beste Weg?
DiEcho
2
@ Vichle fügen Sie Ihre verdammte Transaktion zu diesem Code hinzu und stoppen Sie diesen Unsinn bereits
Ihr
3
@Konerak Ich führe seit über 10 Jahren PHP-Skripte aus. Keiner von ihnen hat die Angewohnheit, zwischen den Zeilen anzuhalten. Sie sollten besser daran denken, Ihre Serverqualität zu verbessern, damit sie nicht so oft abstürzt. Es ist Web, Alter. Es ist nicht Federal Reserve. An einer fehlerhaften Benutzerregistrierung von 100 000 000 erfolgreichen Benutzern ist nichts auszusetzen.
Ihr gesunder Menschenverstand
2
In diesem Beispiel haben Sie möglicherweise Recht. Mein Code wird jedoch immer für die Buchhaltung verwendet - der Gedanke, Geld in A zu entfernen und es nicht in B hinzuzufügen, solange es sollte, ist unerträglich. Außerdem, auch wenn es nur Web ist, ist eine Transaktion nicht so schwer / teuer? IMHO machen sie gute Gewohnheiten.
Konerak
3

Schauen Sie sich mysql_insert_id () an

hier die Dokumentation: http://in.php.net/manual/en/function.mysql-insert-id.php

Daniel Kutik
quelle
1
Diese Funktion ist der Teufel der Datenbankkonsistenz.
Vichle
vereinbart - mit einer Transaktion könnte die bessere Lösung sein
Daniel Kutik
@ Vichle: Ist das so? Ich benutze PHP nicht so oft, aber ich dachte, es wäre ihre Abkürzung, LAST_INSERT_ID()um nach dem Programmierer zu rufen ?
Konerak
1
Alle Abfragen sind Transaktionen. In den meisten Programmiersprachen werden standardmäßig Transaktionen automatisch festgeschrieben, da die meisten Transaktionen nur eine Abfrage sind. Die Funktion mysql_insert_id () ist für den Fall gedacht, dass Sie das automatische Festschreiben deaktiviert haben, wird jedoch sehr häufig von Personen verwendet, die mit dem Transaktionskonzept nicht vertraut sind.
Vichle
3
@dnl Sie können diese Funktion und eine Transaktion in Ordnung verwenden. Diese Transaktionsbemerkung ist für die Frage irrelevant.
Ihr gesunder Menschenverstand
1

Dies ist die Art und Weise, wie ich es für ein Uni-Projekt gemacht habe, funktioniert gut, wahrscheinlich nicht sicher

$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
quelle
-1

Nur eine Bemerkung zu Ihrem Spruch

Hallo, ich habe versucht, nach einer Möglichkeit zu suchen, Informationen in mehrere Tabellen in derselben Abfrage einzufügen

Essen Sie alle Ihre Mittagsgerichte gemischt mit Getränken in derselben Schüssel?
Ich nehme an - nein.

Hier gilt das gleiche.
Es gibt Dinge, die wir separat tun.
2 Einfügeabfragen sind 2 Einfügeabfragen. Es ist alles in Ordnung. Daran ist nichts auszusetzen. Keine Notwendigkeit, es in einem zu zerdrücken.
Gleiches gilt für select. Abfrage muss sinnvoll sein und ihren Job machen. Das sind die einzigen Gründe. Anzahl der Abfragen ist nicht.

Die Transaktionen können verwendet werden, aber für eine durchschnittliche Website ist dies keine große Sache. Wenn es einmal im Jahr (wenn überhaupt) passiert ist, dass eine Benutzerregistrierung fehlerhaft ist, können Sie dies zweifellos beheben.
Es gibt Hunderttausende von Websites, auf denen MySQL ohne Transaktionsunterstützungstreiber ausgeführt wird. Haben Sie von schrecklichen Katastrophen gehört, die diese Orte auseinander brechen? Ich auch nicht.

Und mysql_insert_id () hat nichts mit Transaktionen zu tun. Sie können in Ordnung in die Transaktion einbeziehen. es ist nur eine andere Sache. Jemand hat diese Frage aus dem Nichts gestellt.

Ihr gesunder Menschenverstand
quelle
Warum sollten Sie riskieren, solche Dinge reparieren zu müssen, wenn dies leicht zu vermeiden ist?
Vichle
@ Vichle meine Tabellen sind vom Typ Myisam. Für die Volltextsuche, Vermächtnis und Gewohnheit. Ich riskiere, yup. Was für eine schreckliche (theoretische) Gefahr erwartet mich.
Ihr gesunder Menschenverstand
6
Warum bist du so aggressiv? Ich sage nur, dass Transaktionen der richtige Weg sind. Sie tun es auf Ihre Weise, wenn Sie möchten. Tatsache ist jedoch, dass Transaktionen für diese Art von Situation erstellt wurden.
Vichle
@ Vichle yup, ich war ziemlich hart, ich entschuldige mich. Es war Ihre, die That function is the devil of database consistency.das gemacht hat, nicht Transaktionen. Ich sehe nichts schlechtes in Transaktionen.
Ihr gesunder Menschenverstand
Entschuldigung angenommen. Ich meinte nur, dass die Funktion oft von Leuten missbraucht wird, die sich der Existenz von Transaktionen nicht bewusst sind. Diese Leute sind auch in der PHP-Community ziemlich überrepräsentiert.
Vichle
-4

Für PDO können Sie dies tun

$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
quelle
Genau das suche ich.
Subroto Biswas