MySQL IFNULL()没有捕获空值

时间:2015-11-17 19:08:41

标签: mysql triggers

我正在开发一个Yii2框架应用程序,在我的数据建模阶段,我决定让数据库引擎处理简单的更新并维护我的计数器表(用于返回未读消息的数量和用户的其他内容)。 / p>

我在消息表上设计了一个触发器,用于增加和减少用户的未读消息数。

用户可以是客户或建筑师。

数据库中的所有id列都是UNSIGNED INT(这些也是各个表的PK)。

从Workbench进行正向工程时遇到问题。我制作了一个包含测试数据的脚本,用于完整性测试和初始填充。在我为消息表添加AFTER_INSERT触发器之前,脚本执行正常。

这是触发器代码:

CREATE DEFINER = CURRENT_USER TRIGGER `visavis`.`message_AFTER_INSERT` AFTER INSERT ON `message` FOR EACH ROW
BEGIN
DECLARE archid INT UNSIGNED;

# Cheks if recevier is an architect    
SELECT IFNULL(`visavis`.`architect`.`id`,0)
INTO archid
FROM `visavis`.`architect`
WHERE `visavis`.`architect`.`user` = NEW.`to`;

# Checks if the new message is set to sent (and not read)
IF NEW.status = 1
THEN
    IF archid = 0 -- if the receiver is client
    THEN
        # Checks if the user receiving exists in the user_counter table
        IF NOT EXISTS (SELECT 1 FROM `visavis`.`user_counter` WHERE `visavis`.`user_counter`.`user` = NEW.`to`)
        THEN
            # Insert new row into user_counter table
            INSERT INTO `visavis`.`user_counter` (`user`,`messages`) VALUES (NEW.`to`,1);
        ELSE
            # Add one to the user followings counter
            UPDATE `visavis`.`user_counter`
            SET `visavis`.`user_counter`.`messages` = `visavis`.`user_counter`.`messages` + 1
            WHERE `visavis`.`user_counter`.`user` = NEW.`to`;
        END IF; -- if user_counter
    ELSE
        # Extra check if archid is null
        #IF ISNULL(archid)
        #THEN
        #   SET archid = 1; -- Testing value
        #END IF;

        # Checks if the architect receiving exists in the architect_counter table
        IF NOT EXISTS (SELECT 1 FROM `visavis`.`architect_counter` WHERE `visavis`.`architect_counter`.`architect` = archid)
        THEN                
            # Insert new row into architect_counter table
            INSERT INTO `visavis`.`architect_counter` (`architect`,`messages`) VALUES (archid,1);
        ELSE                
            # Add one to the user followings counter
            UPDATE `visavis`.`architect_counter`
            SET `visavis`.`architect_counter`.`messages` = `visavis`.`architect_counter`.`messages` + 1
            WHERE `visavis`.`architect_counter`.`architect` = archid;
        END IF; -- if architect_counter
    END IF; -- if receiver is client
END IF; -- if message is sent
END

问题是我收到了这个错误:

ERROR: Error 1048: Column 'architect' cannot be null

在客户端或架构师检查的ELSE分支上面的代码中,我插入了额外的检查代码,以便为变量赋值(它被注释掉)。使用此代码脚本可以正常传递,但所有未读消息最终都会出现id = 1的架构师。

我也在添加表格'的DDL:

CREATE TABLE IF NOT EXISTS `architect` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'A PK for the table.',
  `user` INT UNSIGNED NOT NULL COMMENT 'Link to the user that is the architect.',
  `office` INT UNSIGNED NULL COMMENT 'Link to the architect\'s office, if any.',
  `short_description` INT UNSIGNED NOT NULL COMMENT 'Link to the text of short description.',
  `description` INT UNSIGNED NOT NULL COMMENT 'Link to the text of description.',
  `specialty_1` INT UNSIGNED NULL COMMENT 'Link to the specialty.',
  `specialty_2` INT UNSIGNED NULL COMMENT 'Link to the specialty.',
  `specialty_3` INT UNSIGNED NULL COMMENT 'Link to the specialty.',
  `order` INT UNSIGNED NOT NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `fk_architect_specialty_1`
    FOREIGN KEY (`specialty_1`)
    REFERENCES `specialty` (`id`)
    ON DELETE SET NULL
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_architect_specialty_2`
    FOREIGN KEY (`specialty_2`)
    REFERENCES `specialty` (`id`)
    ON DELETE SET NULL
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_architect_specialty_3`
    FOREIGN KEY (`specialty_3`)
    REFERENCES `specialty` (`id`)
    ON DELETE SET NULL
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_architect_short_description`
    FOREIGN KEY (`short_description`)
    REFERENCES `text` (`id`)
    ON DELETE RESTRICT
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_architect_description`
    FOREIGN KEY (`description`)
    REFERENCES `text` (`id`)
    ON DELETE RESTRICT
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_architect_office`
    FOREIGN KEY (`office`)
    REFERENCES `office` (`id`)
    ON DELETE SET NULL
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_architect_user`
    FOREIGN KEY (`user`)
    REFERENCES `user` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = 'The info about the architect.';

