【问题标题】:mysql update another table using a triggermysql使用触发器更新另一个表
【发布时间】:2018-05-02 14:48:59
【问题描述】:

在将一行插入“video_ratings”表后,我需要更新“video_upload”表。在此之前,我需要获取 RATINGS 列的计数,然后应该使用该值进行更新。所需的逻辑如下所述。

  1. 从 video_ratings 中获取全 0 的计数并更新 video_upload 的 RATE_BAD 列。
  2. 从 video_ratings 中获取所有 1 的计数并更新 video_upload 的 RATE_AVERAGE 列。
  3. 从 video_ratings 中获取所有 2 的计数并更新 video_upload 的 RATE_GOOD 列。
  4. 从 video_ratings 中获取所有 3 的计数并更新 video_upload 的 RATE_BEST 列。

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

但我无法根据我的要求对其进行修改。请帮忙。

【问题讨论】:

  • 您应该知道这不是本网站的运作方式。您应该坚持使用这些触发器示例并返回特定错误。
  • 嗨@RubioRic,对不起我的错。我已经更新了这个问题。请参考。
  • 我希望看到 OLD。或新的。更新触发器中的值。
  • 这两个表都应该定义一个主键。而且看起来video_ratings 中的VID_ID 是对video_uploadID 列的外键引用。

标签: php mysql stored-procedures triggers


【解决方案1】:

一些笔记...

我可以肯定地保证不会有满足条件的行

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

考虑一下。如果其中一个条件(比较)评估为 TRUE,则其他比较将评估为 FALSE,并且

  TRUE  AND  FALSE  AND  FALSE 

将评估为 FALSE。

因此,该语句上的 COUNT() 聚合将评估为 0。


在这个 UPDATE 语句中,

update video_upload set RATE=TOTAL;

成功了,这将更新 video_upload 表中的每一行。似乎我们只想更新 video_upload 表上的 一个 行,该行的 ID 值与我们刚刚插入到 videos_ratings 表中的行的 VID_ID 匹配。

我们可以通过引用得到我们刚刚插入的行的VID_ID列的值

 NEW.VID_ID 

在触发器主体中。我们可能想要一个如下所示的更新语句:

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

如果我们想为RATE_BADRATE_AVERAGERATE_GOODRATE_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
;

跟进

我推荐这种模式用于触发器名称:table_name + _suffix

其中_suffix 是“_ad”、“_ai”、“_au”、“_bd”、“_bi”、“_bu”之一(用于删除/插入/更新之后/之前)

遵循这个命名约定可以避免命名冲突,当我们在表上查找触发器时,我们会知道在哪里找到它们。通过按字母顺序列出触发器,给定表的所有触发器将按 table_name (大部分)分组在一起。 (在极端情况下,我们可能会进行一些混合,即表名以另一个表的名称开头,后跟 _a.. 或 _b..)

(在早期开发中,当您有两个表和六个触发器时,这种命名约定的优势并不明显。但是当数据库包含大量表和触发器时,它变得很明显。)

另请注意,对于给定表上的每个 BEFORE/AFTER INSERT/UPDATE/DELETE,MySQL 仅支持单个触发器。

使用局部变量而不是用户定义的变量,除非有特定原因需要使用用户定义的变量。

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 ;

【讨论】:

  • 嗨@spencer7593,我已经修改了触发器,如下所述,但是在运行触发器时它给了我“未知系统变量'updatecount'”错误。 Colud你请帮助我。 ~~ 在更新每一行的 video_ratings 之前创建触发器 after_videor_update 开始 set updatecount = (select count(RATINGS) AS BAD_TOTAL from video_ratings where RATINGS='0');更新 video_upload SET RATE_BAD = updatecount ,其中 USER_ID = NEW.STU_ID AND ID = NEW.VID_ID;结束~~
  • 在我看来 updatecount 没有声明为局部变量。但是,当我们可以使用SELECT ... INTO 时,为什么还要使用SET 声明呢?为什么我们不能一口气获得所有四个评级的计数?为什么不通过一条语句执行更新video_upload 中的所有四列呢?为什么我们不限定列引用?为什么我们要包含一个条件来匹配STU_IDUSER_ID ... 不是STU_ID 给出评级的用户,我们不希望这与上传视频的用户不同?难道我们不想要一个 AFTER 触发器而不是一个 BEFORE 触发器吗?
  • 目前你能帮我用单率触发吗?经过一些修改后,我成功地保存了触发器,但它并没有给我预期的结果。任何建议请。 ~~DELIMITER $$ CREATE TRIGGER after_videor_update AFTER ON UPDATE ON video_ratings FOR EACH BEGIN SET @updatecount = (select count(RATINGS) AS BAD_TOTAL from video_ratings where RATINGS='0');更新 video_upload SET RATE_BAD =`@updatecount` 其中 USER_ID = NEW.STU_ID AND ID = NEW.VID_ID; END$$ 分隔符 ;~~
猜你喜欢
  • 1970-01-01
  • 2011-03-23
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 2014-10-20
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多