MySQL->テーブルをループし、各エントリでストアドプロシージャを実行します


9

私は「本」(子供向けの短編小説)のデータベースを持っています。本の各単語の単語数を知ることは非常に有益です。

私は各単語の単語数を取得する方法を理解しました:

SELECT SUM
( 
    ROUND
    ( 
        (LENGTH(pageText) - LENGTH (REPLACE (pageText, "Word", "")))
        /LENGTH("Word")
    )
) FROM pages WHERE bookID = id;

これは単語を数えるのに素晴らしい働きをします。しかし、それは私が各本を読み、各単語を取り出し、その関数を実行する必要があります(私はそれをストアドプロシージャとして保存しています)。

重複のない、各単語を含むテーブルがあります。

私の質問:ストアドプロシージャを使用してWordsテーブルで何らかの "for each"ループを実行する方法はありますか?

すなわち。ストアドプロシージャに書籍IDと単語を渡して、結果を記録します。すべての本のために、すべての言葉を行う。したがって、多くの手動時間を節約できます...これは私がDB側からでも行う必要があることですか?代わりにPHPで試す必要がありますか?

正直なところ、どんな入力でも大歓迎です!


1
本を解析することで、(すべての)単語の表を作成できます。それから、それは本を言葉に結びつける一つの選択になるでしょう。そこではループは必要ありません。
jkavalik 2016年

一部のタスクは、SQLではなく実際のプログラミング言語で実行する方が適切です。PHPでは、次のようになりcount(explode(' ', $pageText))+1ます。または、単語間の複数のスペースを処理するためのより複雑なもの(おそらく関与する)preg_replace('/\s+/', ' ', $pageText)
リックジェームズ

Perlの場合、と同じくらい短い場合があります1+split(/\s+/, $pageText)。1は、カウントが単語ではなくスペースであるためです。
リックジェームズ

回答:


14

2つのネストされたカーソルを使用する2番目のプロシージャを作成します。

ストアドプロシージャのカーソルを使用すると、SQLに似ていない処理を実行できます。一度に1行ずつ結果セットを反復処理し、選択した列の値を変数に入れて、処理を行います。

SQLは手続き型ではなく宣言型であるため、通常は「for each」タイプの操作を必要としないため、誤用されやすいですが、この場合は有効なアプリケーションのように見えます。

それらのコツをつかめば、カーソルは簡単ですが、カーソルをサポートするコードに構造化されたアプローチが必要であり、常に直感的であるとは限りません。

最近、カーソルを操作してスタックオーバーフローの回答でストアドプロシージャを呼び出すための、かなり標準的な「ボイラープレート」コードをいくつか提供しました。以下では、その回答から大いに借ります。


カーソルを使用するには、カーソルを囲むための標準的な定型コードが必要です。

もしSELECTあなたが(一時テーブル、ベーステーブル、またはビューすることができ、保存された関数の呼び出しを含むことができる)、その後、それらの値を使用してexistinfプロシージャを呼び出すそれらを取得しているどこから、渡したい値。

以下は、必要なコードの構文的に有効な例であり、各コンポーネントの動作を説明するコメントが付いています。

この例では、2つの列を使用して、呼び出されたプロシージャに2つの値を渡します。

ここで発生するイベントは、理由により特定の順序になっていることに注意してください。変数は最初に宣言する必要があり、カーソルは継続ハンドラの前に宣言する必要があり、ループはこれらすべてのものに従う必要があります。

順不同で行うことはできないため、あるカーソルを別のカーソルの内側にネストする場合は、プロシージャ本体内のBEGIN... ENDブロック内に追加のコードをネストして、プロシージャスコープをリセットする必要があります。たとえば、ループ内に2番目のカーソルが必要な場合は、ループ内、別のBEGIN... ENDブロック内で宣言するだけです。

DELIMITER $$

DROP PROCEDURE IF EXISTS `my_proc` $$
CREATE PROCEDURE `my_proc`(arg1 INT) -- 1 input argument; you might need more or fewer
BEGIN

-- declare the program variables where we'll hold the values we're sending into the procedure;
-- declare as many of them as there are input arguments to the second procedure,
-- with appropriate data types.

DECLARE val1 INT DEFAULT NULL;
DECLARE val2 INT DEFAULT NULL;

-- we need a boolean variable to tell us when the cursor is out of data

DECLARE done TINYINT DEFAULT FALSE;

-- declare a cursor to select the desired columns from the desired source table1
-- the input argument (which you might or might not need) is used in this example for row selection

DECLARE cursor1 -- cursor1 is an arbitrary label, an identifier for the cursor
 CURSOR FOR
 SELECT t1.c1, 
        t1.c2
   FROM table1 t1
  WHERE c3 = arg1; 

-- this fancy spacing is of course not required; all of this could go on the same line.

-- a cursor that runs out of data throws an exception; we need to catch this.
-- when the NOT FOUND condition fires, "done" -- which defaults to FALSE -- will be set to true,
-- and since this is a CONTINUE handler, execution continues with the next statement.   

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

-- open the cursor

OPEN cursor1;

my_loop: -- loops have to have an arbitrary label; it's used to leave the loop
LOOP

  -- read the values from the next row that is available in the cursor

  FETCH NEXT FROM cursor1 INTO val1, val2;

  IF done THEN -- this will be true when we are out of rows to read, so we go to the statement after END LOOP.
    LEAVE my_loop; 
  ELSE -- val1 and val2 will be the next values from c1 and c2 in table t1, 
       -- so now we call the procedure with them for this "row"
    CALL the_other_procedure(val1,val2);
    -- maybe do more stuff here
  END IF;
END LOOP;

-- execution continues here when LEAVE my_loop is encountered;
-- you might have more things you want to do here

-- the cursor is implicitly closed when it goes out of scope, or can be explicitly closed if desired

CLOSE cursor1;

END $$

DELIMITER ;

素晴らしい答え、非常に有益です!まだ完全にはダウンしていませんが、提供されたリソースを使用すると、カーソルを確実に機能させることができます!ありがとうございました!
マイケルマクドナルド

これは素晴らしかったです!repeat / whileを使用すると、最後のレコードに対してprocが2回起動されるため、追加のチェックが必要になりますが、これによりその問題が解決します。
Nick M

cursor1を閉じます。OPENが欠落しています-カーソルのクローズが一緒に行われています
Miss Felicia A Kovacs

2
@MissFeliciaAKovacsカーソルはBEGIN/ ENDブロックのスコープ内にのみ存在でき、スコープから外れると暗黙的に閉じられます...したがって、カーソルを閉じる必要はありません。慣例として、それは不要であると考えて、含めませんが、完全にCLOSEするために、答えにステートメントを追加しました。
マイケル-sqlbot 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.