MySQL Birden fazla tabloya eklensin mi? (Veritabanı normalleştirme?)


136

insertAynı sorguda birden çok tabloda bilgi için bir yol aramayı denedim , ancak bunun imkansız olduğunu öğrendim? Yani ben insertsadece birden fazla sorgu yani;

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

Ama nasıl otomatik artış verebilir idgelen users"el" için useridiçin profilemasaya?


8
İşlemler hakkında bilgi edinmek istiyorsunuz.
vichle

Yanıtlar:


241

Hayır, bir MySQL komutunda birden çok tabloya ekleyemezsiniz. Ancak işlemleri kullanabilirsiniz.

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;

Otomatik LAST_INSERT_ID()enterrement değerlerini yeniden kullanmaya bakın .

Düzenleme: " Bu zaman geçtikten sonra anlamaya çalışıyorum, hala çalışmıyor. Dedim ki sadece oluşturulan kimliği bir $ var koymak ve bu $ var tüm MySQL komutları koymak? "

Açıklayayım: Burada 3 olası yol var:

  1. Yukarıda gördüğünüz kodda. Bu MySQL hepsini yapar LAST_INSERT_ID()ve ikinci deyim otomatik olarak ilk deyime eklenen autoincrement-sütunun değeri olacaktır.

    Ne yazık ki, ikinci ifadenin kendisi otomatik artış sütunu olan bir tabloya satır eklediğinde LAST_INSERT_ID(), tablo 1 değil tablo 2'ye güncellenecektir. Daha sonra hala tablo 1'inkine ihtiyacınız varsa, depolamamız gerekecek bir değişken içinde. Bu bizi 2. ve 3. yollara götürür:

  2. LAST_INSERT_ID()MySQL değişkenindeki stokları stoklayacak:

    INSERT ...
    SELECT LAST_INSERT_ID() INTO @mysql_variable_here;
    INSERT INTO table2 (@mysql_variable_here, ...);
    INSERT INTO table3 (@mysql_variable_here, ...);
    
  3. Stok Will LAST_INSERT_ID()php değişken (veya istediğiniz bir veritabanına bağlanmak herhangi bir dilde) 'de:

    • INSERT ...
    • LAST_INSERT_ID()MySQL'de bu değişmez ifadeyi yürüterek veya örneğin mysql_insert_id()sizin için bunu yapan php'leri kullanarak dilinizi kullanın
    • INSERT [use your php variable here]

UYARI

Bunu seçmenin yolu ne olursa olsun, sorgular arasında yürütme kesintiye uğrarsa ne olması gerektiğine karar vermelisiniz (örneğin, veritabanı-sunucu çöküyor). Eğer "bazıları bitti, diğerleri değil" ile yaşayabiliyorsanız, okumaya devam edin.

Ancak "ya tüm sorguları bitirmek ya da hiçbiri bitirmek karar verirseniz - bazı tablolarda satır istemiyorum ama diğerlerinde eşleşen satır yok, her zaman veritabanı tabloları tutarlı olmasını istiyorum", bir işlemde tüm ifadeleri sarmanız gerekir. Bu yüzden BEGINve COMMITburada kullandım.

Daha fazla bilgiye ihtiyacınız varsa tekrar yorum yapın :)


3
Ve 2'den fazla tabloya eklemek istersem ve diğerlerinin benzersiz bir kimliği ve kullanıcı kimliği olmasını istersem ne olur? Mümkün mü?
Jay Wit

Orijinal tablodan last_insert_id değerini bir MySQL değişkenine koyabilir ve bu değişkeni diğer tüm tablolarınızda kullanabilirsiniz. Bir seferde çok sayıda tabloyu işleyecekseniz, f00'ün Saklı Yordam kullanma önerisi daha da mantıklıdır.
Konerak

