How can I add a column that increments on another column in same table?

亡梦爱人 提交于 2020-01-16 01:01:06

问题


I have a table with a column called "IP", and I would like to create another column in the same table "YTD_IP" which would give the cumulative year-to-date sum of "IP" (innings pitched) for any given game in a year reflecting the sum of innings pitched in games (rows/events) that preceded it in the same season. Once there is a change in year, I'd like the sum to start at 0 again.

Here is a sample of data of columns "PIT_ID", "YEAR_ID", "IP", and what I'd like to see the "YTD_IP" column look like:

Pit_ID  YEAR_ID  IP      YTD_IP
aased001      1977  9.0000    9
aased001      1977  9.0000    18
aased001      1977  7.0000    25
aased001      1977  5.0000    30
aased001      1977  6.3333    36.3333
aased001      1977  6.3333    42.6666
aased001      1977  7.0000    49.6666
aased001      1977  6.0000    55.6666
aased001      1977  9.0000    64.6666
aased001      1977  9.0000    73.6666
aased001      1977  5.0000    78.6666
aased001      1977  6.3333    84.9999
aased001      1977  7.3333    92.3333
aased001      1978  4.3333     4.3333
aased001      1978  7.0000    11.3333
aased001      1978  6.3333    17.6666
aased001      1978  3.3333    20.9999

I've put together the following code:

ALTER TABLE starting_pitcher_game_log ADD COLUMN YTD_IP VARCHAR(255);
    SET YTD_IP:= 0;
    SELECT a.IP,  b.YEAR_ID
      IF(@prevYEAR_ID != YEAR_ID,IP,@YTD_IP+IP) AS IP,
      @prevYEAR_ID=YEAR_ID
    FROM starting_pitcher_game_log

But it's not right as it gets 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 'IF(@prevYEAR_ID != YEAR_ID,IP,@YTD_IP+IP) AS IP,
          @prevYEAR_ID=YEAR_ID
' at line 2

Is there a way to make sure that it accommodates future games that will be played?

Any help would go a long way.

screen shot of full table

Hi Darwin,

Can you share code that could work for a temporarily invoked column as well as code for a permanent one that could create a cumulative sum field? In particular, I would like to create another column in a table called "YTD_IP" which would give the cumulative year-to-date sum of "IP" (innings pitched) from the beginning of a season up to any given game in that season reflecting the sum of innings pitched by a given pitcher in games (rows/events) that preceded the given game + the innings pitched in that game. Once there is a change in season, I'd like the sum to start at 0 again.

Thank you in advances in for your help.


回答1:


My best advice to you is, Don't do this. Storing information that can be derived from other information in the database is generally considered very poor design, and attempting to rely on the order of the rows in the database is a sure path to madness.

Here's a first pass at normalizing your table:

-- Table: teams

-- DROP TABLE teams;

CREATE TABLE teams
(
  team_id character(3) primary key,
  team_name varchar(255),
  team_city varchar(255)
) engine=innodb;

-- Table: starting_pitchers_game_log

-- DROP TABLE starting_pitchers_game_log;

CREATE TABLE starting_pitchers_game_log
(
  pitcher_id character(10) NOT NULL,
  game_date date NOT NULL,
  opposing_team character(3),
  game_seq integer NOT NULL,
  outcome character(1),
  innings_pitched real,
  bfp integer,
  hits integer,
  runs integer,
  errors integer,
  homeruns integer,
  bb integer,
  k integer,
  ibb integer,
  hbp integer,
  wp integer,
  balks integer,
  CONSTRAINT starting_pitcher_log_pk
      PRIMARY KEY (pitcher_id , game_date , game_seq ),
  CONSTRAINT team_fk FOREIGN KEY (opposing_team)
      REFERENCES teams (team_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
) engine=innodb;

(I don't follow baseball, so I could only guess at some of the column names.) Note that the year_id, month_id and day_id columns are gone, as those values can be recreated from the game_date column as I indicated in the comments. Also your game_id column is gone; this can be recreated from concatenating opposing_team, game_date and game_seq (which I presume is to account for double-headers &c.) I've also converted W and L into a single column intended to hold the values "W" (win), "L" (loss), and "T" (tie).

The teams table provides a lookup table for the 3-char team ids. It can be expanded to hold any other team data you want. (Note it is meant to describe the team itself; team activities would go in another table.)

To answer your question about the "constraint" clauses, the first one (CONSTRAINT starting_pitcher_log_pk and the indented line below it) specifies that the concatenation of those three columns serves as the primary unique identifier for each row in the table. The second one (CONSTRAINT team_fk FOREIGN KEY (opposing_team) and the indented lines below it) means that for a value to be placed in the opposing_team column it must already exist in the teams.team_id column; you can't play against a team that doesn't exist.

Now to work on actually answering your original question. The best solution I could come up with on MySQL was a scratch table and a stored procedure, as follows:

-- Table: ip_subtotal

-- DROP TABLE ip_subtotal;

CREATE TABLE ip_subtotal
(
  pitcher_id char(10) NOT NULL,
  game_date date NOT NULL,
  game_seq int(11) NOT NULL,
  innings_pitched double,
  ip_total double DEFAULT '0.0',
  CONSTRAINT ip_subtotal_pk
      PRIMARY KEY (pitcher_id , game_date , game_seq )
) ENGINE=InnoDB;

And the stored procedure:

------------------------------------------------------------------------------    --
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$

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

    DECLARE c1 CURSOR FOR
        SELECT pitcher_id, game_date, game_seq, innings_pitched
            FROM ip_subtotal
            ORDER BY pitcher_id, game_date, game_seq;

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

    TRUNCATE TABLE ip_subtotal;
    INSERT INTO ip_subtotal
        SELECT pitcher_id, game_date, game_seq, 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_subtotal
            SET ip_total = accum
            WHERE pitcher_id = pit_id
              AND game_date = gdate
              AND game_seq = seq;
    END LOOP;
    CLOSE c1;
END

This procedure clears out the table ip_subtotal, populates it from the main table, then rolls up the running total for innings pitched. It also uses a simple control-break to reset the accumulator at the start of the year. After you've run the procedure by executing

CALL accumulate_innings();

you can query the ip_subtotal table or join it back to the starting_pitchers_game_log table as desired.

The procedure could also be extended to accept a start and end date; I leave that as an exercise for the reader.

Hope this helps; it was interesting and forced me to learn a little MySQL.



来源:https://stackoverflow.com/questions/33860342/how-can-i-add-a-column-that-increments-on-another-column-in-same-table

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!