web-dev-qa-db-ja.com

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

テーブルAがあり、1つのプライマリキーIDがあります。

ここで、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にコピーします。

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

67
Raffael

ループの提案は、プロシージャタイプのソリューションの要求を暗示しているためです。これが私の.

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

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();

リクエスト例からコピーした「INSERT INTO」行で、好きなことを行うことができます。

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

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

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

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

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

クエリ対象のテーブルの変数と同じ型として使用する変数を必ず宣言してください。

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

102
Mr Purple

実際には、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

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

12
Raj More

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

4
Ron Weston

私がそのようなmysqlトリガーで使用したMr Purpleの例、

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
0
Erkan RUA