sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo puedo agregar una columna que se incrementa en otra columna en la misma tabla?

Mi mejor consejo para ti es, no hagas esto. Almacenar información que puede derivarse de otra información en la base de datos generalmente se considera un diseño muy pobre e intentar confiar en el orden de las filas en la base de datos es un camino seguro hacia la locura.

Aquí hay un primer paso para normalizar su tabla:

-- 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;

(No sigo el béisbol, así que solo pude adivinar algunos de los nombres de las columnas). Tenga en cuenta que el year_id , month_id y day_id las columnas se han ido, ya que esos valores se pueden recrear desde el game_date columna como indiqué en los comentarios. También tu game_id la columna se ha ido; esto se puede recrear concatenando opposing_team , game_date y game_seq (que supongo que es para tener en cuenta los encabezados dobles, etc.) También he convertido W y L en una sola columna destinada a contener los valores "W" (ganador), "L" (pérdida) y "T" (empate).

Los teams table proporciona una tabla de búsqueda para los ID de equipo de 3 caracteres. Se puede ampliar para contener cualquier otro dato del equipo que desee. (Tenga en cuenta que está destinado a describir el equipo en sí; actividades del equipo iría en otra mesa).

Para responder a su pregunta sobre las cláusulas de "restricción", la primera (CONSTRAINT starting_pitcher_log_pk y la línea sangrada debajo) especifica que la concatenación de esas tres columnas sirve como el identificador único principal para cada fila de la tabla. El segundo (CONSTRAINT team_fk FOREIGN KEY (opposing_team) y las líneas sangradas debajo) significa que para que un valor se coloque en el opposing_team columna ya debe existir en el teams.team_id columna; no puedes jugar contra un equipo que no existe.

Ahora a trabajar para responder realmente a su pregunta original. La mejor solución que se me ocurrió en MySQL fue una tabla temporal y un procedimiento almacenado, de la siguiente manera:

-- 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;

Y el procedimiento almacenado:

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

Este procedimiento borra la tabla ip_subtotal , lo completa desde la tabla principal y luego acumula el total acumulado de las entradas lanzadas. También utiliza un simple control de interrupción para reiniciar el acumulador al comienzo del año. Después de ejecutar el procedimiento ejecutando

CALL accumulate_innings();

puede consultar el ip_subtotal mesa o únala de nuevo al starting_pitchers_game_log mesa como se desee.

El procedimiento también podría ampliarse para aceptar una fecha de inicio y finalización; Eso lo dejo como ejercicio para el lector.

Espero que esto ayude; fue interesante y me obligó a aprender un poco de MySQL.