如何使用新的存储过程将列添加到工作表

时间:2015-11-29 22:33:40

标签: mysql datetime stored-procedures

我尝试使用新过程向工作表添加一个新过程,该过程与我用于填充工作表中已有的列的过程不同。我不确定这样做是否会更有效和节省资源,或者通过添加我用于填充表中已有的列/值的原始过程。在任何情况下,这是添加此列的单独过程的代码。该列被称为" starter_rest"并表示为字段中的日期值与#34; Game_Date"之间的差异。和该字段/列中的先前值。

以下是我使用的代码,它给了我一个错误。

DROP PROCEDURE IF EXTSTS accumpts_starter_rest()

DELIMITER $$

CREATE PROCEDURE accumulate_starter_rest()
BEGIN
        DECLARE pit_id VARCHAR(8);
        DECLARE gdate DATE;
        DECLARE prev_date DATE;
        DECLARE seq INT;
        DECLARE strt_rst REAL;
        DECLARE prev_year YEAR(4);
        DECLARE end_of_cursor BOOLEAN;

        DECLARE c1 CURSOR FOR
          SELECT starter_rest
            FROM ip_ER_ERA_subtotal_1
            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_1;
        INSERT INTO ip_ER_ERA_subtotal_1 (starter_rest)
        SELECT starter_rest
            FROM ip_ER_ERA_subtotal_1;

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

        OPEN c1;

        fetch_loop: LOOP
          FETCH c1 INTO pit_id, gdate, prev_date, seq, strt_rst;
          IF end_of_cursor THEN
            LEAVE fetch_loop;
          END IF;
          IF YEAR(gdate) != prev_year THEN
            SET strt_rst := 0;
            SET prev_year := YEAR(gdate);
          END IF;
          SET strt_rst := gdate - prev_date;
          END IF;
          UPDATE TABLE ip_ER_ERA_subtotal_1
          SET starter_rest = strt_rst
              WHERE Starting_Pitcher = pit_id
                AND Game_Date = gdate
                AND Game_Number = seq;
        END LOOP;
        CLOSE c1;
      END

      $$

这是我得到的错误:

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 'IF;
          UPDATE TABLE ip_ER_ERA_subtotal_1
          SET starter_rest = str' at line 39

这是表格的屏幕截图,其中列I'尝试填充已经创建的并且使用" NULL"值。

提前感谢您的帮助。

更新

ALTER TABLE retrosheet.starting_pitcher_game_log ADD COLUMN starter_rest INT;
UPDATE retrosheet.starting_pitcher_game_log AS b,
retrosheet.game AS g
SELECT @prev as previous,
 Game_Date
FROM retrosheet.starting_pitcher_game_log
SET b. starter_rest = Game_date-@prev
WHERE b.`Game_ID` = g.`GAME_ID`

收到错误:

[ERROR in query 2] 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 'SELECT @prev as previous,
 Game_Date
FROM retrosheet.starting_pitcher_game_log
S' at line 3

提前谢谢。

更新:这里是编辑过的表,用于在新年开始时为starter_rest设置0值:

    DROP PROCEDURE IF EXISTS starting_pitcher_stats_rest_time;

DELIMITER $$

    CREATE PROCEDURE starting_pitcher_stats_rest_time()

      BEGIN
        DECLARE pit_id CHAR(10);
        DECLARE gdate DATE;
        DECLARE seq INT;
        DECLARE prev_date DATE;
        DECLARE rest_days INT;
        DECLARE prev_year YEAR(4);
        DECLARE end_of_cursor BOOLEAN;

        DECLARE no_table CONDITION FOR SQLSTATE '42S02';

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

        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 prev_date := 0;
        SET end_of_cursor := FALSE;

        OPEN c1;
        fetch_loop: LOOP
          FETCH c1 INTO pit_id, gdate, seq;
          IF end_of_cursor THEN
            LEAVE fetch_loop;
          END IF;

          IF YEAR(gdate) != prev_year THEN
            SET rest_days := 0;
            SET prev_date := 0;
            SET gdate := 0;
            SET prev_year := YEAR(gdate);
          END IF;

          IF prev_date = 0 THEN
            SET rest_days := 0;
          ELSE
            SET rest_days := DATEDIFF(gdate, prev_date);
          END IF;
          SET prev_date := gdate;

          UPDATE starting_pitcher_stats
            SET starter_rest = rest_days
            WHERE Starting_Pitcher = pit_id
              AND Game_Date = gdate
              AND Game_Number = seq;

        END LOOP;

        CLOSE c1;

      END
    $$

    DELIMITER ;

这是该表的屏幕截图,显示了新赛季开始时首发休息的超值。理想情况下,它只会重置为0:

enter image description here

任何帮助都会很棒。顺便问一下,达尔文,谢谢你 - 你的帮助很棒!

1 个答案:

答案 0 :(得分:1)

这个问题及其答案建立在How can I combine two procedures in one to populate one table rather than each of the two procedures populating it's own table?How can I add a column that increments on another column in same table?的答案之上,而这个问题的答案需要对前两个问题的答案进行微小的修改,我将在适当的时候注意到

由于“投手休息期”和“赢得的平均值”计算是相互独立的,我建议为每个计算单独的程序。但是,由于这两个过程的结果通常会一起使用,我建议使用一个通用的临时表进行计算,并建议refactoring将该临时表的创建和填充到第三个过程中:

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 ;

之前的版本使用了普通的持久表,因为我还不熟悉MySQL对临时表的处理。当用户注销时,会自动删除临时表,回收用于派生数据的空间,可以根据需要重新生成。根据MySQL文档,删除和重新创建表相当于TRUNCATE(除了事先不需要存在表),这反过来比无条件DELETE快得多。我也对earned-runs-average procedure进行了适当的注释更改。

计算投手休息时间的程序再次遵循标准的“控制 - 休息”习惯。请注意,我们在进入循环之前读取第一条记录并设置控制字段一次,然后在循环内我们测试退出条件,处理“当前”记录,读取“下一条”记录,然后循环。

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 ;

在使用中,首先调用pitcher_stats_reset()来初始化工作表。完成后,可以按任何顺序重复调用pitcher_stats_era()pitcher_stats_rest_time()。如果未首先调用pitcher_stats_reset(),则其他两个程序将发出礼貌提醒。