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

¿Cómo puedo combinar dos procedimientos en uno para completar una tabla en lugar de que cada uno de los dos procedimientos complete su propia tabla?

Derecha; veamos qué tenemos aquí.

Primero, el código debe bloquearse de la siguiente manera:

variable declarations
cursor declarations
handler declarations
everything else

Así que tu DECLARE CURSOR c2 debe aparecer entre DECLARE CURSOR c1 y DECLARE CONTINUE HANDLER . Además, solo necesitas un CONTINUE HANDLER porque surte efectos desde el momento de la declaración hasta el final del procedimiento.

La siguiente es la declaración

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

Las columnas nombradas en el SELECT cláusula son las columnas que está seleccionando, no los que estás insertando, entonces tienen que ser columnas en la tabla starting_pitchers_game_log . Además, dado que las columnas no se copian de starting_pitchers_game_log (es decir, ip_total , er_total y era ) todos tienen valores predeterminados, puede usar una lista de columnas en INSERT declaración, así:

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;

Esto ahorra tipeo, documenta en qué columnas está insertando valores y aísla su INSERT declaración del orden físico de las columnas en las tablas de origen y de destino.

A continuación, una vez que termine el CURSOR c1 loop, no truncar la tabla ¡o perderá todo el trabajo que acaba de hacer! TRUNCATE TABLE elimina todas las filas actualmente en la tabla y se usa aquí para borrar los resultados de la ejecución anterior.

Finalmente, los dos bucles deben tener etiquetas diferentes, digamos fetch_loop_1 y fetch_loop_2 . También necesitaría restablecer accum y end_of_cursor antes de entrar en el segundo bucle. Sin embargo, en este caso, creo que podemos hacer todo en un bucle con un cursor, lo que hace que el código sea más simple y, por lo tanto, más fácil de mantener.

Aquí está el procedimiento completo:

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 ;

Eso debería hacer el trabajo. Si alguien encuentra un error, por favor indíquelo.

EDITAR:acabo de agregar un código para ilustrar cómo protegerse contra los valores nulos provenientes de la tabla de origen y cómo evitar una división por cero en el cálculo de ERA.

EDITAR:he vuelto a cambiar a mi columna original y nombres de tabla para reducir mi propia confusión.

EDITAR:el código cambió para ser consistente con la respuesta a ¿Cómo puedo agregar una columna a una tabla de trabajo usando un nuevo procedimiento almacenado?