0

I created two tables that were each populated with a different procedure in MySQL with Sequel Pro. While each table has the correct information in it after running the respective procedure, I'm thinking that my data will be less scattered tables if I consolidate some of the tables more.

So, what I would like to do is combine the data from both tables into one. Below is the code I used to try to accomplish this. Unfortunately, it didn't work, and any help with the code would be much appreciated.

create table code:

-- Table: ip_ER_ERA_subtotal

-- DROP TABLE ip_ER_ERA_subtotal;

CREATE TABLE ip_ER_ERA_subtotal
(
  Starting_Pitcher VARCHAR(8) NOT NULL,
  Game_Date VARCHAR (10) NOT NULL,
  Game_Number VARCHAR (1) NOT NULL,
  innings_pitched double,
  ER double,
  ip_total double DEFAULT '0.0',
  ER_total double DEFAULT '0.0',
  ERA double DEFAULT '0.0',
  CONSTRAINT ip_ER_ERA_subtotal_pk 
      PRIMARY KEY (Starting_Pitcher, Game_Date , Game_Number)
) ENGINE=InnoDB

procedure code:

DELIMITER $$

    CREATE PROCEDURE accumulate_IP_ER()
    BEGIN
        DECLARE pit_id CHAR(10);
        DECLARE gdate DATE;
        DECLARE seq INT;
        DECLARE in_pit REAL;
        DECLARE earned_runs REAL;
        DECLARE accum REAL;
        DECLARE prev_year YEAR(4);
        DECLARE end_of_cursor BOOLEAN;

        DECLARE c1 CURSOR FOR
            SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched
                FROM ip_ER_subtotal
                ORDER BY Starting_Pitcher, Game_Date, Game_Number;

        DECLARE CONTINUE HANDLER FOR NOT FOUND
            SET end_of_cursor := TRUE;

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

        SET prev_year := 0;
        OPEN c1;

        fetch_loop: LOOP
            FETCH c1 INTO pit_id, gdate, seq, in_pit;
            IF end_of_cursor THEN
                LEAVE fetch_loop;
            END IF;
            IF YEAR(gdate) != prev_year THEN
                SET accum := 0.0;
                SET prev_year := YEAR(gdate);
            END IF;
            SET accum := accum + in_pit;
            UPDATE ip_ER_subtotal
                SET ip_total = accum
                WHERE Starting_Pitcher = pit_id
                  AND Game_Date = gdate
                  AND Game_Number = seq;
        END LOOP;
        CLOSE c1;

        DECLARE c2 CURSOR FOR
            SELECT Starting_Pitcher, Game_Date, Game_Number, earned_runs
                FROM ip_ER_subtotal
                ORDER BY Starting_Pitcher, Game_Date, Game_Number;

        DECLARE CONTINUE HANDLER FOR NOT FOUND
            SET end_of_cursor := TRUE;

        TRUNCATE TABLE ER_subtotal;
        INSERT INTO ip_ER_subtotal
            SELECT Starting_Pitcher, Game_Date, Game_Number, ER, 0.0
                FROM starting_pitchers_game_log;

        SET prev_year := 0;
        OPEN c2;

        fetch_loop: LOOP
            FETCH c2 INTO pit_id, gdate, seq, earned_runs;
            IF end_of_cursor THEN
                LEAVE fetch_loop;
            END IF;
            IF YEAR(gdate) != prev_year THEN
                SET accum := 0.0;
                SET prev_year := YEAR(gdate);
            END IF;
            SET accum := accum + earned_runs;
            UPDATE ip_ER_subtotal
                SET ER_total = accum
                WHERE Starting_Pitcher = pit_id
                  AND Game_Date = gdate
                  AND Game_Number = seq;
        END LOOP;
        CLOSE c2;                    
    END

I get the following error:

 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE c2 CURSOR FOR
            SELECT Starting_Pitcher, Game_Date, Game_Number, e' at line 46

Here is a screenshot of the first table I already created "ip_subtotal" enter image description here

Here is a screenshot of the second table I already created "ER_subtotal" enter image description here

Update:

Here is essentially your same code that I made minor changes to:

