9
votes

Création de curseur avec SQL dynamique dans MySQL

J'écris une procédure stockée, qui ouvre un curseur à une table, puis itérale à tous les enregistrements. Dans le processus itérateur, je crée une requête dynamique basée sur les résultats du premier curseur.

J'ai besoin d'ouvrir le curseur sur Dynamic SQL, mais MySQL ne me permet pas de le faire. Selon le Doc officiel de MySQL: "Les curseurs doivent être déclarés avant de déclarer des gestionnaires. Les variables et les conditions doivent être déclarées avant de déclarer des curseurs ou des gestionnaires" < p> Voici le script: xxx

aucune idée de la création de curseur pour une requête dynamique? dans mysql?


0 commentaires

7 Réponses :


6
votes

Créez une autre procédure et écrivez le code de curseur dans cette nouvelle procédure, puis appelez la procédure à partir de laquelle vous souhaitez déclarer un curseur ...


1 commentaires

Est-ce vraiment la meilleure solution? Si encombrant!



0
votes

Je vois 2 problèmes possibles dans votre script:

1) "Déclarez curseur2 curseur pour la STMT;" doit probablement être déplacé vers le haut de la procédure avec toutes les autres déclarations, avant toutes les déclarations exécutables.

2) Les curseurs ne peuvent pas être basés sur SQL dynamique (c'est-à-dire que je ne pense pas que vous puissiez le construire sur une déclaration préparée). Pour contourner cette limitation, vous pouvez déclarer le curseur en fonction de la vue, puis créer la vue avec SQL dynamique avant d'ouvrir le curseur. Le problème avec cette approche est que les points de vue sont publics - la déclaration du curseur doit avoir un nom fixe pour la vue, de sorte que plusieurs utilisateurs simultanés pouvaient voir par inadvertance que d'autres ont défini de manière dynamique. Mon travail est de vérifier l'existence de la vue et de retarder l'exécution de la procédure jusqu'à ce que la vue soit supprimée. Cela signifie que pour être réalisable dans un environnement occupé, vous devez créer la vue, boucler dans le curseur, puis laisser tomber la vue aussi rapidement que possible. Pas techniquement élégant, mais cette approche travaillait dans ma situation de trafic faible et évite les frais généraux des tables temporaires. Sinon, comme d'autres ont suggéré, des tables temporaires sont en sécurité, mais peuvent avoir une incidence sur les performances.


0 commentaires

0
votes

L'approche de Karni est moins encombrante. Créez deux ou plusieurs SPS pour satisfaire chaque branche conditionnelle (pour chacune d'une SQL dynamique aurait été requise). Créez un wrapper SP et des appels de ventilateur de ce SP à «BRICKER» SPS.

L'alternative qui est l'approche "Vue préparée" nécessite plus de cycles de processeurs et de mémoire et d'espace disque supplémentaire tout en exécutant la procédure.


0 commentaires

1
votes

Comme vous ne pouvez pas utiliser les requêtes dynamiques avec le curseur, car vous ne pouvez pas définir avant déclarer . De plus, vous ne pouvez pas utiliser les procédures stockées appel avec curseur pour

Déclarez le curseur Cursor_Name pour Select_Statement

appel n'est pas un select_statement .

comme solution de contournement: de
Vous devez créer 3 procédures au lieu de seulement 1.

  1. Tables temporaires / Vues Générateur de
    Écrivez une procédure stockée pour générer des tables temporaires ou des vues pour vos requêtes dynamiques.
  2. Calcul des résultats de
    Votre procédure actuelle utilisera curseur pour Sélectionnez à partir de les tables temporaires. Mais vous devez vous assurer d'exécuter la procédure de tables / vues temporaires d'abord - pour obtenir des résultats mises à jour. Et vous ne pouvez pas appel une procédure avant déclarer le curseur. C'est la raison pour laquelle vous aurez besoin de la troisième étape.
  3. fonctionnant tous ensemble Une procédure stockée finale sur appel la procédure générant des tables / vues temporaires et ensuite appelez votre procédure prévue pour calculer les résultats. Vous devriez enfin utiliser cette dernière procédure comme celle qui effectue votre résultat.

0 commentaires

9
votes

