Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Instrução Preparada para MySQL - Como Fazer o Loop Through


Como outros já sugeriram, normalmente evitamos percorrendo um conjunto de resultados RBAR (linha por linha agonizante) principalmente por motivos de desempenho. Nós apenas não queremos adquirir o hábito de fazer um loop através de um conjunto de resultados. Mas isso não responde a pergunta que você fez.

Para responder à pergunta que você fez, aqui está um exemplo rudimentar de um programa armazenado no MySQL que usa um CURSOR para processar individualmente as linhas retornadas por uma consulta. O MySQL não suporta blocos anônimos, então a única maneira de fazer isso é em um programa armazenado no MySQL, como um 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 ;

Para executar o procedimento:
CALL loop_through_var_list();

NOTAS:A sintaxe para processar um CURSOR no MySQL é um pouco diferente de outros bancos de dados.

Para obter o "looping", precisamos usar um LOOP ... END LOOP construir.

Mas para evitar que esse loop seja executado para sempre, precisamos de uma instrução LEAVE que nos permita sair do loop.

Usamos um teste condicional para determinar quando sair. Neste exemplo, queremos sair depois de terminar de processar a última linha.

O FETCH vai lançar uma exceção quando não houver mais linhas a serem buscadas.

Nós "capturamos" essa exceção em um CONTINUE HANDLER (por alguma razão misteriosa, os "handlers" têm as últimas coisas declaradas; o MySQL lança um erro se tentarmos declarar algo depois de um HANDLER (diferente de outro HANDLER).

Quando o MySQL lança a exceção "no more rows", isso aciona o código do manipulador. Neste exemplo, estamos apenas configurando uma variável (chamada done ) para um valor.

Como é um manipulador "continue", o processamento é iniciado novamente na instrução em que a exceção foi lançada, neste caso, será a instrução após o FETCH. Então, a primeira coisa que fazemos é verificar se estamos "feitos" ou não. Se estivermos "concluídos", saímos do loop e fechamos o cursor.

Caso contrário, sabemos que temos um id valor de var_list armazenado em uma variável de procedimento chamada v_id . Então agora podemos fazer o que quisermos. Parece que você deseja colocar algum texto SQL em uma variável definida pelo usuário (incluindo o valor de v_id no texto SQL, depois PREPARE, EXECUTE e DEALLOCATE PREPARE.

Certifique-se de declarar o v_id variável com o tipo de dados apropriado, que corresponde ao tipo de dados do id coluna em var_list , acabei de assumir que é um INT.

Quando chegamos ao final do loop, o MySQL "loops" de volta ao início do loop, e lá vamos nós novamente.

No corpo do loop, provavelmente você desejará CONCAT v_id no texto SQL que deseja executar. Parece que você já tem um controle sobre o PREPARE, DEALLOCATE prepare. Para teste, você pode querer adicionar uma cláusula LIMIT no SELECT na declaração do cursor e então fazer um simples SELECT v_id; no corpo, apenas para verificar se o loop está funcionando, antes de adicionar mais código.

ACOMPANHAMENTO

Eu queria mencionar outra abordagem alternativa para a tarefa, ou seja, executar uma série de instruções com base em um modelo, substituindo os valores fornecidos por uma única instrução SQL select...

Por exemplo, se eu tivesse este modelo:
SELECT * 
  INTO OUTFILE '/tmp/[email protected]'
  FIELDS TERMINATED BY ',' ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
FROM data 
WHERE id = @ID
ORDER BY 1 

e eu precisava substituir as ocorrências de @ID por um valor de id específico de uma lista retornada de uma instrução SELECT, por exemplo.
SELECT id
  FROM var_list
 WHERE id IS NOT NULL
 GROUP BY id

Eu provavelmente não usaria um programa armazenado MySQL com um loop CURSOR, eu usaria uma abordagem diferente.

Eu faria uso de uma instrução SELECT para gerar um conjunto de instruções SQL que poderiam ser executadas. Assumindo id é do tipo inteiro, eu provavelmente faria algo assim:
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 retornado de s , a instrução retorna o texto de uma instrução SQL SELECT que pode (e precisa) ser executada. Capturar isso em um arquivo de texto me daria um script SQL que eu poderia executar.