テーブルのすべての行をループするにはどうすればよいですか?(MySQL)


90

テーブルAがあり、主キーIDが1つあります。

ここで、Aのすべての行を調べます。

Aの各レコードに「」のようなものを見つけましたが、これはMySQLでの方法ではないようです。

これは、フィールドを取得して変換し、別のテーブルに挿入してから、行のフィールドの一部を更新する行ごとのものです。select部分とinsertを1つのステートメントに入れることはできますが、そこで更新を取得する方法もわかりません。だから私はループしたい。そして練習のために、MySQL以外のものは使いたくありません。

編集

例をいただければ幸いです。

そして、手続きに入れる必要のない解決策。

編集2

このシナリオを考えてみてください。

表AおよびB、それぞれフィールドIDおよびVAL。

これが私がやりたいことの擬似コードです。

for(each row in A as rowA)
{
  insert into B(ID, VAL) values(rowA[ID], rowA[VAL]);
}

基本的に、ループを使用してAのコンテンツをBにコピーします。

(これは単純化された例です。もちろん、これにはループを使用しません。)}


こんにちはRafeel私にとっては次のようなエラーが発生します:My SQL> for(wp_mobiune_ecommune_user as x){insert into wp_mobiune_ecommune_user(gender_new)values(x [gender])}; RROR 1064(42000):SQL構文にエラーがあります。MySQLサーバーのバージョンに対応するマニュアルで、near'for(wp_mobiune_ecommune_user as x){の1行目でwp_mobiune_ecommune 'に挿入する正しい構文を確認してください
dinesh kandpal 2017

回答:


137

ループの提案は、プロシージャタイプのソリューションの要求を意味するためです。これが私のものです。

テーブルから取得した単一のレコードで機能するクエリは、次のようにテーブルの各行を実行するようにプロシージャでラップできます。

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

次に、例に従って手順を示します(わかりやすくするためにtable_Aとtable_Bを使用)

CREATE PROCEDURE ROWPERROW()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM table_A INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO table_B(ID, VAL) SELECT (ID, VAL) FROM table_A LIMIT i,1;
  SET i = i + 1;
END WHILE;
End;
;;

次に、区切り文字をリセットすることを忘れないでください

DELIMITER ;

そして、新しい手順を実行します

CALL ROWPERROW();

サンプルリクエストからコピーした「INSERTINTO」行で、好きなことを行うことができます。

ここで使用されている「INSERTINTO」行は、質問の行を反映していることに注意してください。この回答へのコメントによると、実行しているSQLのバージョンに対してクエリが構文的に正しいことを確認する必要があります。

IDフィールドがインクリメントされ、1から始まる単純なケースでは、例の行は次のようになります。

INSERT INTO table_B(ID, VAL) VALUES(ID, VAL) FROM table_A WHERE ID=i;

「SELECTCOUNT」行を次のように置き換えます

SET n=10;

table_Aの最初の10レコードでのみクエリをテストできます。

最後に一つだけ。このプロセスは、さまざまなテーブル間でネストするのも非常に簡単で、親テーブルの各行から新しいテーブルにさまざまな数のレコードを動的に挿入する1つのテーブルでプロセスを実行できる唯一の方法でした。

より速く実行する必要がある場合は、必ずセットベースにするようにしてください。そうでない場合は、これで問題ありません。上記をカーソル形式で書き直すこともできますが、パフォーマンスが向上しない場合があります。例えば:

DROP PROCEDURE IF EXISTS cursor_ROWPERROW;
DELIMITER ;;

CREATE PROCEDURE cursor_ROWPERROW()
BEGIN
  DECLARE cursor_ID INT;
  DECLARE cursor_VAL VARCHAR;
  DECLARE done INT DEFAULT FALSE;
  DECLARE cursor_i CURSOR FOR SELECT ID,VAL FROM table_A;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
  OPEN cursor_i;
  read_loop: LOOP
    FETCH cursor_i INTO cursor_ID, cursor_VAL;
    IF done THEN
      LEAVE read_loop;
    END IF;
    INSERT INTO table_B(ID, VAL) VALUES(cursor_ID, cursor_VAL);
  END LOOP;
  CLOSE cursor_i;
