Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak mogę połączyć dwie procedury w jedną, aby wypełnić jedną tabelę, a nie każdą z dwóch procedur wypełniających jej własną tabelę?

Dobrze; zobaczmy, co tu mamy.

Najpierw kod należy zablokować w następujący sposób:

variable declarations
cursor declarations
handler declarations
everything else

Więc twój DECLARE CURSOR c2 musi pojawiają się między DECLARE CURSOR c1 i DECLARE CONTINUE HANDLER . Ponadto potrzebujesz tylko jednego CONTINUE HANDLER ponieważ obowiązuje od momentu deklaracji do końca procedury.

Dalej jest oświadczenie

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

Nazwane kolumny w SELECT klauzula to kolumny, z których wybierasz, nie te, do których wstawiasz, więc muszą to być kolumny w tabeli starting_pitchers_game_log . Ponadto, ponieważ kolumny nie są kopiowane z starting_pitchers_game_log (czyli ip_total , er_total i era ) wszystkie mają wartości domyślne, możesz użyć listy kolumn w INSERT oświadczenie, takie jak:

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;

Oszczędza to pisania, dokumentuje, do których kolumn faktycznie wstawiasz wartości, i izoluje Twój INSERT oświadczenie z fizycznej kolejności kolumn w tabeli źródłowej i docelowej.

Następnie po zakończeniu CURSOR c1 pętla, nie obcinaj tabeli albo stracisz całą pracę, którą właśnie wykonałeś! TRUNCATE TABLE usuwa wszystkie wiersze znajdujące się obecnie w tabeli i służy tutaj do wyczyszczenia wyników poprzedniego przebiegu.

Wreszcie, dwie pętle muszą mieć różne etykiety, powiedzmy fetch_loop_1 i fetch_loop_2 . Będziesz także musiał zresetować accum i end_of_cursor przed wejściem w drugą pętlę. Jednak w tym przypadku uważam, że możemy zrobić wszystko w jednej pętli z jednym kursorem, co czyni kod prostszym, a tym samym łatwiejszym w utrzymaniu.

Oto pełna procedura:

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 ;

To powinno załatwić sprawę. Jeśli ktoś znajdzie błąd, proszę o jego wskazanie.

EDYCJA:Właśnie dodałem trochę kodu, aby zilustrować, jak chronić się przed wartościami null pochodzącymi z tabeli źródłowej i jak uniknąć dzielenia przez zero w obliczeniach ERA.

EDYCJA:Zmieniłem z powrotem moje oryginalne nazwy kolumn i tabel, aby zmniejszyć własne zamieszanie.

EDYCJA:Kod zmieniony, aby był zgodny z odpowiedzią na Jak dodać kolumnę do tabeli roboczej przy użyciu nowej procedury składowanej




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQLite INSERT - PRZY AKTUALIZACJI ZDUPLIKOWANEGO KLUCZA (UPSERT)

  2. Wyzwalacze MySQL do aktualizacji pola na podstawie sumy kolumny z innej tabeli

  3. BŁĄD 1356 (HY000):Widok 'mysql.user' odwołuje się do nieprawidłowych tabel lub kolumn lub funkcji lub definiującego/wywołującego widok nie ma uprawnień do ich używania

  4. Kod błędu:1822. Nie można dodać ograniczenia klucza obcego. Brak indeksu dla ograniczenia „ekdromes_ibfk_1” w tabeli „pwlhseis”, do której istnieje odwołanie

  5. PHP MySql (1045) Odmowa dostępu dla użytkownika