Diese Frage und ihre Antwort bauen auf den Antworten auf Wie kann ich zwei Prozeduren in einer kombinieren, um eine Tabelle zu füllen, anstatt dass jede der beiden Prozeduren ihre eigene Tabelle füllt? und Wie kann ich eine Spalte hinzufügen, die auf einer anderen Spalte in derselben Tabelle erhöht wird? , und die Antwort auf diese Frage erfordert geringfügige Änderungen der Antwort auf die beiden vorherigen, auf die ich gegebenenfalls hinweisen werde.
Da die Berechnungen „Pitcher-Ruhezeit“ und „Durchschnitt der verdienten Läufe“ unabhängig voneinander sind, empfehle ich jeweils ein separates Verfahren. Da die Ergebnisse der beiden Verfahren jedoch häufig zusammen verwendet werden, empfehle ich eine gemeinsame Streichtabelle für die Berechnungen und schlage Refactoring die Erstellung und Bestückung dieser Scratch-Tabelle in ein drittes Verfahren:
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 ;
Die vorherige Version verwendete eine normale, persistente Tabelle, da ich mit der Handhabung von temporären Tabellen durch MySQL noch nicht vertraut war. Eine temporäre Tabelle wird automatisch gelöscht, wenn sich der Benutzer abmeldet, wodurch der Speicherplatz zurückgewonnen wird, der für abgeleitete Daten verwendet wird, die bei Bedarf neu generiert werden können. Das Löschen und Neuerstellen der Tabelle entspricht TRUNCATE
ing (außer dass die Tabelle vorher nicht existieren muss), was wiederum viel schneller ist als ein unbedingtes DELETE
, gemäß den MySQL-Dokumenten. Ich habe entsprechende kommentierte Änderungen an Earned-Runs-Average-Prozedur
auch.
Das Verfahren zur Berechnung der Ruhezeit der Pitcher folgt wieder der Standardsprache „Control-Break“. Beachten Sie, dass wir den ersten Datensatz lesen und die Kontrollfelder einmal einrichten, bevor wir in die Schleife eintreten, dann innerhalb der Schleife auf unsere Ausgangsbedingung testen, den "aktuellen" Datensatz verarbeiten, den "nächsten" Datensatz lesen und eine Schleife ausführen.
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 Verwendung, pitcher_stats_reset()
wird zuerst aufgerufen, um die Arbeitstabelle zu initialisieren. Sobald das erledigt ist, pitcher_stats_era()
und pitcher_stats_rest_time()
können in beliebiger Reihenfolge wiederholt aufgerufen werden. Wenn pitcher_stats_reset()
nicht zuerst aufgerufen wird, werden die beiden anderen Prozeduren höflich daran erinnern.