2

大家好 StackOverflow 的各位,我是本站的粉丝,帮我走出了无数的坑。但是这次我似乎在这里或其他任何地方都找不到答案,我的问题是:

我正在开发一个 Yii 框架应用程序来处理数据库中的数据,保持一致性和一切。基本上,我正在创建一个用于操作数据库信息的界面。我正在使用 MySQL 5.0(计划更新到 5.5)和 InnoDB。

我的开发将支持一个已经存在的模式(我称之为main),它有几个设计问题。我们的目标是用设计正确的新模式(我称之为shadow)替换旧模式。但与此同时,我们正在尝试将新模式实现为主要模式的影子模式,并通过触发器保持更改的一致性。

所有重要的更改都对影子模式进行,它使用触发器将其反映到主模式。两种模式都托管在同一台服务器中,每当通过命令行客户端或使用 MySQL Workbench 进行更改时,触发器都能很好地反映从影子到主的数据更改,但每当我使用 Yii 应用程序进行更改以隐藏数据时......仅对影子模式进行更改,而不会触发反映到主模式。

这是shadow.tbl_device描述并触发 DDL:

mysql> use shadow;
mysql> describe tbl_device;
+--------------+-------------+------+-----+-------------------+-----------------------------+
| Field        | Type        | Null | Key | Default           | Extra                       |
+--------------+-------------+------+-----+-------------------+-----------------------------+
| Id           | int(11)     | NO   | PRI | NULL              | auto_increment              |
| SerialNumber | varchar(40) | NO   |     | NULL              |                             |
| State        | varchar(20) | NO   | MUL | Recién Llegado    |                             |
| ProviderId   | int(11)     | NO   | MUL | NULL              |                             |
| OwnerId      | int(11)     | NO   | MUL | NULL              |                             |
| ProfileId    | int(11)     | YES  | MUL | NULL              |                             |
| ChipId       | int(11)     | YES  | UNI | NULL              |                             |
| IMEI         | varchar(15) | YES  |     | NULL              |                             |
| ModelNumber  | varchar(20) | YES  |     | NULL              |                             |
| FirstUsed    | date        | YES  |     | NULL              |                             |
| Brand        | varchar(45) | NO   | MUL | No Definida       |                             |
| Agreement    | varchar(20) | NO   | MUL | No Establecido    |                             |
| LastUpdated  | timestamp   | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+--------------+-------------+------+-----+-------------------+-----------------------------+
13 rows in set (0.01 sec)

-- Trigger DDL Statements

USE `shadow`$$

CREATE
DEFINER=`root`@`localhost`
TRIGGER `shadow`.`trg_device_after_insert_produce_location_and_register_device`
AFTER INSERT ON `shadow`.`tbl_device`
FOR EACH ROW
BEGIN
    DECLARE NumCel VARCHAR(10) DEFAULT NULL;
    INSERT INTO tbl_location(DeviceId) values (New.Id);
    IF New.ChipId IS NOT NULL THEN
        SELECT CONCAT(AreaCode,Phone) INTO NumCel FROM tbl_chip WHERE Id = New.ChipId;
    END IF;

    IF New.Brand = 'Navcel' THEN
        INSERT INTO navcel.detalle_aplicacion(apId, pdRadioId, adActivo) values (1,CAST(New.SerialNumber as UNSIGNED), 1);
    END IF;
    IF New.Brand = 'Calamp' Then
        INSERT INTO main.equipos(eqId,eqNumCel,shadowDeviceId) values (CONV(SUBSTRING(New.SerialNumber,-6),16,10),NumCel,New.Id);
    ELSE
        INSERT INTO main.equipos(eqId,eqNumCel,shadowDeviceId) values (CAST(New.SerialNumber as UNSIGNED),NumCel,New.Id);
    END IF;
END$$

CREATE
DEFINER=`root`@`localhost`
TRIGGER `shadow`.`trg_device_after_update_reflect_changes`
AFTER UPDATE ON `shadow`.`tbl_device`
FOR EACH ROW
BEGIN
    DECLARE acuerdo TINYINT(4);
    DECLARE NumCel VARCHAR(10);
    DECLARE eqIdToUpdate INT;
    IF New.LastUpdated <> Old.LastUpdated THEN
        /* UPDATING THE REFLECTION OF THIS DEVICE IN THE main SCHEMA */
        IF New.Agreement = 'Renta' THEN set acuerdo := 1;
            ELSEIF New.Agreement = 'Venta' THEN set acuerdo := 2;
            ELSEIF New.Agreement = 'Prestamo' THEN set acuerdo := 3;
            ELSE set acuerdo := 0;
        END IF;
        IF New.ChipId IS NULL THEN 
            SET NumCel = NULL;
        ELSE 
            Select CONCAT(AreaCode,Phone) INTO NumCel FROM tbl_chip WHERE Id = New.ChipId;
        END IF;
        UPDATE main.equipos SET 
            eqId := New.SerialNumber,
            eqInstalado := 1,
            eqAcuerdo := acuerdo,
            eqNumCel := NumCel
        WHERE shadowDeviceId = New.id;
    END IF;
