MySQL Inserisci in più tabelle? (Normalizzazione del database?)


136

Ho provato a cercare insertinformazioni su più tabelle nella stessa query, ma ho scoperto che è impossibile? Quindi voglio insertfarlo semplicemente usando più query cioè;

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

Ma come posso dare l'incremento automatico iddal usersal "manuale" useridper la profiletabella?


8
Vuoi conoscere le transazioni.
vichle

Risposte:


241

No, non puoi inserire più tabelle in un solo comando MySQL. Puoi comunque utilizzare le transazioni.

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()Dai un'occhiata al riutilizzo dei valori di incremento automatico.

Modifica: hai detto " Dopo tutto questo tempo cercando di capirlo, non funziona ancora. Non posso semplicemente mettere l'ID appena generato in un $ var e mettere quel $ var in tutti i comandi di MySQL? "

Permettetemi di elaborare: ci sono 3 possibili modi qui:

  1. Nel codice che vedi sopra. Questo fa tutto in MySQL e la LAST_INSERT_ID()seconda istruzione sarà automaticamente il valore della colonna di incremento automatico che è stata inserita nella prima istruzione.

    Sfortunatamente, quando la seconda istruzione stessa inserisce le righe in una tabella con una colonna di incremento automatico, LAST_INSERT_ID()verrà aggiornata a quella della tabella 2 e non alla tabella 1. Se in seguito sarà ancora necessario quella della tabella 1, dovremo memorizzarla in una variabile. Questo ci porta ai modi 2 e 3:

  2. Immagazzinerà LAST_INSERT_ID()una variabile MySQL:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
  3. Memorizzerà LAST_INSERT_ID()in una variabile php (o qualsiasi lingua che può connettersi a un database, a tua scelta):

    • INSERT ...
    • Usa la tua lingua per recuperare LAST_INSERT_ID(), eseguendo quell'istruzione letterale in MySQL o usando ad esempio php mysql_insert_id()che lo fa per te
    • INSERT [use your php variable here]

AVVERTIMENTO

Qualunque sia il modo di risolvere ciò che si sceglie, è necessario decidere cosa dovrebbe accadere in caso di interruzione dell'esecuzione tra le query (ad esempio, il crash del server di database). Se riesci a convivere con "alcuni hanno finito, altri no", non continuare a leggere.

Se tuttavia decidi "o tutte le query finiscono o nessuna finisce - non voglio righe in alcune tabelle ma nessuna riga corrispondente in altre, voglio sempre che le mie tabelle del database siano coerenti", devi racchiudere tutte le istruzioni in una transazione. Ecco perché ho usato il BEGINe COMMITqui.

Commenta di nuovo se hai bisogno di maggiori informazioni :)


3
E se volessi inserire più di 2 tabelle e voglio che tutti gli altri abbiano un ID univoco e l'id utente? È possibile?
Jay Wit,

È possibile inserire last_insert_id dalla tabella originale in una variabile MySQL e utilizzare quella variabile in tutte le altre tabelle. Il suggerimento di f00 per l'uso di una Stored Procedure ha ancora più senso se hai intenzione di manipolare molte tabelle in una volta.
Konerak,

3
@Jay Wit: ho aggiornato la risposta. 'Way 3' spiega che puoi davvero inserire l'ID in una variabile e riutilizzarlo in tutti i comandi MySQL, ma dovresti leggere le transazioni se vuoi che il tuo db sia coerente in caso di crash.
Konerak,

3
Certo, sono accettate le istruzioni MySQL, proprio come SELECT, UPDATE, INSERT e DELETE. Crea prima un piccolo testscript e, se tutto funziona bene, sei a posto.
Konerak,

2
@Konerak qualche consiglio su come usare queste multiple istruzioni SQL @mysql_variablesinsieme alle istruzioni preparate?
Fernando Silva,

17

abbastanza semplice se si utilizzano procedure memorizzate:

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

sceneggiatura completa:

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
Siamo spiacenti, ma questo è PHP? Non ho mai visto niente del genere. Sto usando PHP e MySQL, qualche consiglio?
Jay Wit,

1
sembra una lunga serie di dichiarazioni sql
Melbourne2991

1
@JayWit: No, queste sono tutte istruzioni SQL. Crea le procedure, quindi puoi usarle quando vuoi. Vedi dev.mysql.com/doc/refman/5.7/it/create-procedure.html
Pierre-Olivier Vares,

7

Cosa succederebbe se si volessero creare molti di questi record (per registrare 10 utenti, non solo uno)? Trovo la seguente soluzione (solo 5 query):

Passaggio I: creare una tabella temporanea per archiviare nuovi dati.

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

Quindi, riempire questa tabella con i valori.

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

Qui, invece di $ALL_VALinserire un elenco di valori: ('test1', 'test1', 'bio1', 'home1'), ..., ('testn', 'testn', 'bion', 'homen')

Passaggio II: inviare i dati alla tabella "utente".

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

Qui è possibile utilizzare "IGNORE", se si consente ad alcuni utenti di essere già dentro. Facoltativamente, è possibile utilizzare UPDATE in modo simile al passaggio III, prima di questo passaggio, per individuare gli utenti già presenti (e contrassegnarli nella tabella tmp). Qui supponiamo che quel nome utente sia dichiarato come PRIMARYnella tabella degli utenti.

