Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Vorbereitete MySQL-Anweisung – Durchschleifen

Wie andere bereits angedeutet haben, vermeiden wir es normalerweise Durchlaufen einer Ergebnismenge RBAR (Reihe für quälende Reihe) hauptsächlich aus Performance-Gründen. Wir wollen uns nur nicht angewöhnen, eine Ergebnismenge zu durchlaufen. Aber das beantwortet nicht die Frage, die Sie gestellt haben.

Um die von Ihnen gestellte Frage zu beantworten, finden Sie hier ein rudimentäres Beispiel für ein gespeichertes MySQL-Programm, das einen CURSOR verwendet, um die von einer Abfrage zurückgegebenen Zeilen einzeln zu verarbeiten. MySQL unterstützt keine anonymen Blöcke, also ist die einzige Möglichkeit, dies zu tun, in einem gespeicherten MySQL-Programm wie einer 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 ;

So führen Sie die Prozedur aus:

CALL loop_through_var_list();

HINWEISE:Die Syntax zum Verarbeiten eines CURSORs in MySQL unterscheidet sich ein wenig von der anderer Datenbanken.

Um die "Schleife" zu erhalten, müssen wir eine LOOP ... END LOOP verwenden konstruieren.

Aber um zu verhindern, dass diese Schleife für immer läuft, brauchen wir eine LEAVE-Anweisung, die uns erlaubt, die Schleife zu verlassen.

Wir verwenden einen bedingten Test, um zu bestimmen, wann wir gehen müssen. In diesem Beispiel möchten wir beenden, nachdem wir die Verarbeitung der letzten Zeile beendet haben.

Der FETCH wird eine Ausnahme auslösen, wenn keine weiteren Zeilen abgerufen werden müssen.

Wir „fangen“ diese Ausnahme in einem CONTINUE HANDLER (aus irgendeinem geheimnisvollen Grund müssen die „Handler“ die letzten Dinge deklarieren; MySQL wirft einen Fehler, wenn wir versuchen, etwas nach einem HANDLER (anders als einem anderen HANDLER) zu deklarieren.)

Wenn MySQL die Ausnahme „no more rows“ auslöst, wird der Handler-Code ausgelöst. In diesem Beispiel setzen wir nur eine Variable (mit dem Namen done ) auf einen Wert.

Da es sich um einen „Continue“-Handler handelt, beginnt die Verarbeitung wieder bei der Anweisung, bei der die Ausnahme ausgelöst wurde, in diesem Fall ist dies die Anweisung nach dem FETCH. Als erstes prüfen wir also, ob wir „fertig“ sind oder nicht. Wenn wir "fertig" sind, verlassen wir die Schleife und schließen den Cursor.

Andernfalls wissen wir, dass wir eine id haben Wert aus var_list gespeichert in einer Prozedurvariablen namens v_id . Also können wir jetzt machen, was wir wollen. Sieht so aus, als wollten Sie etwas SQL-Text in eine benutzerdefinierte Variable einfügen (einschließlich des Werts von v_id in den SQL-Text, dann PREPARE, EXECUTE und DEALLOCATE PREPARE.

Achten Sie darauf, die v_id zu deklarieren Variable mit dem passenden Datentyp, der zum Datentyp der id passt Spalte in var_list , ich habe nur angenommen, dass es ein INT ist.

Wenn wir das Ende der Schleife erreichen, "schleift" MySQL zurück zum Anfang der Schleife, und es geht wieder los.

Im Hauptteil der Schleife möchten Sie wahrscheinlich v_id mit CONCAT in den SQL-Text einfügen, den Sie ausführen möchten. Sieht so aus, als hätten Sie die Vorbereitung PREPARE, DEALLOCATE bereits im Griff. Zum Testen möchten Sie vielleicht eine LIMIT-Klausel zu SELECT in der Cursor-Deklaration hinzufügen und dann ein einfaches SELECT v_id ausführen; im Hauptteil, nur um zu überprüfen, ob die Schleife funktioniert, bevor Sie weiteren Code hinzufügen.

NACHVERFOLGUNG

Ich wollte einen anderen alternativen Ansatz für die Aufgabe erwähnen, d. h. das Ausführen einer Reihe von Anweisungen auf der Grundlage einer Vorlage, das Ersetzen von Werten, die von einer einzelnen SQL-Select-Anweisung bereitgestellt werden ...

Wenn ich zum Beispiel diese Vorlage hätte:

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

und ich musste die Vorkommen von @ID durch einen bestimmten ID-Wert aus einer Liste ersetzen, die von einer SELECT-Anweisung zurückgegeben wurde, z.

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

Ich würde wahrscheinlich kein gespeichertes MySQL-Programm mit einer CURSOR-Schleife verwenden, ich würde einen anderen Ansatz verwenden.

Ich würde eine SELECT-Anweisung verwenden, um eine Reihe von SQL-Anweisungen zu generieren, die ausgeführt werden könnten. Angenommen id ganzzahliger Typ ist, würde ich wahrscheinlich so etwas tun:

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

Für jeden Wert von id zurückgegeben von s , gibt die Anweisung den Text einer SQL SELECT-Anweisung zurück, die ausgeführt werden könnte (und muss). Das Erfassen in einer Textdatei würde mir ein SQL-Skript geben, das ich ausführen könnte.