sql >> Databasteknik >  >> RDS >> Mysql

Hur kan jag kombinera två procedurer i en för att fylla en tabell i stället för att var och en av de två procedurerna fyller sin egen tabell?

Rätt; låt oss se vad vi har här.

Först måste koden blockeras enligt följande:

variable declarations
cursor declarations
handler declarations
everything else

Så din DECLARE CURSOR c2 måste visas mellan DECLARE CURSOR c1 och DECLARE CONTINUE HANDLER . Dessutom behöver du bara en CONTINUE HANDLER eftersom den träder i kraft från deklarationstillfället till slutet av förfarandet.

Nästa är uttalandet

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

De namngivna kolumnerna i SELECT klausul är kolumnerna du väljer från, inte de du infogar in i så de måste vara kolumner i tabellen starting_pitchers_game_log . Dessutom, eftersom kolumnerna inte kopieras från starting_pitchers_game_log (det vill säga ip_total , er_total och era ) alla har standardvärden, kan du använda en kolumnlista på INSERT uttalande, som så:

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;

Detta sparar skrivning, dokument vilka kolumner du faktiskt infogar värden i och isolerar din INSERT uttalande från den fysiska ordningen för kolumner i käll- och måltabellerna.

Därefter, när du är klar med CURSOR c1 loop, avkorta inte tabellen eller så förlorar du allt arbete du just har gjort! TRUNCATE TABLE tar bort alla rader för närvarande i tabellen och används här för att rensa bort resultaten från föregående körning.

Slutligen måste de två slingorna ha olika etiketter, säg fetch_loop_1 och fetch_loop_2 . Du skulle också behöva återställa accum och end_of_cursor innan du går in i den andra slingan. Men i det här fallet tror jag att vi kan göra allt i en slinga med en markör, vilket gör koden enklare och därmed lättare att underhålla.

Här är hela proceduren:

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 ;

Det borde göra jobbet. Om någon hittar en bugg, påpeka det för all del.

EDIT:Jag har precis lagt till lite kod för att illustrera hur man skyddar mot nollvärden som kommer från källtabellen, och hur man undviker att dividera med noll i ERA-beräkningen.

EDIT:Jag har ändrat tillbaka till mina ursprungliga kolumn- och tabellnamn för att minska min egen förvirring.

EDIT:Koden har ändrats för att överensstämma med svaret på Hur kan jag lägga till en kolumn i en arbetstabell med en ny lagrad procedur




  1. COUNT CASE och WHEN-sats i MySQL

  2. Tar bort oönskat tecken från kolumnen

  3. Anpassad BESTÄLLNING EFTER Förklaring

  4. Undersökningar med EXISTS vs IN - MySQL