Questa domanda e la relativa risposta si basano sulle risposte a Come posso combinare due procedure in una per popolare una tabella anziché ciascuna delle due procedure che popolano la propria tabella? e Come posso aggiungere una colonna che si incrementa su un'altra colonna nella stessa tabella? , e la risposta a questa domanda richiede lievi modifiche alla risposta alle due precedenti, che prenderò nota quando opportuno.
Poiché i calcoli del "periodo di riposo del lanciatore" e della "media delle corse guadagnate" sono indipendenti l'uno dall'altro, raccomando una procedura separata per ciascuno. Tuttavia, poiché i risultati delle due procedure verranno spesso utilizzati insieme, raccomando uno scratch table comune per i calcoli e suggerisco refactoring la creazione e il popolamento di quel scratch table in una terza procedura:
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 ;
La versione precedente utilizzava una tabella normale e persistente perché non avevo ancora familiarità con la gestione delle tabelle temporanee da parte di MySQL. Una tabella temporanea viene automaticamente eliminata quando l'utente si disconnette, recuperando lo spazio utilizzato per i dati derivati che possono essere rigenerati in caso di necessità. Eliminare e ricreare la tabella equivale a TRUNCATE
ing (tranne che la tabella non deve esistere in anticipo) che a sua volta è molto più veloce di un DELETE
incondizionato , secondo i documenti MySQL. Ho apportato le modifiche annotate appropriate al procedura media guadagnata
anche.
La procedura per calcolare il tempo di riposo dei lanciatori segue ancora una volta il linguaggio standard "control-break". Nota che leggiamo il primo record e impostiamo i campi di controllo una volta prima di entrare nel ciclo, quindi all'interno del ciclo verifichiamo la nostra condizione di uscita, elaboriamo il record "corrente", leggiamo il record "successivo" e esegui il ciclo.
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 ;
In uso, pitcher_stats_reset()
viene chiamato per primo, per inizializzare la tabella di lavoro. Fatto ciò, pitcher_stats_era()
e pitcher_stats_rest_time()
può essere chiamato ripetutamente in qualsiasi ordine. Se pitcher_stats_reset()
non viene chiamato per primo, le altre due procedure invieranno un gentile sollecito di farlo.