DELIMITER $$
CREATE PROCEDURE accumulate_IP_ER_ERA()
  BEGIN
    DECLARE pit_id VARCHAR(8);
    DECLARE gdate VARCHAR(10);
    DECLARE seq VARCHAR(1);
    DECLARE in_pit REAL;
    DECLARE earned_runs 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 c1 CURSOR FOR
      SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER
        FROM ip_ER_ERA_subtotal
        ORDER BY Starting_Pitcher, Game_Date, Game_Number;

    DECLARE CONTINUE HANDLER FOR NOT FOUND
      SET end_of_cursor := TRUE;

    TRUNCATE TABLE ip_ER_ERA_subtotal;
    INSERT INTO ip_ER_ERA_subtotal
        (Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER)
      SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER
        FROM starting_pitcher_game_log;

    SET end_of_cursor := FALSE;
    SET prev_year := 0;
    OPEN c1;

    fetch_loop: LOOP
      FETCH c1 INTO pit_id, gdate, seq, in_pit, earned_runs, accum_ip, accum_er, earned_run_avg;
      IF end_of_cursor THEN
        LEAVE fetch_loop;
      END IF;
      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;
      SET earned_run_avg := (accum_er / accum_ip) * 9;
      UPDATE ip_ER_ERA_subtotal
        SET ip_total = accum_ip,
            ER_total = accum_er,
            STD_ERA = earned_run_avg
          WHERE Starting_Pitcher = pit_id
            AND Game_Date = gdate
            AND Game_Number = seq
            AND prev_year=YEAR;
    END LOOP;
    CLOSE c1;
  END

Here's the error:

Incorrect number of FETCH variables

I did try to see what if any variables aren't being fetched and tried to add "accum_ip", "accum_er", "earned_run_avg" but it didn't work...It seems like the eight variables are the ones that the latter three + ones in your fetch statement should be the ones needed...

Here's the table code:

-- Table: ip_ER_ERA_subtotal

-- DROP TABLE ip_ER_ERA_subtotal;

CREATE TABLE ip_ER_ERA_subtotal
(
  Starting_Pitcher VARCHAR(8) NOT NULL,
  Game_Date VARCHAR(10) NOT NULL,
  Game_Number INT(1) NOT NULL,
  innings_pitched double,
  ER double,
  ip_total double DEFAULT '0.0',
  ER_total double DEFAULT '0',
  STD_ERA double DEFAULT '0.0',
  CONSTRAINT ip_ER_ERA_subtotal_pk 
      PRIMARY KEY (Starting_Pitcher, Game_Date , Game_Number)
) ENGINE=InnoDB

update:

Here's the code based on your changes, but columns ER, ER_total, and STD_ERA are filled with only "Null" values.

DELIMITER $$

CREATE PROCEDURE accumulate_ip_ER_ERA()
BEGIN
    DECLARE pit_id VARCHAR(8);
    DECLARE gdate VARCHAR(10);
    DECLARE seq VARCHAR(1);
    DECLARE in_pit REAL;
    DECLARE ER REAL;
    DECLARE accum_ip REAL;
    DECLARE accum_er REAL;
    DECLARE earned_run_avg REAL;
    DECLARE prev_year YEAR(4);
    DECLARE end_of_cursor BOOLEAN;

    DECLARE c1 CURSOR FOR
      SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER
        FROM ip_ER_ERA_subtotal
        ORDER BY Starting_Pitcher, Game_Date, Game_Number;

    DECLARE CONTINUE HANDLER FOR NOT FOUND
      SET end_of_cursor := TRUE;

    TRUNCATE TABLE ip_ER_ERA_subtotal;
    INSERT INTO ip_ER_ERA_subtotal
        (Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER)
      SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER
        FROM starting_pitcher_game_log;

    SET end_of_cursor := FALSE;
    SET prev_year := 0;
    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;
      IF YEAR(gdate) != prev_year THEN
        SET accum_ip := 0.0;
        SET accum_er := 0.0;
        SET prev_year := YEAR(gdate);
      END IF;
      SET accum_ip := accum_ip + in_pit;
      SET accum_er := accum_er + ER;
      SET earned_run_avg := (accum_er / accum_ip) * 9;
      UPDATE ip_ER_ERA_subtotal
        SET ip_total = accum_ip,
            ER_total = accum_er,
            STD_ERA = earned_run_avg
          WHERE Starting_Pitcher = pit_id
            AND Game_Date = gdate
            AND Game_Number = seq;
    END LOOP;
    CLOSE c1;
  END
  $$

