sql >> Database >  >> RDS >> Mysql

Door MySQL opgestelde verklaring - Hoe door te lussen

Zoals anderen al hebben gesuggereerd, vermijden we meestal een resultatenset doorlopen RBAR (rij voor pijnlijke rij) voornamelijk om prestatieredenen. We willen er gewoon niet de gewoonte van maken om door een resultatenset te bladeren. Maar dat is geen antwoord op de vraag die je hebt gesteld.

Om de door u gestelde vraag te beantwoorden, volgt hier een rudimentair voorbeeld van een in MySQL opgeslagen programma dat een CURSOR gebruikt om rijen die door een query worden geretourneerd, afzonderlijk te verwerken. MySQL ondersteunt geen anonieme blokken, dus de enige manier om dit te doen is in een in MySQL opgeslagen programma, zoals een 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 ;

Om de procedure uit te voeren:

CALL loop_through_var_list();

OPMERKINGEN:De syntaxis voor het verwerken van een CURSOR in MySQL is nogal anders dan bij andere databases.

Om de "looping" te krijgen, moeten we een LOOP ... END LOOP . gebruiken construeren.

Maar om te voorkomen dat die lus eeuwig blijft lopen, hebben we een LEAVE-statement nodig waarmee we de lus kunnen verlaten.

We gebruiken een voorwaardelijke test om te bepalen wanneer we moeten vertrekken. In dit voorbeeld willen we afsluiten nadat we de laatste rij hebben verwerkt.

De FETCH gaat een uitzondering genereren wanneer er geen rijen meer kunnen worden opgehaald.

We "vangen" die uitzondering in een CONTINUE HANDLER (om de een of andere mysterieuze reden moeten de "handlers" de laatste dingen declareren; MySQL geeft een fout als we iets proberen te declareren na een HANDLER (anders dan een andere HANDLER.)

Wanneer MySQL de uitzondering "geen rijen meer" genereert, wordt de handlercode geactiveerd. In dit voorbeeld stellen we alleen een variabele in (met de naam done ) naar een waarde.

Omdat het een "continue"-handler is, begint de verwerking weer bij de instructie waar de uitzondering is gegenereerd, in dit geval is dat de instructie die volgt op de FETCH. Het eerste wat we dus doen, is controleren of we "klaar" zijn of niet. Als we "klaar" zijn, verlaten we de lus en sluiten we de cursor.

Anders weten we dat we een id . hebben waarde van var_list opgeslagen in een procedurevariabele met de naam v_id . Dus nu kunnen we doen wat we willen. Het lijkt erop dat u wat SQL-tekst in een door de gebruiker gedefinieerde variabele wilt plaatsen (inclusief in de waarde van v_id in de SQL-tekst, vervolgens PREPARE, EXECUTE en DEALLOCATE PREPARE.

Zorg ervoor dat u de v_id . declareert variabele met het juiste datatype, dat overeenkomt met het datatype van de id kolom in var_list , ik ben er gewoon vanuit gegaan dat het een INT is.

Wanneer we het einde van de lus bereiken, "loopt" MySQL terug naar het begin van de lus, en daar gaan we weer.

In de hoofdtekst van de lus wilt u waarschijnlijk v_id CONCAT in de SQL-tekst die u wilt uitvoeren, invoeren. Het lijkt erop dat je de PREPARE, DEALLOCATE-voorbereiding al onder de knie hebt. Om te testen, wil je misschien een LIMIT-component toevoegen aan de SELECT in de cursordeclaratie, en dan een eenvoudige SELECT v_id doen; in de body, alleen om te controleren of de lus werkt, voordat je meer code toevoegt.

VERVOLG

Ik wilde een andere alternatieve benadering van de taak noemen, d.w.z. een reeks instructies uitvoeren op basis van een sjabloon, waarbij de waarden worden vervangen door een enkele SQL-select-instructie...

Als ik bijvoorbeeld dit sjabloon had:

SELECT * 
  INTO OUTFILE '/tmp/[email protected]'
  FIELDS TERMINATED BY ',' ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
FROM data 
WHERE id = @ID
ORDER BY 1 

en ik moest de exemplaren van @ID vervangen door een specifieke id-waarde uit een lijst die werd geretourneerd door een SELECT-instructie, b.v.

SELECT id
  FROM var_list
 WHERE id IS NOT NULL
 GROUP BY id

Ik zou waarschijnlijk geen MySQL-programma met een CURSOR-lus gebruiken, ik zou een andere benadering gebruiken.

Ik zou gebruik maken van een SELECT-statement om een ​​set SQL-statements te genereren die kunnen worden uitgevoerd. Ervan uitgaande dat id integer type is, zou ik waarschijnlijk zoiets als dit doen:

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

Voor elke waarde van id teruggestuurd van s , retourneert de instructie de tekst van een SQL SELECT-instructie die kan (en moet) worden uitgevoerd. Als ik dat in een tekstbestand zou vastleggen, zou ik een SQL-script krijgen dat ik zou kunnen uitvoeren.



  1. LINQ to Entities herkent de methode 'System.String ToString()' niet, en deze methode kan niet worden vertaald in een winkeluitdrukking

  2. Een query maken met group_concat in sql-server

  3. formaat interval met to_char

  4. Forceer exacte string MATCH voor PDO voorbereide statements