Cette question et sa réponse s'appuient sur les réponses à 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 ? et Comment puis-je ajouter une colonne qui s'incrémente sur une autre colonne dans la même table ? , et la réponse à cette question nécessite des modifications mineures de la réponse aux deux précédentes, que je noterai le cas échéant.
Étant donné que les calculs de la "période de repos du lanceur" et de la "moyenne des points mérités" sont indépendants l'un de l'autre, je recommande une procédure distincte pour chacun. Cependant, étant donné que les résultats des deux procédures seront souvent utilisés ensemble, je recommande une table de travail commune pour les calculs et suggère refactoring la création et la population de cette table de travail dans une troisième procédure :
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 version précédente utilisait une table normale et persistante car je n'étais pas encore familiarisé avec la gestion des tables temporaires par MySQL. Une table temporaire est automatiquement supprimée lorsque l'utilisateur se déconnecte, récupérant l'espace utilisé pour les données dérivées qui peuvent être régénérées au besoin. Supprimer et recréer la table équivaut à TRUNCATE
ing (sauf que la table n'a pas besoin d'exister au préalable) qui à son tour est beaucoup plus rapide qu'un DELETE
inconditionnel , selon la documentation MySQL. J'ai apporté les modifications annotées appropriées au procédure de moyenne des exécutions gagnées
aussi.
La procédure de calcul du temps de repos des lanceurs suit à nouveau l'idiome standard de «pause de contrôle». Notez que nous lisons le premier enregistrement et configurons les champs de contrôle une fois avant d'entrer dans la boucle, puis dans la boucle, nous testons notre condition de sortie, traitons l'enregistrement "actuel", lisons l'enregistrement "suivant" et effectuons une boucle.
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 ;
En cours d'utilisation, pitcher_stats_reset()
est appelée en premier, pour initialiser la table de travail. Une fois cela fait, pitcher_stats_era()
et pitcher_stats_rest_time()
peut être appelé à plusieurs reprises dans n'importe quel ordre. Si pitcher_stats_reset()
n'est pas appelé en premier, les deux autres procédures émettront un rappel poli pour le faire.