MySQL обновить другую таблицу с помощью триггера - PullRequest
0 голосов
/ 02 мая 2018

Мне нужно обновить таблицу "video_upload" после вставки строки в таблицу "video_ratings". Прежде чем сделать это, мне нужно получить счетчик столбца RATINGS, а затем выполнить обновление, используя это значение. Необходимая логика приведена ниже.

  1. взять счетчик всех 0 из video_ratings и обновить столбец RATE_BAD video_upload.
  2. взять подсчет всех 1 из video_ratings и обновить столбец RATE_AVERAGE video_upload.
  3. взять счетчик всех 2-х из video_ratings и обновить столбец RATE_GOOD для video_upload.
  4. взять счетчик всех 3-х из video_ratings и обновить столбец RATE_BEST video_upload.

Таблица video_upload

  CREATE TABLE `video_upload` (
  `ID` int(10) NOT NULL,
  `USER_ID` int(10) NOT NULL,
  `VIDEO_NAME` varchar(75) COLLATE utf32_sinhala_ci NOT NULL,
  `VIDEO_URL` varchar(100) COLLATE utf32_sinhala_ci DEFAULT NULL,
  `PIC1_URL` varchar(100) COLLATE utf32_sinhala_ci DEFAULT NULL,
  `PIC2_URL` varchar(100) COLLATE utf32_sinhala_ci DEFAULT NULL,
  `PIC3_URL` varchar(100) COLLATE utf32_sinhala_ci DEFAULT NULL,
  `ATT_URL` varchar(100) COLLATE utf32_sinhala_ci DEFAULT NULL,
  `TYPE` varchar(7) COLLATE utf32_sinhala_ci NOT NULL,
  `DESCRIPTION` varchar(2000) COLLATE utf32_sinhala_ci NOT NULL,
  `IsAPPROVED` int(1) NOT NULL DEFAULT '0',
  `RATE_BAD` int(10) NOT NULL,
  `RATE_AVERAGE` int(10) NOT NULL,
  `RATE_GOOD` int(10) NOT NULL,
  `RATE_BEST` int(10) NOT NULL,
  `UPLOADED_ON` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `UPDATED_ON` timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf32 COLLATE=utf32_sinhala_ci;

Таблица video_ratings

    CREATE TABLE `video_ratings` (
  `VID_ID` int(10) NOT NULL,
  `STU_ID` int(10) NOT NULL,
  `RATINGS` int(1) NOT NULL,
  `RATED_ON` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `UPDATED_ON` timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf32 COLLATE=utf32_sinhala_ci;

Я планирую сделать это с помощью триггера. Я также нашел код запуска MySQL из Интернета.

//Rating count trigger
create trigger rate_count_update
after insert on video_ratings for each row begin
DECLARE updatecount INT;
  set updatecount = ( select count(RATINGS) AS TOTAL from video_ratings where RATINGS='0' AND RATINGS='1' AND RATINGS='2' AND RATINGS='3' );
  if updatecount=4
    then
      update video_upload set RATE=TOTAL;
  end if;
end//

Но я не смог изменить его в соответствии с моим требованием. Пожалуйста, помогите в этом.

1 Ответ

0 голосов
/ 02 мая 2018

Пара заметок ...

Я могу с уверенностью гарантировать, что не будет НИКАКИХ строк, удовлетворяющих условиям

  FROM video_ratings
 WHERE RATINGS='0' 
   AND RATINGS='1'
   AND RATINGS='2'

Подумай об этом. Если одно из этих условий (сравнений) оценивается как ИСТИНА, то другие сравнения будут оцениваться как ЛОЖЬ, и

  TRUE  AND  FALSE  AND  FALSE 

собирается оценить в ЛОЖЬ.

Таким образом, агрегат COUNT () для этого оператора будет иметь значение 0.


В этом операторе UPDATE

update video_upload set RATE=TOTAL;

если он успешен, он обновит каждую строку в таблице video_upload. Похоже, мы бы хотели обновить только одну строку в таблице video_upload, строку, которая имеет значение ID, соответствующее VID_ID строки, которую мы только что вставили в таблицу videos_ratings.

Мы можем получить значение столбца VID_ID строки, которую мы только что вставили, ссылаясь на

 NEW.VID_ID 

в теле триггера. Вероятно, нам нужен оператор обновления, который выглядит примерно так:

UPDATE video_upload v
   SET ...
 WHERE v.ID = NEW.VID_ID ; 

Если мы хотим присвоить значения столбцам RATE_BAD, RATE_AVERAGE, RATE_GOOD и RATE_BEST, нам нужно предложение SET для ссылки на эти столбцы ...

UPDATE video_upload v
   SET v.RATE_BAD      = some_expr 
     , v.RATE_AVERAGE  = another_expr
     , v.RATE_GOOD     = expr_for_good
     , v.RATE_BEST     = expr_for_best
 WHERE v.ID = NEW.VID_ID ; 

Может быть, мы хотели сделать что-то подобное, чтобы получить счетчик оценок и сохранить их в локальных переменных, чтобы мы могли ссылаться на них позже в триггере.

SELECT IFNULL(SUM(r.RATINGS='0'),0) AS cnt_r0
     , IFNULL(SUM(r.RATINGS='1'),0) AS cnt_r1 
     , IFNULL(SUM(r.RATINGS='2'),0) AS cnt_r2 
     , IFNULL(SUM(r.RATINGS='3'),0) AS cnt_r3 
  FROM video_ratings r 
 WHERE r.VID_ID = NEW.VID_ID
   INTO li_cnt_r0
      , li_cnt_r1
      , li_cnt_r2
      , li_cnt_r3
;

Followup

Я рекомендую этот шаблон для имен триггеров: table_name + _suffix

, где _suffix является одним из '_ad', '_ai', '_au', '_bd', '_bi', '_bu' (после / до удаления / вставки / обновления)

Следуя этому соглашению об именах, вы избегаете коллизий имен, и когда мы ищем триггеры на столе, мы будем знать, где их найти. При перечислении триггеров в алфавитном порядке все триггеры для данной таблицы будут сгруппированы по имени таблицы (в основном). (В крайнем случае, когда у нас есть потенциал для некоторого смешения, это имя таблицы, которое начинается с имени другой таблицы, за которым следует _a .. или _b ..)

(В ранней разработке, когда у вас есть две таблицы и полдюжины триггеров, преимущество такого соглашения об именах не очевидно. Но оно становится очевидным, когда база данных содержит множество таблиц и триггеров.)

Также обратите внимание, что MySQL поддерживает только один триггер для каждого из BEFORE / AFTER INSERT / UPDATE / DELETE для данной таблицы.

Используйте локальные переменные в пользу пользовательских переменных, если только нет особой причины для использования пользовательской переменной.

DELIMITER $$

DROP TRIGGER IF EXISTS video_ratings_ad$$

CREATE TRIGGER video_ratings_ad
AFTER DELETE ON video_ratings
FOR EACH ROW
BEGIN
   -- declare local variables
   DECLARE li_cnt_r0  BIGINT;
   DECLARE li_cnt_r1  BIGINT;
   DECLARE li_cnt_r2  BIGINT;
   DECLARE li_cnt_r3  BIGINT;
   -- get counts of ratings for specific VID_ID
   -- and store counts in local variables 
   SELECT IFNULL(SUM(r.RATINGS='0'),0) AS cnt_r0
        , IFNULL(SUM(r.RATINGS='1'),0) AS cnt_r1
        , IFNULL(SUM(r.RATINGS='2'),0) AS cnt_r2
        , IFNULL(SUM(r.RATINGS='3'),0) AS cnt_r3
     FROM video_ratings r
    WHERE r.VID_ID = OLD.VID_ID
     INTO li_cnt_r0
        , li_cnt_r1
        , li_cnt_r2
        , li_cnt_r3
   ;
   -- update target table with rating counts from local variables
   UPDATE video_upload t
      SET t.RATE_BAD      = li_cnt_r0
        , v.RATE_AVERAGE  = li_cnt_r1
        , v.RATE_GOOD     = li_cnt_r2
        , v.RATE_BEST     = li_cnt_r3
    WHERE t.ID = OLD.VID_ID
   ;
END$$

DROP TRIGGER IF EXISTS video_ratings_ai$$

CREATE TRIGGER video_ratings_ai
AFTER UPDATE ON video_ratings
FOR EACH ROW
BEGIN
   -- declare local variables
   DECLARE li_cnt_r0  BIGINT;
   DECLARE li_cnt_r1  BIGINT;
   DECLARE li_cnt_r2  BIGINT;
   DECLARE li_cnt_r3  BIGINT;
   -- get counts of ratings for specific VID_ID
   -- and store counts in local variables 
   SELECT IFNULL(SUM(r.RATINGS='0'),0) AS cnt_r0
        , IFNULL(SUM(r.RATINGS='1'),0) AS cnt_r1
        , IFNULL(SUM(r.RATINGS='2'),0) AS cnt_r2
        , IFNULL(SUM(r.RATINGS='3'),0) AS cnt_r3
     FROM video_ratings r
    WHERE r.VID_ID = NEW.VID_ID
     INTO li_cnt_r0
        , li_cnt_r1
        , li_cnt_r2
        , li_cnt_r3
   ;
   -- update target table with rating counts from local variables
   UPDATE video_upload t
      SET t.RATE_BAD      = li_cnt_r0
        , v.RATE_AVERAGE  = li_cnt_r1
        , v.RATE_GOOD     = li_cnt_r2
        , v.RATE_BEST     = li_cnt_r3
    WHERE t.ID = NEW.VID_ID
   ;
END$$

DROP TRIGGER IF EXISTS video_ratings_au$$

CREATE TRIGGER video_ratings_au
AFTER UPDATE ON video_ratings
FOR EACH ROW
BEGIN
   -- declare local variables
   DECLARE li_cnt_r0  BIGINT;
   DECLARE li_cnt_r1  BIGINT;
   DECLARE li_cnt_r2  BIGINT;
   DECLARE li_cnt_r3  BIGINT;
   IF( OLD.RATINGS <=> NEW.RATINGS 
   -- if VID_ID and RATINGS is not changed, we can skip getting counts
   IF( NEW.VID_ID <=> OLD.VID_ID AND NEW.RATINGS <=> OLD.RATINGS )
   THEN BEGIN END
   ELSE
      -- get counts of ratings for OLD.VID_ID
      -- and store counts in local variables
      SELECT IFNULL(SUM(r.RATINGS='0'),0) AS cnt_r0
           , IFNULL(SUM(r.RATINGS='1'),0) AS cnt_r1
           , IFNULL(SUM(r.RATINGS='2'),0) AS cnt_r2
           , IFNULL(SUM(r.RATINGS='3'),0) AS cnt_r3
        FROM video_ratings r
       WHERE r.VID_ID = OLD.VID_ID
        INTO li_cnt_r0
           , li_cnt_r1
           , li_cnt_r2
           , li_cnt_r3
      ;
      -- update target table with rating counts from local variables
      UPDATE video_upload t
         SET t.RATE_BAD      = li_cnt_r0
           , v.RATE_AVERAGE  = li_cnt_r1
           , v.RATE_GOOD     = li_cnt_r2
           , v.RATE_BEST     = li_cnt_r3
       WHERE t.ID = OLD.VID_ID
      ;
      IF( NEW.VID_ID <=> OLD.VID_ID )
      THEN BEGIN END
      ELSE
         -- get counts of ratings for specific VID_ID
         -- and store counts in local variables 
         SELECT IFNULL(SUM(r.RATINGS='0'),0) AS cnt_r0
              , IFNULL(SUM(r.RATINGS='1'),0) AS cnt_r1
              , IFNULL(SUM(r.RATINGS='2'),0) AS cnt_r2
              , IFNULL(SUM(r.RATINGS='3'),0) AS cnt_r3
           FROM video_ratings r
          WHERE r.VID_ID = NEW.VID_ID
           INTO li_cnt_r0
              , li_cnt_r1
              , li_cnt_r2
              , li_cnt_r3
         ;
         -- update target table with rating counts from local variables
         UPDATE video_upload t
            SET t.RATE_BAD      = li_cnt_r0
              , v.RATE_AVERAGE  = li_cnt_r1
              , v.RATE_GOOD     = li_cnt_r2
              , v.RATE_BEST     = li_cnt_r3
          WHERE t.ID = NEW.VID_ID
         ;
      END IF;
   END IF;
END$$

DELIMITER ;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...