END;
;;

使用する変数は、クエリされたテーブルの変数と同じ型として宣言することを忘れないでください。

可能な場合はセットベースのクエリを使用し、必要な場合にのみ単純なループまたはカーソルを使用することをお勧めします。


2
INSERT INTO table_B(ID、VAL)VALUES(ID、VAL)FROM table_A LIMIT i、1; 構文エラーが発生します。
ジョナサン

1
'DECLARE done INT DEFAULTFALSE;'が欠落しているようです。cursor_i宣言後
ErikL 2014年

1
プロパティはどこでtrueに設定されますか、それを配置する必要がありますか、それともmysqlカーソルによって自動的に使用される予約語ですか?
IgniteCoders 2015年

1
INSERT INTO呼び出しは、SQL 5.5の時点で構文エラーをスローします。正しい呼び出しは、INSERT INTO table_B(ID、VAL)SELECT(ID、VAL)FROM table_A WHERE ID = iです。
アンバー2016

これは、寿命を取るので、リミットと増分を変更することにより、1000年にバッチサイズを大きくしようとします。LIMIT i,1000;そしてset i = i + 1000
アランディープ

16

実際には、2つのクエリ(基本的な挿入)を含むセットベースのソリューションを使用する必要があります。

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT id, column1, column2 FROM TableA

UPDATE TableA SET column1 = column2 * column3

そしてあなたの変容のために:

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT 
    id, 
    column1 * column4 * 100, 
    (column2 / column12) 
FROM TableA

UPDATE TableA SET column1 = column2 * column3

変換がそれよりも複雑で、複数のテーブルが含まれる場合は、詳細を記載した別の質問を投稿してください。


例では+1。これが私の状況に当てはまるかどうかは疑わしいですが、その後の更新は挿入に関連しており、特に選択した行が特定の挿入を引き起こします。これが、ループを提案した理由です。これにより、各行に対して個別に選択/挿入/更新を実行できます。
ラファエル2011

2
@ Raffael1984:質問を編集して、「特定の挿入を引き起こす特定の行」の条件を追加してください。それを支援できます。あなたは本当にカーソル/ループルートに行きたくありません-それは非常に非効率的です。
Raj More

まあ...あなたは私がセットベースのクエリの方法を行って幸せになることを知っています!しかし、私の質問は学術的な関心が高いため、効率はここでは動機付けではありません。テーブルは手でできるほど小さいです。ループバージョンをいただければ幸いです。誰かがセットベースのスタイルで私を助けてくれるように、私はその問題をもう一度投稿して詳細を提供するかもしれません。
ラファエル2011

@RajMoreに同意します。カーソル/ループは非効率的です。以下は、参照用のカーソルパフォーマンスに関する2つのリンクです。1。rpbouman.blogspot.tw / 2006/09 / refactoring -
Browny Lin

@RajMoreすることができますが、私の中で私を助け質問
Nurav

4

カーソルはここでのオプションですが、クエリエンジンを最大限に活用しないことが多いため、一般的には嫌われています。'SET Based Queries'を調査して、CURSORを使用せずに実行したいことを達成できるかどうかを確認することを検討してください。


セットベースのクエリに関する多くの情報が見つかりません。しかし、私はあなたが一種のステートメントへの選択を意味すると思います。問題は、更新も実行する必要があることです。
ラファエル2011

0

私がmysqlトリガーで使用したパープル氏の例は、

begin
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
Select COUNT(*) from user where deleted_at is null INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO user_notification(notification_id,status,userId)values(new.notification_id,1,(Select userId FROM user LIMIT i,1)) ;
  SET i = i + 1;
END WHILE;
end

2
ソリューションがどのように機能するかについて、いくつかの情報を追加していただけますか?
TheTanic 2018

-26
    Use this:

    $stmt = $user->runQuery("SELECT * FROM tbl WHERE ID=:id");
    $stmt->bindparam(":id",$id);
    $stmt->execute();

        $stmt->bindColumn("a_b",$xx);
        $stmt->bindColumn("c_d",$yy);


    while($rows = $stmt->fetch(PDO::FETCH_BOUND))
    {
        //---insert into new tble
    }   
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.