Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment puis-je combiner deux procédures en une pour remplir une table plutôt que chacune des deux procédures remplissant sa propre table?

À droite; voyons ce que nous avons ici.

Tout d'abord, le code doit être bloqué comme suit :

variable declarations
cursor declarations
handler declarations
everything else

Donc votre DECLARE CURSOR c2 doit apparaissent entre DECLARE CURSOR c1 et DECLARE CONTINUE HANDLER . De plus, vous n'avez besoin que d'un seul CONTINUE HANDLER car elle prend effet du moment de la déclaration jusqu'à la fin de la procédure.

Vient ensuite la déclaration

INSERT INTO ip_ER_subtotal
    SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, 0.0
        FROM starting_pitchers_game_log;

Les colonnes nommées dans le SELECT clause sont les colonnes dans lesquelles vous sélectionnez, pas ceux que vous insérez dans, ils doivent donc être des colonnes dans la table starting_pitchers_game_log . De plus, puisque les colonnes ne sont pas copiées de starting_pitchers_game_log (c'est-à-dire ip_total , er_total et era ) ont tous des valeurs par défaut, vous pouvez utiliser une liste de colonnes sur le INSERT déclaration, comme suit :

INSERT INTO pitcher_stats_temp
    (Starting_Pitcher, Game_Date, Game_Number, innings_pitched, er)
  SELECT pitcher_id, game_date, game_seq, innings_pitched, runs
    FROM starting_pitchers_game_log;

Cela évite de taper, documente les colonnes dans lesquelles vous insérez réellement des valeurs et isole votre INSERT déclaration de l'ordre physique des colonnes dans les tables source et cible.

Ensuite, une fois que vous avez terminé le CURSOR c1 boucle, ne tronquez pas le tableau ou vous perdrez tout le travail que vous venez de faire ! TRUNCATE TABLE supprime toutes les lignes actuellement dans le tableau et est utilisé ici pour effacer les résultats de l'exécution précédente.

Enfin, les deux boucles doivent avoir des étiquettes différentes, disons fetch_loop_1 et fetch_loop_2 . Vous devrez également réinitialiser accum et end_of_cursor avant d'entrer dans la deuxième boucle. Cependant, dans ce cas, je pense que nous pouvons tout faire en une seule boucle avec un seul curseur, ce qui rend le code plus simple et donc plus facile à maintenir.

Voici la procédure complète :

DROP PROCEDURE IF EXISTS pitcher_stats_era;

DELIMITER $$

CREATE PROCEDURE pitcher_stats_era()
  BEGIN
    DECLARE pit_id CHAR(10);
    DECLARE gdate DATE;
    DECLARE seq INT;
    DECLARE in_pit REAL;
    DECLARE er INT;
    DECLARE accum_ip REAL;
    DECLARE accum_er INT;
    DECLARE earned_run_avg REAL;
    DECLARE prev_year YEAR(4);
    DECLARE end_of_cursor BOOLEAN;

    DECLARE no_table CONDITION FOR SQLSTATE '42S02';

    DECLARE c1 CURSOR FOR
      SELECT pitcher_id, game_date, game_seq, innings_pitched, earned_runs
        FROM pitcher_stats_temp
        ORDER BY pitcher_id, game_date, game_seq;

    DECLARE CONTINUE HANDLER FOR NOT FOUND
      SET end_of_cursor := TRUE;

    DECLARE EXIT HANDLER FOR no_table
    BEGIN
      SIGNAL no_table
        SET MESSAGE_TEXT = "Work table not initialized. Please call pitcher_stats_reset() before continuing",
        MYSQL_ERRNO = 1146;
    END;
------------------------------------------------------------------
-- The following steps are now performed by pitcher_stats_reset()
------------------------------------------------------------------
--  TRUNCATE TABLE ip_subtotal;  -- Clear our work table for a new run
    -- Copy data from main table into work table
--  INSERT INTO ip_subtotal
--      (pitcher_id, game_date, game_seq, innings_pitched, earned_runs)
--    SELECT pitcher_id, game_date, game_seq,
--        IFNULL(innings_pitched, 0),  -- replace NULL with 0, if
--        IFNULL(runs, 0)              --   column not initialized
--      FROM starting_pitchers_game_log;
---------------------------------------------------------------------

    SET end_of_cursor := FALSE;  -- reset
    SET prev_year := 0;          -- reset control-break

    OPEN c1;

    fetch_loop: LOOP
      FETCH c1 INTO pit_id, gdate, seq, in_pit, er;
      IF end_of_cursor THEN
        LEAVE fetch_loop;
      END IF;

      -- check control-break conditions
      IF YEAR(gdate) != prev_year THEN
        SET accum_ip := 0.0;
        SET accum_er := 0;
        SET prev_year := YEAR(gdate);
      END IF;

      SET accum_ip := accum_ip + in_pit;
      SET accum_er := accum_er + er;
      IF accum_er = 0 THEN  -- prevent divide-by-zero
        SET earned_run_avg := 0;
      ELSE
        SET earned_run_avg := (accum_ip / accum_er) * 9;
      END IF;

      UPDATE pitcher_stats_temp
        SET ip_total = accum_ip,
            er_total = accum_er,
            std_era = earned_run_avg
        WHERE pitcher_id = pit_id
          AND game_date = gdate
          AND game_seq = seq;

    END LOOP;

    CLOSE c1;
  END
$$
DELIMITER ;

Cela devrait faire l'affaire. Si quelqu'un trouve un bogue, n'hésitez pas à le signaler.

EDIT :Je viens d'ajouter du code pour illustrer comment se protéger contre les valeurs nulles provenant de la table source et comment éviter une division par zéro dans le calcul de l'ERA.

EDIT :j'ai changé les noms de mes colonnes et tables d'origine afin de réduire ma propre confusion.

EDIT :le code a été modifié pour être cohérent avec la réponse à Comment ajouter une colonne à une table de travail à l'aide d'une nouvelle procédure stockée