MySQL -> Loop through a table, menjalankan prosedur tersimpan pada setiap entri

9

Saya memiliki database dengan 'buku' (cerita pendek untuk anak-anak) dan akan sangat informatif untuk memiliki jumlah kata dari setiap kata dalam buku.

Saya menemukan cara untuk mendapatkan jumlah kata untuk setiap kata menggunakan:

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

Yang bekerja luar biasa untuk menghitung kata-kata. TAPI itu mengharuskan saya membaca setiap buku, dan mengeluarkan setiap kata, dan menjalankannya melalui fungsi itu (saya menyimpannya sebagai Prosedur yang Disimpan.)

Saya memiliki tabel yang berisi setiap kata, tanpa duplikat.

Pertanyaan saya: apakah ada cara saya bisa melakukan semacam "untuk setiap" loop pada tabel Words menggunakan prosedur tersimpan saya?

yaitu. lulus prosedur tersimpan ID buku dan kata dan catat hasilnya. Melakukan SETIAP kata, untuk SETIAP buku. Dengan demikian menghemat banyak waktu manual ... Apakah ini sesuatu yang seharusnya saya lakukan dari sisi DB? Haruskah saya mencobanya dengan PHP?

Jujur setiap masukan sangat dihargai!

Michael MacDonald
sumber
1
Anda bisa membuat tabel (semua) kata dengan menguraikan buku. Maka itu akan menjadi satu pilih buku bergabung dengan kata-kata. Tidak ada loop yang diperlukan di sana.
jkavalik
Beberapa tugas lebih baik dilakukan dalam bahasa pemrograman nyata, bukan SQL. Dalam PHP mungkin seperti ini count(explode(' ', $pageText))+1. Atau sesuatu yang lebih rumit untuk menangani banyak spasi di antara kata-kata, mungkin melibatkanpreg_replace('/\s+/', ' ', $pageText)
Rick James
Untuk Perl, mungkin sesingkat 1+split(/\s+/, $pageText). Angka 1 adalah karena jumlah adalah spasi, bukan kata-kata.
Rick James

Jawaban:

14

Buat prosedur kedua yang menggunakan dua kursor bersarang.

Kursor dalam prosedur tersimpan memungkinkan Anda untuk melakukan hal yang sangat non-SQL: beralih melalui hasil yang ditetapkan satu baris pada satu waktu, menempatkan nilai-nilai kolom yang dipilih ke dalam variabel dan melakukan sesuatu dengan mereka.

Mereka mudah disalahgunakan, karena SQL, lebih bersifat deklaratif daripada prosedural, biasanya tidak perlu "untuk setiap" operasi tipe, tetapi dalam kasus ini, sepertinya aplikasi yang valid.

Setelah Anda menguasai mereka, kursor mudah, tetapi mereka memang membutuhkan pendekatan terstruktur dalam kode pendukung mereka yang tidak selalu intuitif.

Baru-baru ini saya memberikan beberapa kode "boilerplate" yang cukup standar untuk bekerja dengan kursor untuk memanggil prosedur tersimpan dalam jawaban di Stack Overflow , dan saya akan meminjam sangat banyak dari jawaban itu, di bawah ini.


Menggunakan kursor memerlukan beberapa kode boilerplate standar untuk mengelilinginya.

Anda SELECTnilai-nilai yang ingin Anda sampaikan, dari mana pun Anda mendapatkannya (yang bisa berupa tabel sementara, tabel dasar, atau tampilan, dan dapat menyertakan panggilan ke fungsi yang disimpan) dan kemudian memanggil prosedur ada info Anda dengan nilai-nilai itu.

Berikut ini adalah contoh kode yang diperlukan secara sintaksis yang valid, dengan komentar untuk menjelaskan apa yang dilakukan setiap komponen.

Contoh ini menggunakan 2 kolom untuk meneruskan 2 nilai ke prosedur yang dipanggil.

Perhatikan bahwa ada peristiwa yang terjadi di sini dalam urutan tertentu karena suatu alasan. Variabel harus dideklarasikan terlebih dahulu, kursor harus dideklarasikan sebelum penangan berlanjut mereka, dan loop harus mengikuti semua hal itu.

Anda tidak dapat melakukan hal-hal yang tidak beres, jadi ketika Anda membuat satu kursor di dalam kursor yang lain, Anda harus mengatur ulang ruang lingkup prosedur dengan membuat kode tambahan di dalam BEGIN... ENDblok-blok di dalam badan prosedur; misalnya, jika Anda membutuhkan kursor kedua di dalam loop, Anda hanya akan mendeklarasikannya di dalam loop, di dalam BEGIN... ENDblok lain.

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 ;
Michael - sqlbot
sumber
Jawaban yang fantastis, sangat informatif! Belum turun, tapi dengan sumber daya yang disediakan saya yakin saya bisa membuat kursor bekerja! Terima kasih!
Michael MacDonald
ini hebat! menggunakan repeat / while menyebabkan proc saya memecat dua kali untuk record terakhir sehingga membutuhkan pemeriksaan tambahan tapi ini menyelesaikan masalah itu.
Nick M
tutup kursor1; hilang BUKA - TUTUP pergi bersama untuk kursor
Miss Felicia A Kovacs
2
Kursor @MissFeliciaAKovacs hanya bisa ada dalam lingkup blok BEGIN/ END, dan secara implisit ditutup ketika mereka keluar dari ruang lingkup ... sehingga menutup kursor tidak sepenuhnya diperlukan. Sebagai praktik, saya menganggap itu tidak dibutuhkan dan tidak memasukkannya, tetapi untuk kelengkapan, saya telah menambahkan CLOSEpernyataan itu ke jawabannya.
Michael - sqlbot