END$$

DDL 中的触发器是旨在反映对 main 的更改的触发器,这里是main.equipos描述(目前它不使用触发器):

+-----------------------+------------------+------+-----+---------+-------+
| Field                 | Type             | Null | Key | Default | Extra |
+-----------------------+------------------+------+-----+---------+-------+
| eqId                  | int(10) unsigned | NO   | PRI | 0       |       |
| shadowDeviceId        | int(11)          | YES  | UNI | NULL    |       |
| eqNumCel              | varchar(20)      | YES  |     | NULL    |       |
| stId                  | int(10) unsigned | NO   |     | 0       |       |
| TIPO_EQUIPOS_tpId     | int(10) unsigned | YES  |     | NULL    |       |
| eqNombre              | varchar(100)     | YES  |     | NULL    |       |
| eqNUI                 | varchar(50)      | YES  |     | NULL    |       |
| eqModelo              | varchar(20)      | YES  |     | NULL    |       |
| eqPlacas              | varchar(20)      | YES  |     | NULL    |       |
| eqLatitud             | decimal(9,6)     | YES  |     | NULL    |       |
| eqLongitud            | decimal(9,6)     | YES  |     | NULL    |       |
| eqAltitud             | float            | YES  |     | NULL    |       |
| eqSatelite            | varchar(20)      | YES  |     | NULL    |       |
| eqFechaActEq          | datetime         | YES  |     | NULL    |       |
| eqFechaActSer         | datetime         | YES  |     | NULL    |       |
| eqNivelGPRS           | float            | YES  |     | NULL    |       |
| eqIcono               | varchar(200)     | YES  |     | NULL    |       |
| eqTiempoRep           | datetime         | YES  |     | NULL    |       |
| eqVersion             | varchar(20)      | YES  |     | NULL    |       |
| eqLatDinGeo           | float            | YES  |     | NULL    |       |
| eqLonDinGeo           | float            | YES  |     | NULL    |       |
| eqTiempoGeo           | datetime         | YES  |     | NULL    |       |
| eqNumEconomico        | varchar(20)      | YES  |     | NULL    |       |
| eqNumPedido           | varchar(20)      | YES  |     | NULL    |       |
| eqVelocidad           | int(10)          | YES  |     | NULL    |       |
| eqNumSerie            | varchar(45)      | YES  |     |         |       |
| EsGeocercaId          | int(10) unsigned | YES  |     | NULL    |       |
| eqPuntoCercano        | int(10)          | NO   |     | 1       |       |
| eqDistanciaCercano    | float            | NO   |     | 0       |       |
| eqIconoActual         | varchar(100)     | NO   |     | 0       |       |
| eqStatusDBS           | varchar(45)      | YES  |     |         |       |
| eqTieneDBS            | int(1)           | NO   |     | 1       |       |
| eqFechaDBS            | datetime         | YES  |     | NULL    |       |
| eqEnAlarma            | tinyint(1)       | NO   |     | 0       |       |
| eqTipoMascara         | int(11)          | NO   |     | 1       |       |
| eqAdvComunicacion     | int(11)          | NO   |     | 120     |       |
| eqFallaComunicacion   | int(11)          | NO   |     | 300     |       |
| eqStComs              | int(11)          | YES  |     | 0       |       |
| eqCiudadCercana       | int(11)          | YES  |     | NULL    |       |
| eqDistCiudadCercana   | float(11,0)      | YES  |     | NULL    |       |
| eqUsaGeocercaDinamica | int(1)           | NO   |     | 0       |       |
| eqUcStatus            | tinyint(4)       | NO   |     | 0       |       |
| eqOdometro            | float            | NO   |     | 0       |       |
| eqBoletin             | int(11)          | YES  |     | 0       |       |
| eqPaseSalida          | int(11)          | YES  |     | 0       |       |
| eqMedioTx             | varchar(20)      | YES  |     | 0       |       |
| eqDigInputs           | int(11)          | YES  |     | 0       |       |
| eqFechaActEqLocal     | datetime         | YES  |     | NULL    |       |
| eqLatitudCruda        | decimal(9,6)     | YES  |     | NULL    |       |
| eqLongitudCruda       | decimal(9,6)     | YES  |     | NULL    |       |
| eqVelocidadCruda      | int(11)          | YES  |     | NULL    |       |
| eqIconoWeb            | varchar(20)      | YES  |     | car     |       |
| eqUsaAnalogicas       | tinyint(4)       | NO   |     | 0       |       |
| eqInstalado           | tinyint(4)       | YES  |     | 0       |       |
| eqAcuerdo             | tinyint(4)       | YES  |     | NULL    |       |
| idEntidad             | int(11)          | YES  |     | NULL    |       |
| eqFallaECM            | tinyint(4)       | YES  |     | 0       |       |
+-----------------------+------------------+------+-----+---------+-------+