Here's a screenshot of the table: enter image description here

Could it be that only a single formula can be handled in any given cursor operation?

SET accum_ip := accum_ip + in_pit;
  SET accum_er := accum_er + ER;
  SET earned_run_avg := (accum_er / accum_ip) * 9

Thank you for your help.

Darwin, Ok, here is the edited code that populates the following columns with correct values: Starting_Pitcher, Game_Date, Game_Number, innings pitched and ER from the table ip_ER_ERA_subtotal. Columns ER_total and STD_ERA have all "0"s in them.
Here is the code:

    DELIMITER $$
CREATE PROCEDURE accumulate_ip_ER_ERA()
    BEGIN
        DECLARE pit_id VARCHAR(8);
        DECLARE gdate VARCHAR(10);
        DECLARE seq VARCHAR(1);
        DECLARE in_pit REAL;
        DECLARE earned_runs REAL;
        DECLARE accum_ip REAL;
        DECLARE accum_er REAL;
        DECLARE earned_run_avg REAL;
        DECLARE prev_year YEAR(4);
        DECLARE end_of_cursor BOOLEAN;

        DECLARE c1 CURSOR FOR
          SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER
            FROM ip_ER_ERA_subtotal
            ORDER BY Starting_Pitcher, Game_Date, Game_Number;

        DECLARE CONTINUE HANDLER FOR NOT FOUND
          SET end_of_cursor := TRUE;

        TRUNCATE TABLE ip_ER_ERA_subtotal;
        INSERT INTO ip_ER_ERA_subtotal
            (Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER)
          SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, ER
            FROM starting_pitcher_game_log;

        SET end_of_cursor := FALSE;
        SET prev_year := 0;
        OPEN c1;

        fetch_loop: LOOP
          FETCH c1 INTO pit_id, gdate, seq, in_pit, earned_runs;
          IF end_of_cursor THEN
            LEAVE fetch_loop;
          END IF;
          IF YEAR(gdate) != prev_year THEN
            SET accum_ip := 0.0;
            SET accum_er := 0.0;
            SET earned_run_avg := 0.0;
            SET prev_year := YEAR(gdate);
          END IF;
          SET accum_ip := accum_ip + in_pit;
          SET accum_er := accum_er + ER;
          SET earned_run_avg := (accum_er / accum_ip) * 9;
          UPDATE ip_ER_ERA_subtotal
            SET ip_total = accum_ip,
                ER_total = accum_er,
                STD_ERA = earned_run_avg
              WHERE Starting_Pitcher = pit_id
                AND ER = earned_runs
                AND Game_Date = gdate
                AND Game_Number = seq;
        END LOOP;
        CLOSE c1;
      END
$$

I get the following error:

Unknown column 'ER' in 'field list'

Here is the screenshot of the table:

enter image description here

Now the "ER" column populated but no more "ip_total"

Ok, it finally worked with the below code. Not sure, but I had a hunch that it wasn't liking the name of the field "ER" from the table we were calling up the values (starting_pitcher_game_log) to be inserted into the new table. Don't know why it didn't like it...

EDIT: Here's my edited code to handle "NULL" and divide-by-zero situations:

DELIMITER $$

