mysql trigger work not always - mysql

I try to insert trigger to mysql table of 3Th party windows program.
In original it was seample table:
CREATE TABLE `data` (
`id` int(11) DEFAULT NULL,
`dt` datetime DEFAULT NULL,
`x` double DEFAULT NULL,
`y` double DEFAULT NULL,
`sent` tinyint(4) DEFAULT NULL,
KEY `fast` (`dt`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
My goal is mark a data, inserted from my php scripts. For this purpose i create 2 new field in table.
`sent` tinyint(4) DEFAULT NULL,
`sourceColumn` int(11) DEFAULT NULL,
sent - for marking is data alredy used or not, and sourceColumn - to mark insertion reason.
sometimes i need to set send=99 in ponts, are inserted of 3Th party windows program. For this i create trigger:
CREATE
DEFINER=`root`#`%`
TRIGGER `abc`.`data`
BEFORE INSERT ON `abc`.`data`
FOR EACH ROW
BEGIN
IF (EXISTS (select ID FROM OrigLock where id=NEW.id AND NEW.dt between date_start AND date_stop) ) THEN
SET NEW.sent=99;
END IF;
END
$$
This work fine, but some times approximaly 1 of 1000 in table exist rows, where dt inside interval date_start AND date_stop and sent=NULL and sourceColumn=NULL, i.e. looks like rows inserted Windows programm without trigger.
Have any ideas on how data can get to the table, without processing the trigger?
OrigLock Structure:
enter code here
CREATE TABLE `OrigLock` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`DataID1` int(11) NOT NULL,
`DataID2` int(11) NOT NULL,
`date_start` datetime DEFAULT NULL,
`date_stop` datetime DEFAULT NULL,
PRIMARY KEY (`ID`,`DataID1`)
) ENGINE=InnoDB AUTO_INCREMENT=450 DEFAULT CHARSET=latin1

Related

Get data of table with some data from other table

I have a simple table visitor and another table visitor_tokens.
SQL creation script of visitor:
CREATE TABLE `visitor` (
`id` int(7) UNSIGNED NOT NULL,
`phone` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
ALTER TABLE `visitor`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `phone_index` (`phone`);
ALTER TABLE `visitor`
MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
INSERT INTO `visitor` (`phone`) VALUES
('111111111');
SQL creation script of visitor_tokens:
CREATE TABLE `visitor_tokens` (
`id` int(7) UNSIGNED NOT NULL,
`visitor` int(7) UNSIGNED NOT NULL,
`token` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
ALTER TABLE `visitor_tokens`
ADD PRIMARY KEY (`id`);
ALTER TABLE `visitor_tokens`
MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT;
INSERT INTO `visitor_tokens` (`visitor`, `token`) VALUES
(1, 'abc_token'),
(1, 'xyz_token');
I want to get some data of visitor by id:
I want to get the phone number (phone column) and the visitor tokens (visitor_tokens.token). All - according to given id.
My current SQL script is: SELECT visitor.phone, visitor_tokens.token FROM visitor JOIN visitor_tokens ON visitor_tokens.visitor=visitor.id WHERE id=1. This gives me only the phone and the first token: abc. But I also want to get the tokens of the visitor from the second table. To get something like [abc_token, xyz_token]. How can I do it?
So this is my solution (also recommend on reading the main comments):
SELECT visitor.phone, GROUP_CONCAT(visitor_tokens.token) AS tokens FROM visitor LEFT JOIN visitor_tokens ON visitor_tokens.visitor=visitor.id WHERE visitor.id=1 LIMIT 1;
It returns result regardless of missing visitor tokens (LEFT JOIN), and also if there are tokens - it returns the tokens separated by comma in one row.

Is there a way to use a MySQL trigger to duplicate a record in a table, but modify the data before insert?

I am applying a series of triggers to a few tables in our database - these will serve the purpose of creating soft-delete/shadow records when certain relevant data is updated.
An example, consider this table:
CREATE TABLE `cms_content` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`tag` varchar(20) NOT NULL,
`data` text NOT NULL,
`is_active` tinyint(1) unsigned NOT NULL DEFAULT '0',
`created_by` int(10) unsigned DEFAULT NULL,
`updated_by` int(10) unsigned DEFAULT NULL,
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT=' ';
Using this query
UPDATE cms_content SET `tag` = 'user-cities-field' WHERE id = 3;
When this query is executed, I want to essentially perform the following manipulations:
Set the INSERT id of the incoming record to NULL, so that AUTO_INCREMENT will set the next value accordingly.
Insert a copy of the record that was updated, with the only modification being that the is_active flag needs to get set to 1
I know that you cannot update the table from within here as MySQL will block it to prevent recursion.
If there is a way, can you essentially select the record being updated, prior to being updated, and then INSERT the modified record?
Would it be more useful to use a stored function to handle this?
What I need to do is to select the OLD record pre-update, and then change the value of the is_active flag on that record before it is inserted. The code I have been working with is below.
Trigger
CREATE DEFINER=`root`#`localhost` TRIGGER `cms_content_AFTER_UPDATE` BEFORE UPDATE ON `cms_content` FOR EACH ROW BEGIN
IF (NEW.is_active = 1) THEN
SET NEW.id = NULL;
INSERT INTO `tester`.`cms_content` (SELECT *, 0 as is_active from `cms_content` WHERE `id` = OLD.`id`);
#UPDATE `tester`.`cms_content` SET is_active = 0 WHERE `id` = OLD.`id`;
END IF;
END

How to make a trigger in mysql that I update the data of a bitmap when ejecting another trigger from insert

I am working on a project in php where I have to do audit of the database of all the changes. I have a table where the table is stored changes of a person table
CREATE TABLE `personas` (
`cedula` varchar(13) NOT NULL,
`nombres` varchar(30) NOT NULL,
`apellidos` varchar(20) NOT NULL,
`sexo` varchar(10) NOT NULL,
`telefono` varchar(20) NOT NULL,
`direccion` text NOT NULL,
`fnacimiento` date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And I have a users table
CREATE TABLE `usuarios` (
`id` int(10) NOT NULL,
`correo` varchar(80) DEFAULT NULL,
`usuario` varchar(10) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`last_session` datetime DEFAULT NULL,
`activacion` int(15) NOT NULL DEFAULT '0',
`token` varchar(40) NOT NULL,
`token_password` varchar(100) NOT NULL,
`password_request` int(11) NOT NULL DEFAULT '0',
`id_tipo` varchar(20) DEFAULT NULL,
`usuarios_cedula` varchar(13) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The logbook is as follows
CREATE TABLE `bitacorapersonas` (
`idBitacora` int(20) NOT NULL,
`accion` varchar(20) NOT NULL,
`fecha` datetime NOT NULL,
`cedUsuario` varchar(20) DEFAULT NULL,
`nombreUsuario` varchar(13) NOT NULL,
`tipoUsuario` varchar(15) NOT NULL,
`cedula` varchar(13) NOT NULL,
`nombreNuevo` varchar(25) DEFAULT NULL,
`nombreViejo` varchar(20) DEFAULT NULL,
`apellidoNuevo` varchar(30) NOT NULL,
`apellidoViejo` varchar(30) DEFAULT NULL,
`sexoNuevo` varchar(10) NOT NULL,
`sexoViejo` varchar(10) DEFAULT NULL,
`telefonoNuevo` varchar(15) NOT NULL,
`telefonoViejo` varchar(15) DEFAULT NULL,
`direccionNuevo` varchar(40) NOT NULL,
`direccionViejo` varchar(40) DEFAULT NULL,
`fnacimientoNuevo` date NOT NULL,
`fnacimientoViejo` date DEFAULT NULL,
`usuarioNuevo` varchar(15) NOT NULL,
`correoNuevo` varchar(15) NOT NULL,
`correoViejo` varchar(15) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
In the people table and the user table I have a trigger for each one. But I am presenting the following: when I enter a person the data are divided into two rows apart instead of placing them in a single (for being the same person that I am entering)
The triggers are as follows
CREATE TRIGGER `Personas_AInsertar` AFTER INSERT ON `personas`
FOR EACH ROW
INSERT INTO bitacorapersonas (accion,fecha,cedUsuario, nombreUsuario,tipoUsuario,cedula,nombreNuevo,apellidoNuevo,sexoNuevo,telefonoNuevo,direccionNuevo,fnacimientoNuevo)
VALUES ('Inserto',NOW(),#identificador,#identificador2,#identificador3,NEW.cedula,NEW.nombres,NEW.apellidos,NEW.sexo,NEW.telefono, NEW.direccion,NEW.fnacimiento)
CREATE TRIGGER `Usuarios_AInsertar` AFTER INSERT ON `usuarios`
FOR EACH ROW
INSERT INTO bitacorapersonas (accion,fecha,cedUsuario, nombreUsuario,tipoUsuario, usuarioNuevo,correoNuevo)
VALUES ('Inserto',NOW(),#identificador,#identificador2,#identificador3,NEW.usuario,NEW.correo)
At the time of registering a person I am saving it as follows in the table bitacora
I thought of doing an update trigger in the bitacora so that when user data is logged and the trigger is triggered, the fill is filled with the same fields that are being entered by the person in the fields that are empty on the left side but I have no idea how to do it. If someone can help me or give me some idea how to do it I would appreciate it
Assuming usuarios_cedula is a foreign key to personas.cedula, you can do this in the second trigger:
CREATE TRIGGER `Usuarios_AInsertar` AFTER INSERT ON `usuarios`
FOR EACH ROW
UPDATE bitacorapersonas
SET usuarioNuevo = NEW.usuario, correoNuevo = NEW.correo
WHERE cedula = NEW.usuarios_cedula
This will add these two columns to the row that was inserted by the trigger on personas.
This might do the trick:
Define a key
Change your TRIGGERS to have ON DUPLICATE KEY UPDATE.
And take care that this is running in a transaction.
By having both check on the key, the order of the actions does not have to be guaranteed.
CREATE TRIGGER `Personas_AInsertar` AFTER INSERT ON `personas`
FOR EACH ROW
INSERT INTO bitacorapersonas (accion,fecha,cedUsuario, nombreUsuario,tipoUsuario,cedula,nombreNuevo,apellidoNuevo,sexoNuevo,telefonoNuevo,direccionNuevo,fnacimientoNuevo)
VALUES ('Inserto',NOW(),#identificador,#identificador2,#identificador3,NEW.cedula,NEW.nombres,NEW.apellidos,NEW.sexo,NEW.telefono, NEW.direccion,NEW.fnacimiento) ON DUPLICATE KEY UPDATE....
CREATE TRIGGER `Usuarios_AInsertar` AFTER INSERT ON `usuarios`
FOR EACH ROW
INSERT INTO bitacorapersonas (accion,fecha,cedUsuario, nombreUsuario,tipoUsuario, usuarioNuevo,correoNuevo)
VALUES ('Inserto',NOW(),#identificador,#identificador2,#identificador3,NEW.usuario,NEW.correo) ON DUPLICATE KEY UPDATE ....
If you know that the order of execution is fixed, you can make your live easier by running an INSERT in the first trigger and an UPDATE in the second trigger:
CREATE TRIGGER `Personas_AInsertar` AFTER INSERT ON `personas`
FOR EACH ROW
INSERT INTO bitacorapersonas (accion,fecha,cedUsuario, nombreUsuario,tipoUsuario,cedula,nombreNuevo,apellidoNuevo,sexoNuevo,telefonoNuevo,direccionNuevo,fnacimientoNuevo)
VALUES ('Inserto',NOW(),#identificador,#identificador2,#identificador3,NEW.cedula,NEW.nombres,NEW.apellidos,NEW.sexo,NEW.telefono, NEW.direccion,NEW.fnacimiento) ON DUPLICATE KEY UPDATE....
CREATE TRIGGER `Usuarios_AInsertar` AFTER INSERT ON `usuarios`
FOR EACH ROW
UPDATE bitacorapersonas
SET ....
About security of the actual keeping of a log like this:
If you do not protect your log and your data with a checksum of some kind, the data can still be altered in the log in such a way that you can not determine reality. By adding a checksum methodology, you have easier and more portable proof of tampering with the data.

mySQL trigger fails on UPDATE statement

I have two tables and one trigger. The trigger fails on the UPDATE on table sensors. I have tested the trigger updating another table and that just works fine so I expect this to be a problem with locking on sensors. I'm certainly not an expert on mySQL and I did some searching. I have tried to add SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; before the first SELECT in the trigger but that did not make any difference.
Table measurements:
CREATE TABLE `measurements` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sensorid` int(16) DEFAULT NULL,
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`distance` int(11) NOT NULL,
`temperature` float DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=26727 DEFAULT CHARSET=latin1;
Table sensors:
CREATE TABLE `sensors` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` char(32) DEFAULT '',
`zeropoint` int(11) unsigned DEFAULT NULL,
`threshold` int(11) unsigned DEFAULT NULL,
`hysteresis` int(11) DEFAULT NULL,
`status` enum('normal','alarm') DEFAULT 'normal',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
Trigger raise alarm:
CREATE TRIGGER `raise alarm` BEFORE INSERT ON `measurements`
FOR EACH ROW
begin
declare zp integer;
declare st char(32);
select zeropoint into zp from sensors where id = new.sensorid;
select status into st from sensors where id = new.sensorid;
if new.distance > zp then
if st = 'normal' then
update sensors set status = 'alarm' where id = new.sensorid;
end if;
end if;
end;
There is something in the documentation that you might be interested in:
https://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html
In a BEFORE trigger, the NEW value for an AUTO_INCREMENT column is 0,
not the sequence number that is generated automatically when the new
row actually is inserted.
It means, that all queries in your trigger are always looking for a record with id=0, since id column in measurement table is auto-increment.
In case there is no record id=0 in sensors table, then zp variable is null, and this condition: if new.distance > zp then is always false.

Create trigger to update column value when update or insert happens in the table

I am new to writing mysql triggers.
here is the create sql statement
CREATE TABLE BRAND_PROFILE_MAPPING (
PROFILE_MAPPING_ID BIGINT(20) NOT NULL AUTO_INCREMENT,
DOTLOOP_PROFILE_ID BIGINT(20) NULL DEFAULT NULL,
BRAND_PROFILE_ID VARCHAR(255) NULL DEFAULT NULL,
PROFILE_TYPE VARCHAR(90) NULL DEFAULT NULL,
PROFILE_NAME VARCHAR(225) NULL DEFAULT NULL,
SOURCE VARCHAR(255) NULL DEFAULT NULL,
PROFILE_HASH VARCHAR(32) NULL DEFAULT NULL,
ENTERED_DATE DATETIME NULL DEFAULT NULL,
CHANGED_DATE DATETIME NULL DEFAULT NULL,
PRIMARY KEY (PROFILE_MAPPING_ID)
);
i created table based on above structure.here i have 2 fields ENTERED_DATE DATETIME
CHANGED_DATE DATETIME , date columns i want to write trigger if any insert operation both date fields should be inserted if there is any update operation,changed date field should be updated.
Appreciate your help.Thanks in advance!!.
Remove profile_mapping_id from list of values as it is auto incremental.
CREATE TRIGGER `brand_profile_mapping_trigger` AFTER INSERT ON brand_profile_mapping FOR EACH ROW
BEGIN insert into **brand_profile_mapping_log** values(NEW.dotloop_profile_id,NEW.brand_profile_id,NEW.profile_type,NEW.profile_name,NEW.source,NEW.profile_hash);
END
If you want both columns to be populated by triggers you can do it in the following way
CREATE TRIGGER brand_profile_mapping_before_insert_trigger
BEFORE INSERT ON brand_profile_mapping -- you have to use BEFORE event
FOR EACH ROW
SET NEW.entered_date = CURRENT_DATE,
NEW.changed_date = CURRENT_DATE;
CREATE TRIGGER brand_profile_mapping_before_update_trigger
BEFORE UPDATE ON brand_profile_mapping -- you have to use BEFORE event
FOR EACH ROW
SET NEW.entered_date = OLD.entered_date, -- preserve the original value, so it can't be overridden
NEW.changed_date = CURRENT_DATE;
Here is a SQLFiddle demo