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

しかし、テーブルの自動インクリメントidusers「手動」にuseridするにはどうすればよいprofileですか?


8
トランザクションについて学びたい。
vichle

回答:


241

いいえ、1つの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()自動インクリメント値を再利用するために見てください。

編集:「これを解決しようとしても、それでも機能しません。生成されたばかりのIDを$ varに入れて、その$ varをすべてのMySQLコマンドに入れることはできませんか?

詳しく説明します。ここには3つの方法があります。

  1. 上記のコードで。これはすべてMySQLで行わ LAST_INSERT_ID()れ、2番目のステートメントのは、最初のステートメントに挿入されたautoincrement-columnの値になります。

    残念ながら、2番目のステートメント自体が自動インクリメント列のあるテーブルに行を挿入LAST_INSERT_ID()すると、はテーブル1ではなくテーブル2の行に更新されます。それでもテーブル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]

警告

これを解決する方法が何であれ、クエリ間で実行が中断された場合(データベースサーバーがクラッシュした場合など)に何を行うか決定する必要があります。あなたが「いくつかは終わった、他は終わっていない」と一緒に暮らせるなら、読み進めないでください。

ただし、「すべてのクエリが終了するか、いずれも終了しない-一部のテーブルには行がなく、他のテーブルには一致する行がないようにしたい場合は、常にデータベーステーブルに一貫性を持たせたい」と決定した場合、すべてのステートメントをトランザクションでラップする必要があります。そのためBEGINCOMMITこことここを使用しました。

さらに情報が必要な場合は、もう一度コメントしてください:)


3
また、3つ以上のテーブルに挿入し、他のすべてのテーブルに一意のIDとユーザーIDを割り当てたい場合はどうなりますか?それは可能ですか?
ジェイウィット

元のテーブルのlast_insert_idをMySQL変数に入れ、その変数を他のすべてのテーブルで使用できます。多数のテーブルを一度に操作する場合は、ストアドプロシージャを使用するためのf00の提案はさらに理にかなっています。
Konerak

3
@ジェイウィット:私は答えを更新しました。「方法3」では、IDを変数に入れてすべてのMySQLコマンドで再利用できることを説明していますが、クラッシュした場合にデータベースの整合性を保つには、トランザクションについて読む必要があります。
Konerak

3
もちろん、それらはSELECT、UPDATE、INSERT、DELETEと同様に、MySQLステートメントを受け入れます。最初に小さなテストスクリプトを作成します。すべてが正常に機能する場合は、問題ありません。
Konerak

2
@Konerakこれらの複数のSQLステートメント@mysql_variablesを準備済みステートメントと組み合わせて使用する方法に関するヒントはありますか?
Fernando Silva

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を使用していますが、何かヒントはありますか?
ジェイウィット

1
SQLステートメントの長いセットのように見える
Melbourne2991

1
@JayWit:いいえ、これらはすべてSQLステートメントです。プロシージャを作成すれば、いつでも使用できます。dev.mysql.com/doc/refman/5.7/en/create-procedure.htmlを
Pierre-Olivier

7

このようなレコードを多数作成したい場合(1人だけでなく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」を使用できます。オプションで、このステップの前に、ステップIIIと同様のUPDATEを使用して、すでに内部にいるユーザーを見つける(およびtmpテーブルでマークする)ことができます。ここでは、そのユーザー名がPRIMARYusersテーブルのように宣言されていると仮定します。

ステップIII:更新を適用して、すべてのユーザーIDをユーザーからtmpテーブルに読み取ります。これは必須のステップです。

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

ステップIV:ユーザーの読み取りIDを使用して別のテーブルを作成する

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

1
それは6つのクエリです
-DROP

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は、このコードにdarnトランザクションを追加し、そのナンセンスをすでに停止しています
常識

3
@Konerak PHPスクリプトを10年以上実行しています。どちらも行間で止まる癖はありません。頻繁にクラッシュしないようにサーバーの品質を向上させることをお勧めします。それはウェブです。連邦準備制度ではありません。100 000 000の成功したユーザー登録の1つの壊れたユーザー登録に問題はありません。
常識

2
この例では、あなたは正しいかもしれません。私のコードは常に会計処理に使用されますが、Aでお金を削除し、必要なときにBで追加しないという考えは耐えられません。さらに、それがWebだけの場合でも、トランザクションはそれほど難しくなく、高価でもありませんか?私見、彼らは良い習慣を作る。
Konerak

3

mysql_insert_id()を見てください

ここにドキュメント:http : //in.php.net/manual/en/function.mysql-insert-id.php


1
その機能はデータベースの一貫性の悪魔です。
vichle

合意-トランザクションを使用する方が良い解決策かもしれない
Daniel Kutik

@vichle:そうですか?私は頻繁にphpを使用していませんがLAST_INSERT_ID()、プログラマーを呼び出すための彼らの近道だと思いましたか?
Konerak

1
すべてのクエリはトランザクションです。ほとんどのトランザクションは1つのクエリにすぎないため、ほとんどのプログラミング言語のデフォルトでは、トランザクションが自動コミットされます。mysql_insert_id()関数は、自動コミットをオフにした場合を対象としていますが、トランザクションの概念に精通していない人が間違ったコンテキストで使用することがよくあります。
vichle

3
@dnlでは、この関数とトランザクションを使用できます。この取引の発言は質問には関係ありません。
常識

1

これは私がユニプロジェクトのためにそれをした方法であり、うまく機能し、おそらく安全ではありません

$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つの挿入クエリです。大丈夫。何も問題はありません。つぶす必要はありません。
選択についても同じです。クエリは賢明でなければならず、それが仕事です。それが唯一の理由です。クエリの数ではありません。

トランザクションに関しては-あなたはそれらを使うかもしれませんが、平均的なウェブサイトにとってそれはそれほど大したことではありません。1年に1回(あるとしても)1人のユーザー登録が壊れた場合は、間違いなく修正できます。
トランザクションサポートドライバーなしでmysqlを実行している数十万のサイトがあります。これらのサイトを破壊する恐ろしい災害について聞いたことがありますか?私もダメ。

そして、mysql_insert_id()はトランザクションと関係があります。取引に含めても大丈夫です。それはただ別の問題です。誰かがこの質問をどこからともなく提起しました。


簡単に回避できるのに、なぜそのようなものを修正しなければならないリスクを冒したいのですか
vichle

@vichleテーブルはmyisamタイプです。全文検索、レガシー、習慣のために。私は危険だ、うん。なんとひどい(理論的には)危険が私を待っています。
常識

6
なぜそんなにアグレッシブなのですか?私はトランザクションがここに行く方法であると述べています。あなたが望むなら、あなたはあなたのやり方でそれを行います、実際には、トランザクションはこの種の状況のた​​めに作成されたということです。
vichle

@vichleうん、私はかなり過酷だった、私はお詫び申し上げます。それ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 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.