私は「本」(子供向けの短編小説)のデータベースを持っています。本の各単語の単語数を知ることは非常に有益です。
私は各単語の単語数を取得する方法を理解しました:
SELECT SUM
(
ROUND
(
(LENGTH(pageText) - LENGTH (REPLACE (pageText, "Word", "")))
/LENGTH("Word")
)
) FROM pages WHERE bookID = id;
これは単語を数えるのに素晴らしい働きをします。しかし、それは私が各本を読み、各Wordを取り出し、その関数を実行する必要があります(私はそれをストアドプロシージャとして保存しました)。
各Wordを含むテーブルがあり、重複はありません。
私の質問:ストアドプロシージャを使用して、Wordsテーブルで何らかの "for each"ループを実行する方法はありますか?
すなわち。ストアドプロシージャにブックIDとWordを渡し、結果を記録します。すべての本のために、すべての単語を行う。したがって、多くの手動時間を節約できます...これは、DB側から実行する必要があることですか?代わりにPHPで試してみる必要がありますか?
正直なところ、どんな入力でも大歓迎です!
2つのネストされたカーソルを使用する2番目のプロシージャを作成します。
カーソル ストアドプロシージャを使用すると、SQLに似ていない処理を実行できます。一度に1行ずつ結果セットを反復処理し、選択した列の値を変数に入れて、処理を行います。
SQLは手続き型ではなく宣言型であるため、通常は「for each」タイプの操作を必要としないため、簡単に誤用されますが、この場合は有効なアプリケーションのように見えます。
それらのコツをつかめば、カーソルは簡単ですが、カーソルをサポートするコードに構造化されたアプローチが必要であり、常に直感的であるとは限りません。
私は最近、カーソルを操作してストアドプロシージャを呼び出すためのかなり標準的な「ボイラープレート」コードをいくつか提供しました Stack Overflowの回答で 、そして以下の回答から非常に多く借ります。
カーソルを使用するには、カーソルを囲むための標準的な定型コードが必要です。
渡したい値を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 ;