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.