sql >> Base de Datos >  >> RDS >> Mysql

Declaración preparada de MySQL:cómo realizar un bucle

Como ya han sugerido otros, normalmente evitamos recorriendo un conjunto de resultados RBAR (fila por fila agonizante) principalmente por razones de rendimiento. Simplemente no queremos adquirir el hábito de recorrer un conjunto de resultados. Pero eso no responde a la pregunta que hiciste.

Para responder a la pregunta que hizo, aquí hay un ejemplo rudimentario de un programa almacenado de MySQL que usa un CURSOR para procesar individualmente las filas devueltas por una consulta. MySQL no admite bloques anónimos, por lo que la única forma de hacerlo es en un programa almacenado de MySQL, como un PROCEDIMIENTO

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 ;

Para ejecutar el procedimiento:

CALL loop_through_var_list();

NOTAS:La sintaxis para procesar un CURSOR en MySQL es bastante diferente a la de otras bases de datos.

Para obtener el "bucle", necesitamos usar un LOOP ... END LOOP construir.

Pero para evitar que ese ciclo se ejecute para siempre, necesitamos una instrucción LEAVE que nos permita salir del ciclo.

Usamos una prueba condicional para determinar cuándo salir. En este ejemplo, queremos salir después de que hayamos terminado de procesar la última fila.

El FETCH lanzará una excepción cuando no haya más filas para recuperar.

"Atrapamos" esa excepción en un CONTINUE HANDLER (por alguna extraña razón, los "handlers" tienen que declarar lo último; MySQL arroja un error si intentamos declarar algo después de un HANDLER (que no sea otro HANDLER).

Cuando MySQL lanza la excepción "no más filas", activa el código del controlador. En este ejemplo, solo estamos configurando una variable (llamada done ) a un valor.

Dado que es un controlador de "continuación", el procesamiento comienza de nuevo en la declaración donde se lanzó la excepción, en este caso, esa será la declaración que sigue a FETCH. Entonces, lo primero que hacemos es verificar si hemos "terminado" o no. Si hemos "terminado", salimos del bucle y cerramos el cursor.

De lo contrario, sabemos que tenemos un id valor de var_list almacenado en una variable de procedimiento llamada v_id . Así que ahora podemos hacer lo que queramos. Parece que desea colocar texto SQL en una variable definida por el usuario (incluido el valor de v_id en el texto SQL, luego PREPARAR, EJECUTAR y DESASIGNAR PREPARAR.

Asegúrese de declarar el v_id variable con el tipo de datos apropiado, que coincida con el tipo de datos del id columna en var_list , acabo de suponer que es un INT.

Cuando llegamos al final del ciclo, MySQL "recorre" de regreso al principio del ciclo y continuamos de nuevo.

En el cuerpo del ciclo, probablemente querrá CONCAT v_id en el texto SQL que desea ejecutar. Parece que ya tienes un control sobre la preparación PREPARE, DEALLOCATE. Para realizar pruebas, es posible que desee agregar una cláusula LIMIT en SELECT en la declaración del cursor y luego hacer un simple SELECT v_id; en el cuerpo, solo para verificar que el ciclo funciona, antes de agregar más código.

SEGUIMIENTO

Quería mencionar otro enfoque alternativo para la tarea, es decir, ejecutar una serie de declaraciones basadas en una plantilla, sustituyendo los valores proporcionados por una única declaración de selección de SQL...

Por ejemplo, si tuviera esta plantilla:

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

y necesitaba reemplazar las apariciones de @ID con un valor de identificación específico de una lista devuelta de una instrucción SELECT, p.

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

Probablemente no usaría un programa almacenado de MySQL con un bucle CURSOR, usaría un enfoque diferente.

Haría uso de una declaración SELECT para generar un conjunto de declaraciones SQL que podrían ejecutarse. Asumiendo id es de tipo entero, probablemente haría algo como esto:

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

Para cada valor de id devuelto por s , la instrucción devuelve el texto de una instrucción SQL SELECT que podría (y debería) ejecutarse. Capturar eso en un archivo de texto me daría un script SQL que podría ejecutar.