CREATE PROCEDURE accumulate_ip_ER_ERA()
BEGIN
        DECLARE pit_id VARCHAR(8);
        DECLARE gdate DATE;
        DECLARE seq INT;
        DECLARE in_pit REAL;
        DECLARE ER_id REAL;
        DECLARE accum_ip REAL;
        DECLARE accum_er REAL;
        DECLARE earned_run_avg REAL;
        DECLARE prev_year YEAR(4);
        DECLARE end_of_cursor BOOLEAN;

        DECLARE c1 CURSOR FOR
          SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, earned_runs
            FROM ip_ER_ERA_subtotal
            ORDER BY Starting_Pitcher, Game_Date, Game_Number;

        DECLARE CONTINUE HANDLER FOR NOT FOUND
          SET end_of_cursor := TRUE;

        TRUNCATE TABLE ip_ER_ERA_subtotal;
        INSERT INTO ip_ER_ERA_subtotal (Starting_Pitcher, Game_Date, Game_Number, innings_pitched, earned_runs)
        SELECT Starting_Pitcher, Game_Date, Game_Number, innings_pitched, earned_runs,
          IFNULL(innings_pitched, 0),  -- replace NULL with 0, if
          IFNULL(earned_runs, 0)              --   column not initialized
            FROM starting_pitcher_game_log;
        END IF;
        SET end_of_cursor := FALSE;
        SET prev_year := 0;
        OPEN c1;

        fetch_loop: LOOP
          FETCH c1 INTO pit_id, gdate, seq, in_pit, ER_id;
          IF end_of_cursor THEN
            LEAVE fetch_loop;
          END IF;
          IF YEAR(gdate) != prev_year THEN
            SET accum_ip := 0.0;
            SET accum_er := 0;
            SET earned_run_avg := 0.0;
            SET prev_year := YEAR(gdate);
          END IF;
          SET accum_ip := accum_ip + in_pit;
          SET accum_er := accum_er + ER_id;
          IF accum_er = 0 THEN  -- prevent divide-by-zero
        SET earned_run_avg := 0;
      ELSE          
          SET earned_run_avg := (accum_er / accum_ip) * 9;
          END IF;
            UPDATE ip_ER_ERA_subtotal
            SET ip_total = accum_ip,
                ER_total = accum_er,
                STD_ERA = earned_run_avg
              WHERE Starting_Pitcher = pit_id
                AND Game_Date = gdate
                AND Game_Number = seq;
        END LOOP;
        CLOSE c1;
      END

      $$

error:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(innings_pitched, 0), -- replace NULL with 0, if IFNULL(earned_runs, ' at line 25

UPDATE: Screenshots of table when sorting by STD_ERA showing abnormally high STD_ERA values at one end of the range for that field or grayed-out "NULL" values at the other end of the range.

enter image description here

enter image description here

4

1 回答 1

1

正确的; 让我们看看我们这里有什么。

首先,代码必须被阻塞如下:

variable declarations
cursor declarations
handler declarations
everything else

所以你DECLARE CURSOR c2 必须出现在DECLARE CURSOR c1和之间DECLARE CONTINUE HANDLER。此外,您只需要一个CONTINUE HANDLER,因为它从声明点到程序结束都生效。

接下来是声明

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

子句中的命名列SELECT是您从中选择的列, 而不是您要插入的列,因此它们必须是 table 中的列starting_pitchers_game_log。此外,由于未从starting_pitchers_game_log(即和)复制的列都有默认值,因此您可以在语句中使用列列表,如下所示:ip_totaler_totaleraINSERT

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;

这样可以节省输入,记录您实际将值插入哪些列,并将您的INSERT语句与源表和目标表中列的物理顺序隔离开来。

接下来,一旦你完成了CURSOR c1循环,不要截断表格,否则你会丢失刚刚完成的所有工作!TRUNCATE TABLE删除当前表中的所有行,这里用于清除上一次运行的结果。

最后,两个循环必须有不同的标签,比如fetch_loop_1fetch_loop_2。您还需要在进入第二个循环之前重置accum和。end_of_cursor但是,在这种情况下,我相信我们可以使用一个光标在一个循环中完成所有操作,这使代码更简单,因此更易于维护。

这是完整的程序:

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 ;

那应该做的工作。如果有人发现错误,请务必指出。

编辑:我刚刚添加了一些代码来说明如何防止来自源表的空值,以及如何避免在 ERA 计算中被零除。

编辑:我已经改回原来的列名和表名,以减少我自己的困惑。

编辑:代码更改为与如何使用新存储过程将列添加到工作表的答案一致

于 2015-11-27T05:48:38.600 回答