问题
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