Som andre allerede har foreslået, undgår vi typisk sløjfe gennem et resultatsæt RBAR (række for pinefulde række) primært af præstationsmæssige årsager. Vi ønsker bare ikke at vænne os til at gennemgå et resultatsæt. Men det besvarer ikke det spørgsmål, du stillede.
For at besvare det spørgsmål, du stillede, er her et rudimentært eksempel på et MySQL-lagret program, der bruger en CURSOR til individuelt at behandle rækker, der returneres af en forespørgsel. MySQL understøtter ikke anonyme blokeringer, så den eneste måde at gøre dette på er i et MySQL-lagret program, f.eks. en PROCEDURE
DELIMITER $$
CREATE PROCEDURE loop_through_var_list
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE v_id INT DEFAULT NULL;
DECLARE csr_var_list CURSOR FOR SELECT id FROM var_list ORDER BY id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN csr_var_list;
get_id: LOOP
FETCH csr_var_list INTO v_id;
IF done = 1 THEN
LEAVE get_id;
END IF;
-- at this point, we have an id value in v_id, so we can do whatever
SET @s1 = CONCAT('SELECT ... WHERE id =''', v_id, ''' ...');
END LOOP get_id;
CLOSE csr_var_list;
END$$
DELIMITER ;
Sådan udføres proceduren:
CALL loop_through_var_list();
BEMÆRKNINGER:Syntaksen til at behandle en CURSOR i MySQL er en del anderledes end andre databaser.
For at få "looping" skal vi bruge en LOOP ... END LOOP
konstruere.
Men for at forhindre den løkke i at køre for evigt, har vi brug for en LEAVE-sætning, der giver os mulighed for at forlade løkken.
Vi bruger en betinget test til at bestemme, hvornår vi skal rejse. I dette eksempel ønsker vi at afslutte, når vi er færdige med at behandle den sidste række.
FETCH
kommer til at kaste en undtagelse, når der ikke er flere rækker, der skal hentes.
Vi "fanger" den undtagelse i en CONTINUE HANDLER (af en eller anden mystisk årsag skal "handlerne" til de sidste ting erklæret; MySQL kaster en fejl, hvis vi forsøger at erklære noget efter en HANDLER (andre end en anden HANDLER.)
Når MySQL kaster undtagelsen "ikke flere rækker", udløser det handlerkoden. I dette eksempel indstiller vi blot en variabel (ved navn done
) til en værdi.
Da det er en "fortsæt"-handler, starter behandlingen igen ved sætningen, hvor undtagelsen blev kastet, i dette tilfælde vil det være sætningen efter FETCH. Så den første ting vi gør er at tjekke om vi er "færdige" eller ej. Hvis vi er "færdige", så forlader vi løkken og lukker markøren.
Ellers ved vi, at vi har et id
værdi fra var_list
gemt i en procedurevariabel ved navn v_id
. Så nu kan vi gøre, hvad vi vil. Det ser ud til, at du vil indsætte noget SQL-tekst i en brugerdefineret variabel (inklusive værdien af v_id i SQL-teksten, derefter PREPARE, EXECUTE og DEALLOCATE PREPARE.
Sørg for at angive v_id
variabel med den relevante datatype, der matcher datatypen for id
kolonne i var_list
, jeg har lige antaget, at det er en INT.
Når vi når slutningen af løkken, "løkker" MySQL tilbage til begyndelsen af løkken, og så går vi igen.
I løkkens brødtekst vil du sandsynligvis CONCAT v_id ind i den SQL-tekst, du vil udføre. Det ser ud til, at du allerede har styr på forberedelsen, AFDELING. Til test kan du tilføje en LIMIT-sætning på SELECT i markørerklæringen og derefter lave en simpel SELECT v_id; i kroppen, bare for at bekræfte, at løkken fungerer, før du tilføjer mere kode.
OPFØLGNING
Jeg ønskede at nævne en anden alternativ tilgang til opgaven, dvs. at køre en række sætninger baseret på en skabelon, der erstatter værdier fra en enkelt SQL select-sætning...
For eksempel, hvis jeg havde denne skabelon:
SELECT *
INTO OUTFILE '/tmp/[email protected]'
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM data
WHERE id = @ID
ORDER BY 1
og jeg havde brug for at erstatte forekomsterne af @ID med en specifik id-værdi fra en liste returneret fra en SELECT-sætning, f.eks.
SELECT id
FROM var_list
WHERE id IS NOT NULL
GROUP BY id
Jeg ville sandsynligvis ikke bruge et MySQL-lagret program med en CURSOR-løkke, jeg ville bruge en anden tilgang.
Jeg ville gøre brug af en SELECT-sætning til at generere et sæt SQL-sætninger, som kunne udføres. Forudsat id
er heltalstype, ville jeg sandsynligvis gøre noget som dette:
SELECT CONCAT(' SELECT *
INTO OUTFILE ''/tmp/orders_',s.id,'.csv''
FIELDS TERMINATED BY '','' ENCLOSED BY ''"''
LINES TERMINATED BY ''\n''
FROM data
WHERE id = ',s.id,'
ORDER BY 1;') AS `stmt`
FROM ( SELECT v.id
FROM var_list v
WHERE v.id IS NOT NULL
GROUP BY v.id
) s
ORDER BY s.id
For hver værdi af id
returneret fra s
, returnerer sætningen teksten af en SQL SELECT-sætning, der kunne (og skal) udføres. At fange det i en tekstfil ville give mig et SQL-script, jeg kunne køre.