Il n'est pas autorisé à définir un curseur de cur pour préparer_statement, vous devez définir une instruction SQL valide. La bonne nouvelle est que vous pouvez définir le curseur sur une vue pouvant être créée de manière dynamique plus tard. Par exemple ...

DROP PROCEDURE IF EXISTS my_dynamic_proc;
DELIMITER //
CREATE PROCEDURE my_dynamic_proc(tablename varchar(64), fieldname varchar(64), country VARCHAR(64))
BEGIN
    DECLARE adr_value varchar(500);
    DECLARE done BOOLEAN DEFAULT FALSE;
    -- Cursor definition
    DECLARE cur1 CURSOR FOR SELECT address FROM tmp_view_address;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    -- Dynamic view definition and creation
    SET @v = concat('CREATE OR REPLACE VIEW tmp_view_address as SELECT `',fieldname,'` as address FROM ',tablename,' WHERE country_name = "',country,'" group by 1 order by count(1) desc');
    PREPARE stm FROM @v;
    EXECUTE stm;
    DEALLOCATE PREPARE stm;
    -- Open cursor
    OPEN cur1;
    read_loop: LOOP
      FETCH cur1 INTO adr_value;
      IF done THEN
        LEAVE read_loop;
      END IF;
      -- Basic output result
      SELECT concat("My address is ",adr_value);
      -- Use every result in a dynamic update
      SET @u = concat('update ',tablename,' set new_field_address = "',adr_value,'" where country_name = "',country,'" and new_field_address is null');
      PREPARE stm FROM @u;
      EXECUTE stm;
      DEALLOCATE PREPARE stm;
  END LOOP;
  CLOSE cur1;
END//
DELIMITER ;


1 commentaires

Ce fut vraiment un économiseur de vie. Merci beaucoup d'avoir donné à 100% de ce que je cherchais, et cela a travaillé comme un charme sans tracas nulle part! À votre santé! :)



0
votes

Ce fil m'a beaucoup aidé alors voici ma réponse sur la manière dont vous utilisez des valeurs d'une table pour itérer cela à travers la requête et sur la base de la deuxième table ou de la vue. XXX


0 commentaires

0
votes

Nous pouvons penser à un travail autour de ce cas en utilisant stmt code>:

  1. comptez tous les enregistrements qui seront renvoyés par la requête p> li>

  2. boucle et parcourez chaque enregistrement renvoyé par la requête, car cela utilise la limite. P> li> ol>

    Voir l'exemple ci-dessous: P>

    CREATE PROCEDURE `proc_example`(IN p_where text)
    BEGIN
    
        DECLARE v_where text default "";
        DECLARE v_cont integer default 0;
        
        #build a dynamic where
        set v_where = p_where;
        
        #Count query records 
        set @v_sqlSelect_count = 'select count(*) into @v_total ';
        set @v_sqlSelect_count = concat(@v_sqlSelect_count,'from table ');
        set @v_sqlSelect_count = concat(@v_sqlSelect_count,'where ');
        set @v_sqlSelect_count = concat(@v_sqlSelect_count,v_where);
        
        #Executa query
        PREPARE stmt_total FROM @v_sqlSelect_count;
        EXECUTE stmt_total;
        DEALLOCATE PREPARE stmt_total;
        
        #if exists records
        if (@v_total > 0) then
            
            set v_cont = 0;
            
            navRecords:loop
            
                if (v_cont > (@v_total - 1)) then
                    leave getAgend;
                end if;
                
                #build select
                set @v_sqlSelect = 'select id,name ';
                set @v_sqlSelect = concat(@v_sqlSelect,'into @id,@name ');
                set @v_sqlSelect = concat(@v_sqlSelect,'from table ');
                set @v_sqlSelect = concat(@v_sqlSelect,'where ');
                set @v_sqlSelect = concat(@v_sqlSelect,v_where);
                set @v_sqlSelect = concat(@v_sqlSelect,' order by id asc limit ',v_cont,',1'); 
                
                #Execute query
                PREPARE stmt_select FROM @v_sqlSelect;
                EXECUTE stmt_select;
                DEALLOCATE PREPARE stmt_select;   
                
                #Do anything with the data @id, @name
                update table1 set desc1 = @name where id1 = @id;
                
                #Next record
                set v_cont = v_cont + 1;
                
            end loop navRecords;
            
        end if;
    
    END
    


0 commentaires