3
@Jay Wit: Cevabı güncelledim. 'Yol 3', kimliği gerçekten bir değişkene koyabileceğinizi ve tüm MySQL komutlarında yeniden kullanabileceğinizi açıklar, ancak bir çökme durumunda db'nizin tutarlı olmasını istiyorsanız işlemler hakkında okumalısınız.
Konerak

3
Elbette, bunlar SELECT, UPDATE, INSERT ve DELETE gibi MySQL ifadeleri olarak kabul edilir. Önce küçük bir test scripti yapın ve her şey yolunda giderse, başlayabilirsiniz.
Konerak

2
@Konerak, bu çoklu SQL ifadelerini @mysql_variableshazırlanan ifadelerle birlikte nasıl kullanacağınıza dair ipuçları var mı?
Fernando Silva

17

saklı yordamlar kullanıyorsanız oldukça basit:

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

tam komut dosyası:

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
Üzgünüm, ama bu PHP mi? Hiç böyle bir şey görmedim. PHP ve MySQL kullanıyorum, herhangi bir ipucu?
Jay Wit

1
sql ifadeleri uzun bir set gibi görünüyor
Melbourne2991

1
@JayWit: Hayır, bunların hepsi SQL ifadeleridir. Prosedürleri oluşturun, ardından istediğiniz zaman kullanabilirsiniz. Bkz. Dev.mysql.com/doc/refman/5.7/en/create-procedure.html
Pierre-Olivier Vares

7

Bu tür birçok kayıt oluşturmak istiyorsanız ne olur (sadece bir değil 10 kullanıcı kaydetmek için)? Aşağıdaki çözümü (sadece 5 sorgu) bulabilirsiniz:

Adım I: Yeni verileri saklamak için geçici tablo oluşturun.

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

Ardından, bu tabloyu değerlerle doldurun.

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

Burada, $ALL_VALdeğer listesi yerine : ('test1', 'test1', 'bio1', 'home1'), ..., ('testn', 'testn', 'bion', 'homen')

Adım II: Verileri 'kullanıcı' tablosuna gönderin.

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

Burada, bazı kullanıcıların zaten içeride olmasına izin verirseniz, "IGNORE" kullanılabilir. İsteğe bağlı olarak, bu kullanıcıların öncesinde kimin bulunduğunu bulmak için III. Adıma benzer bir UPDATE kullanabilirsiniz (ve bunları tmp tablosunda işaretleyin). Burada, kullanıcı adının PRIMARYkullanıcılar tablosunda olduğu gibi beyan edildiğini varsayıyoruz .

Adım III: Kullanıcılardan tmp tablosuna tüm kullanıcı kimliklerini okumak için güncellemeyi uygulayın. BU GEREKLİ ADIM.

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

IV. Adım: Kullanıcılar için okuma kimliğini kullanarak başka bir tablo oluşturun

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

1
6 sorgu - GEÇİCİ
DÜŞÜRMEYİ

3

bunu dene

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

Kaynaklar
PHP
MYSQL


2
Sunucu, kullanıcı oluşturulduktan sonra ancak profil oluşturmadan önce çökerse, bu durum büyük olasılıkla istenmeyen davranışlara neden olur.
vichle

@Vichle sonra en iyi yolu nedir ??
diEcho

2
@vichle lanet işleminizi bu koda ekleyin ve bu saçmalıklarınızı zaten durdurun
Ortak

3
@Konerak 10 yılı aşkın süredir PHP betikleri kullanıyorum. Her ikisinin de hatlar arasında durma alışkanlığı yoktur. sık sık çökmesini sağlamak için sunucu kalitenizi iyileştirmeyi düşünürseniz iyi olur. Web, ahbap. Federal Rezerv değil. 100 000 000 başarılı bir kırık kullanıcı kaydı ile yanlış bir şey yok.
Ortak Duygunuz

2
Bu örnekte haklı olabilirsiniz. Kodum her zaman muhasebe için kullanılır - A'daki parayı kaldırma ve gerektiğinde B'ye eklememe düşüncesi dayanılmazdır. Ayrıca, sadece web olsa bile, bir işlem o kadar zor / pahalı değil mi? IMHO, iyi alışkanlıklar yapıyorlar.
Konerak