Passaggio III: applicare l'aggiornamento per leggere tutti gli ID utenti dagli utenti alla tabella tmp. QUESTO È UN PASSO ESSENZIALE.

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

Passaggio IV: creare un'altra tabella, utilizzando l'id di lettura per gli utenti

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

1
sono 6 domande - non dimenticare di DROP TEMPORARY
Zigulik

3

prova questo

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

Riferimenti
PHP
MYSQL


2
Molto probabilmente ciò comporterebbe un comportamento indesiderato se il server si arresta in modo anomalo dopo la creazione dell'utente ma prima della creazione del profilo.
vichle

@Vichle allora qual è il modo migliore ??
Eco Eco

2
@vichle aggiungi la tua dannata transazione a questo codice e ferma già questa assurdità
Il tuo senso comune

3
@Konerak Sto eseguendo script PHP da oltre 10 anni. Nessuno dei due ha l'abitudine di fermarsi tra le linee. faresti meglio a pensare di migliorare la qualità del tuo server per renderlo un arresto anomalo spesso NON. È web, amico. non è la Federal Reserve. Nulla di sbagliato con una registrazione utente rotta di 100.000.000 di quelle riuscite.
Il tuo senso comune

2
In questo esempio, potresti avere ragione. Il mio codice viene sempre utilizzato per la contabilità, tuttavia, il pensiero di rimuovere denaro in A e di non aggiungerlo in B mentre dovrebbe, è intollerabile. Inoltre, anche se è solo web, una transazione non è così difficile / costosa? IMHO, fanno buone abitudini.
Konerak

3

dai un'occhiata a mysql_insert_id ()

qui la documentazione: http://in.php.net/manual/en/function.mysql-insert-id.php


1
Quella funzione è il diavolo della coerenza del database.
vichle

d'accordo - usare una transazione potrebbe essere la soluzione migliore
Daniel Kutik

@vichle: è così? Non uso php così spesso, ma ho pensato che fosse la loro scorciatoia per chiamare LAST_INSERT_ID()il programmatore?
Konerak

1
Tutte le query sono transazioni. L'impostazione predefinita nella maggior parte dei linguaggi di programmazione è il commit automatico delle transazioni, poiché la maggior parte delle transazioni sono solo una query. La funzione mysql_insert_id () è pensata per quando hai disattivato il commit automatico, ma molto spesso viene usata nel contesto sbagliato da persone che non hanno familiarità con il concetto di transazione.
vichle

3
@dnl puoi usare questa funzione E una transazione va bene. questa osservazione della transazione è irrilevante per la domanda.
Il tuo senso comune

1

Questo è il modo in cui l'ho fatto per un progetto uni, funziona bene, probabilmente non è sicuro

$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

Solo un'osservazione sul tuo detto

Ciao, ho provato a cercare un modo per inserire informazioni in più tabelle nella stessa query

Mangi tutti i tuoi piatti del pranzo mescolati con bevande nella stessa ciotola?
Suppongo che no.

Anch'io.
Ci sono cose che facciamo separatamente.
2 query di inserimento sono 2 query di inserimento. Va tutto bene. Niente di sbagliato. Non c'è bisogno di schiacciarlo in uno.
Lo stesso per selezionare. La query deve essere ragionevole e fare il suo lavoro. Questo è l'unico motivo. Il numero di query non lo è.

Per quanto riguarda le transazioni, puoi usarle, ma non è un grosso problema per il sito medio. Se è successo una volta all'anno (se mai) che una registrazione utente viene interrotta, sarai in grado di risolvere, senza dubbio.
ci sono centinaia di migliaia di siti che eseguono mysql senza driver di supporto delle transazioni. Hai mai sentito parlare di terribili catastrofi che hanno distrutto questi siti? Neanche io.

E mysql_insert_id () ha a che fare con le transazioni. puoi includere nella transazione tutto bene. sono solo questioni diverse. Qualcuno ha sollevato questa domanda dal nulla.


Perché dovresti rischiare di dover sistemare cose del genere quando è facilmente evitabile?
vichle

@vichle i miei tavoli sono di tipo myisam. Per motivi di ricerca full-text, eredità e abitudine. Sto rischiando, sì. Che terribile pericolo (in teoria) mi sta aspettando.
Il tuo senso comune

6
Perché sei così aggressivo? Sto solo affermando che le transazioni sono la strada da percorrere qui. Fai a modo tuo, se lo desideri, il fatto è che le transazioni sono state create per questo tipo di situazione.
vichle

@vichle yup, ero piuttosto duro, mi scuso. È stato tuo a That function is the devil of database consistency.farlo, non alle transazioni. Non vedo nulla di male nelle transazioni.
Il tuo senso comune

Scuse accettate. Tutto ciò che intendevo era che la funzione veniva spesso utilizzata in modo improprio da persone che non erano a conoscenza dell'esistenza di transazioni. Queste persone sono anche abbastanza sovrarappresentate nella comunità php.
vichle

-4

Per DOP Puoi farlo

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

Questo è esattamente quello che sto cercando.
Subroto Biswas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.