我认为这个表(由触发器引用)也可能是相关的:

mysql> describe shadow.tbl_chip;
+--------------+-------------+------+-----+---------+----------------+
| Field        | Type        | Null | Key | Default | Extra          |
+--------------+-------------+------+-----+---------+----------------+
| Id           | int(11)     | NO   | PRI | NULL    | auto_increment |
| ProviderId   | int(11)     | NO   | MUL | NULL    |                |
| OwnerId      | int(11)     | YES  | MUL | NULL    |                |
| ChipState    | varchar(15) | NO   | MUL | Nuevo   |                |
| AreaCode     | varchar(3)  | NO   |     | NULL    |                |
| Phone        | varchar(7)  | NO   |     | NULL    |                |
| SerialNumber | varchar(45) | NO   |     | NULL    |                |
| PIN          | varchar(4)  | YES  |     | NULL    |                |
| PUK          | varchar(45) | YES  |     | NULL    |                |
+--------------+-------------+------+-----+---------+----------------+
9 rows in set (0.02 sec)

所以,基本上......只要通过命令行/mysql-workbench 发送查询,就会触发触发器,但不会在通过 yii(与两个数据库模式托管在同一服务器中)发送查询时触发。我看过以下内容:

根据MySQL 5.0 文档

MySQL 触发器仅由 SQL 语句激活。它们不会被不将 SQL 语句传输到 MySQL 服务器的 API 对表所做的更改激活;特别是,它们不会被使用 NDB API 所做的更新激活。

非常感谢任何帮助或指导。提前致谢。

编辑: Yii 使用 PDO 执行插入/更新语句,插入反映成功,更新仍然失败。

4

2 回答 2

1

我正在使用的触发器AFTER UPDATE有一个子句,它以某种方式阻止在触发器内进行更改(实际上总是被触发),

CREATE
DEFINER=`root`@`localhost`
TRIGGER `shadow`.`trg_device_after_update_reflect_changes`
AFTER UPDATE ON `shadow`.`tbl_device`
FOR EACH ROW
BEGIN
    DECLARE acuerdo TINYINT(4);
    DECLARE NumCel VARCHAR(10);
    DECLARE eqIdToUpdate INT;
    IF New.LastUpdated <> Old.LastUpdated THEN
        ...
    END IF;
END$$

似乎New.LastUpdated <> Old.LastUpdated从 Yii 更新时为假,但从 CLI 或 WorkBench 更新时为真。因为在 Yii 应用程序上我没有收到 tbl_device.LastUpdated 的输入(我期待 MySQL 为我完成这项工作,现在我觉得有点愚蠢,我可以使用beforeSave()更新时间戳字段的方法来纠正它我正在使用验证是否确实对记录进行了更改...)

于 2012-10-30T00:29:35.823 回答
0

首先:尝试使用您的 yii 应用程序使用的相同 mysql 用户从控制台/工作台调试它。

Yii 仍然使用常规 SQL 语句,而不是您引用的文档中提到的 api 调用。

继续并放入你的 config/main.php 文件

组件/数据库部分:

'enableParamLogging' => true

和组件/日志部分:

    'log' => array(
        'class' => 'CLogRouter',
        'routes' => array(
            array(
                'class'=>'CFileLogRoute',
                'levels'=>'trace,log',
                'categories' => 'system.db.CDbCommand',
                'logFile' => 'db.log',
            ),
        ),
    ),

然后观察在 protected/runtime/db.log 中执行的 sql 命令:

$ tail -f db.log

另外,您确定在更新主触发器更新影子触发器更新主触发器更新影子触发器时没有进入无限循环......?

于 2012-10-29T20:45:05.893 回答