3

mysql_insert_id () 'a bir göz atın

burada dokümantasyon: http://in.php.net/manual/en/function.mysql-insert-id.php


1
Bu işlev veritabanı tutarlılığının şeytanıdır.
vichle

kabul - bir işlem kullanmak daha iyi bir çözüm olabilir
Daniel Kutik

@vichle: öyle mi? Ben php bu kadar sık ​​kullanmıyorum, ama LAST_INSERT_ID()programcı çağırmak için onların kısayol olduğunu düşündüm ?
Konerak

1
Tüm sorgular işlemdir. Çoğu programlama dilinde varsayılan işlem otomatik işlemdir, çünkü çoğu işlem yalnızca bir sorgudur. Mysql_insert_id () işlevi, otomatik taahhüdü kapattığınız zaman içindir, ancak genellikle işlem kavramına aşina olmayan kişiler tarafından yanlış bağlamda kullanılır.
vichle

3
@dnl bu işlevi VE bir işlemi tamam kullanabilirsiniz. bu işlem açıklaması soru ile ilgisizdir.
Ortak Aklın

1

Bu bir uni proje için yaptım, iyi çalışıyor, güvenli değil tho

$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

Söylediklerine dair sadece bir açıklama

Merhaba, aynı sorguda birden çok tabloya bilgi eklemek için bir yol aramayı denedim

Tüm öğle yemeğinizi aynı kasede içeceklerle karıştırıyor musunuz?
Sanırım - hayır.

Burada aynı.
Ayrı ayrı yaptığımız şeyler var.
2 insert sorgusu 2 insert sorgusudur. Her şey yolunda. Bunda yanlış bir şey yok. Birinde ezmeye gerek yok.
Seçim için aynı. Sorgu mantıklı olmalı ve işi yapmalı. Tek sebep bu. Sorgu sayısı değil.

İşlemlere gelince - bunları kullanabilirsiniz, ancak ortalama web sitesi için önemli değil. Yılda bir kez (eğer varsa) bir kullanıcı kaydının bozulduysa, bunu düzeltebilirsiniz, şüphesiz.
hiçbir işlem destek sürücüsü olmadan mysql çalıştıran yüz binlerce site vardır. Bu siteleri birbirinden ayıran korkunç felaketler duydunuz mu? Ben de değil.

Ve mysql_insert_id () işlemlerle ilgisi yoktur. Tamamen işleme dahil edebilirsiniz. bu sadece farklı konular. Birisi bu soruyu hiçbir yerden ortaya koymadı.


Kolayca önlenebilir olduğunda neden böyle şeyleri düzeltmek zorunda kalmak isteyesiniz ki?
vichle

@vichle tablolarım myisam tipindedir. Tam metin arama, eski ve alışkanlık için. Risk alıyorum, evet. Ne kadar korkunç (teoride) beni bekliyor.
Ortak Duygunuz

6
Neden bu kadar agresifsin? İşlemlerin buraya gitmenin bir yolu olduğunu söylüyorum. İsterseniz bunu yaparsınız, gerçek şu ki bu tür bir durum için işlemler yaratılmıştır.
vichle

@ vichle yup, oldukça serttim, özür dilerim. That function is the devil of database consistency.İşlemleri değil, bunu sizin yaptı. İşlemlerde kötü bir şey görmüyorum.
Ortak Duygunuz

Özrün kabul edildi. Demek istediğim, işlevin genellikle işlemlerin varlığından haberdar olmayan insanlar tarafından kötüye kullanılmasıydı. Bu insanlar da php toplumda oldukça fazla temsil edilmektedir.
vichle

-4

PDO için Bunu yapabilirsiniz

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

Tam da aradığım şey bu.
Subroto Biswas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.