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

Como posso adicionar uma coluna a uma tabela de trabalho usando um novo procedimento armazenado


Esta pergunta e sua resposta se baseiam nas respostas para Como posso combinar dois procedimentos em um para preencher uma tabela em vez de cada um dos dois procedimentos preencher sua própria tabela? e Como posso adicionar uma coluna que aumenta em outra coluna na mesma tabela? , e a resposta a esta pergunta exige pequenas alterações à resposta às duas anteriores, que anotarei quando apropriado.

Como os cálculos do "período de descanso do arremessador" e da "média de corridas ganhas" são independentes um do outro, recomendo um procedimento separado para cada um. No entanto, como os resultados dos dois procedimentos costumam ser usados ​​juntos, recomendo uma tabela de rascunho comum para os cálculos e sugiro refactoring a criação e o preenchimento dessa tabela de rascunho em um terceiro procedimento:
DELIMITER $$

-- DROP PROCEDURE pitcher_stats_reset $$

CREATE PROCEDURE pitcher_stats_reset()
BEGIN
  DROP TEMPORARY TABLE IF EXISTS pitcher_stats_temp;

  CREATE TEMPORARY TABLE pitcher_stats_temp
  (
    pitcher_id      char(10)    NOT NULL,
    game_date       date        NOT NULL,
    game_seq        int         NOT NULL,
    innings_pitched double      DEFAULT 0.0,
    ip_total        double      DEFAULT 0.0,
    earned_runs     INT         DEFAULT 0,
    er_total        INT         DEFAULT 0,
    std_era         DOUBLE      DEFAULT 0.0,
    starter_rest    INT         DEFAULT 0,
    CONSTRAINT pitcher_stats_temp_pk
      PRIMARY KEY (pitcher_id , game_date , game_seq )
  ) ENGINE=InnoDB;

  INSERT INTO pitcher_stats_temp
        (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;
END $$

DELIMITER ;

A versão anterior usava uma tabela normal e persistente porque eu ainda não estava familiarizado com o manuseio de tabelas temporárias do MySQL. Uma tabela temporária é automaticamente descartada quando o usuário faz logoff, recuperando o espaço usado para dados derivados que podem ser regenerados quando necessário. Descartar e recriar a tabela é equivalente a TRUNCATE ing (exceto que a tabela não precisa existir de antemão) que por sua vez é muito mais rápido que um DELETE incondicional , de acordo com os documentos do MySQL. Fiz as alterações anotadas apropriadas no procedimento de média de execuções por ganhos também.

O procedimento para calcular o tempo de descanso dos arremessadores mais uma vez segue o idioma padrão "control-break". Observe que lemos o primeiro registro e configuramos os campos de controle uma vez antes de entrar no loop, então dentro do loop testamos nossa condição de saída, processamos o registro "atual", lemos o registro "próximo" e fazemos o loop.
DROP PROCEDURE IF EXISTS pitcher_stats_rest_time;

DELIMITER $$

CREATE PROCEDURE pitcher_stats_rest_time()
  BEGIN
    DECLARE pit_id          CHAR(10);
    DECLARE prev_pit        CHAR(10);
    DECLARE gdate           DATE;
    DECLARE seq             INT;
    DECLARE prev_date       DATE;
    DECLARE rest_days       INT;
    DECLARE end_of_cursor   BOOLEAN;

    DECLARE no_table CONDITION FOR SQLSTATE '42S02';

    DECLARE c1 CURSOR FOR
      SELECT pitcher_id, game_date, game_seq
        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;

    SET end_of_cursor := FALSE;

    -- Read first record and initialize control fields
    OPEN c1;
    FETCH c1 INTO pit_id, gdate, seq;
    SET prev_date := 0;
    SET prev_pit := pit_id;

    fetch_loop: LOOP
      -- Test for end-of-cursor
      IF end_of_cursor THEN
        LEAVE fetch_loop;
      END IF;

      -- Test for change in control fields. If the pitcher changes,
      --  fake a change in the year to trigger the break.
      IF pit_id != prev_pit THEN
        SET prev_date := 0;
      END IF;

      IF YEAR(prev_date) = YEAR(gdate) THEN
        SET rest_days := DATEDIFF(gdate, prev_date);
      ELSE
        SET rest_days := 0;
      END IF;

      UPDATE pitcher_stats_temp
        SET starter_rest = rest_days
        WHERE pitcher_id = pit_id
          AND game_date = gdate
          AND game_seq = seq;

      -- After processing record, update control fields
      SET prev_date := gdate;
      SET prev_pit := pit_id;

      -- Read next record and repeat
      FETCH c1 INTO pit_id, gdate, seq;
    END LOOP;

    CLOSE c1;

  END $$

DELIMITER ;

Em uso, pitcher_stats_reset() é chamado primeiro, para inicializar a tabela de trabalho. Feito isso, pitcher_stats_era() e pitcher_stats_rest_time() pode ser chamado repetidamente em qualquer ordem. Se pitcher_stats_reset() não for chamado primeiro, os outros dois procedimentos emitirão um lembrete educado para fazê-lo.