CREATE TABLE IF NOT EXISTS `visavis`.`message` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'A PK of the table.',
  `from` INT UNSIGNED NOT NULL COMMENT 'User that sent the message.',
  `to` INT UNSIGNED NOT NULL COMMENT 'User that recieves the message.',
  `text` VARCHAR(2000) NOT NULL COMMENT 'Text of the message. Length constrained in the frontend.',
  `status` INT UNSIGNED NOT NULL DEFAULT 6 COMMENT 'Status of the message.',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Date and time when the message was created. Automaticaly recieves status 6 (draft).',
  `viewed_at` DATETIME NULL COMMENT 'Date and time when the message was viewed by the reciever. Set when the status changes to 1.',
  `sent_at` DATETIME NULL COMMENT 'Date and time when the message was sent. Set when the status changes to 2.',
  `replied_at` DATETIME NULL COMMENT 'Date and time when the message was replied, if any. Set when the status changes to 3.',
  `shared_at` DATETIME NULL COMMENT 'Date and time when the message was shared to external board. Set when the status changes to 4.',
  `deleted_at` DATETIME NULL COMMENT 'Date and time of message deletion (from the view). Set when the status changes to 5.',
  `message_type` INT UNSIGNED NOT NULL COMMENT 'Link to the type of the message.',
  `attachment` INT UNSIGNED NULL COMMENT 'Link to the attachment.',
  `template` INT UNSIGNED NULL COMMENT 'Link to the template the message implements.',
  PRIMARY KEY (`id`),
  INDEX `fk_user_from_idx` (`from` ASC),
  INDEX `fk_user_to_idx` (`to` ASC),
  INDEX `fk_message_type_type_idx` (`message_type` ASC),
  INDEX `fk_message_status_status_idx` (`status` ASC),
  INDEX `fk_message_attachment_idx` (`attachment` ASC),
  INDEX `fk_message_template_idx` (`template` ASC),
  CONSTRAINT `fk_user_from`
    FOREIGN KEY (`from`)
    REFERENCES `visavis`.`user` (`id`)
    ON DELETE CASCADE
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_user_to`
    FOREIGN KEY (`to`)
    REFERENCES `visavis`.`user` (`id`)
    ON DELETE CASCADE
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_message_type_type`
    FOREIGN KEY (`message_type`)
    REFERENCES `visavis`.`message_type` (`id`)
    ON DELETE RESTRICT
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_message_status_status`
    FOREIGN KEY (`status`)
    REFERENCES `visavis`.`message_status` (`id`)
    ON DELETE RESTRICT
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_message_attachment`
    FOREIGN KEY (`attachment`)
    REFERENCES `visavis`.`attachment` (`id`)
    ON DELETE SET NULL
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_message_template`
    FOREIGN KEY (`template`)
    REFERENCES `visavis`.`message_template` (`id`)
    ON DELETE SET NULL
    ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = 'Internal messaging system.'

CREATE TABLE IF NOT EXISTS `visavis`.`architect_counter` (
  `architect` INT UNSIGNED NOT NULL COMMENT 'A PK of the table.',
  `houses` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The number of houses in the system.',
  `followers` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The number of followers.',
  `liked_houses` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The number of houses that the users liked.',
  `sold` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The number of purchased items of the architect.',
  `messages` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The number of unread messages.',
  `customizings` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The number of customize request an architect has received.',
  `workings` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'The number of customizing work an archotect has begun work on. (accepted and assigned quotes)',
  PRIMARY KEY (`architect`),
  CONSTRAINT `fk_architect_counter_architect`
    FOREIGN KEY (`architect`)
    REFERENCES `visavis`.`architect` (`id`)
    ON DELETE CASCADE
    ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = 'Counter table for summed values, of the logged in achitect, ' /* comment truncated */ /*needed for the front end.*/

我还尝试过COALESCE(),CASE,读取IFNULL()返回SIGNED值,以便我CAST()整个IFNULL子句。

我对分隔符(Workbench处理它们)没有任何问题

这是固定的:

IF EXISTS (SELECT 1 FROM `visavis`.`architect` WHERE `visavis`.`architect`.`user` = NEW.`to`)
THEN
    SELECT `visavis`.`architect`.`id`
    INTO archid
    FROM `visavis`.`architect`
    WHERE `visavis`.`architect`.`user` = NEW.`to`;
ELSE
    SET archid = 0;
END IF;

1 个答案:

答案 0 :(得分:1)

我可能会偏离此处,但此查询

SELECT IFNULL(`visavis`.`architect`.`id`,0)
INTO archid
FROM `visavis`.`architect`
WHERE `visavis`.`architect`.`user` = NEW.`to`;

会将archid设置为select语句的结果行中的visavis.architect.id 的值,如果结果行中的值,则设置为0 null - 即null中的id值为visavis.architect

那么如果select语句没有匹配任何行,会发生什么?好吧,没有结果行,因此IFNULL语句永远不会运行,而archid仍保留null的初始值。

我会删除ifnull并将archid设置为null。然后只需更新您的客户端/架构师检查以测试archid是否为null:

SELECT `visavis`.`architect`.`id`
INTO archid
FROM `visavis`.`architect`
WHERE `visavis`.`architect`.`user` = NEW.`to`;

IF NEW.status = 1
THEN
    IF ISNULL(archid) THEN -- if the receiver is client
...