time difference calculation in sql - mysql

Hello I'm trying to create a trigger(or more) in order to calculate the time difference in a mysql table (I'm using mysql 5.7.21).
The table I have is this :
CREATE TABLE people(
username VARCHAR(255) NOT NULL,
state VARCHAR(255) NOT NULL DEFAULT 'not active',
PRIMARY KEY(username)
);
Some explanation about the table : The username column is self-explanatory and the state column get only 1 out of 3 values : 'active', 'not active','working' .
Specifically a person can change from 'active' to 'not active'/'working' and vice-versa but cannot change from 'not active' to 'working' or from 'working' to 'not active'.
What I want to do is to keep track of how much time has a person been active/working for.
Now my idea is have a table like this :
Create table Time(
username VARCHAR(255) NOT NULL,
timestarted TIME,
timeended TIME,
timeworking TIME,
FOREIGN KEY (username) REFERENCES people(username)
);
My first guess was to use the CURRENT_TIME() function to keep track of state using triggers.
What I did is :
Delimiter $$
CREATE TRIGGER time_calculation
BEFORE Update
ON people
FOR EACH ROW BEGIN
DECLARE #times time;
DECLARE #timee time;
DECLARE #timet time;
IF OLD.state = 'not active' THEN
UPDATE Time SET timestart = CURRENT_TIME() WHERE username = new.username;
END IF;
IF NEW.state = 'not active' THEN
set #times = (select timestarted from time where username = new.username);
set #timee = (select timeended from time where username = new.username);
set #timet = (select timeworking from time where username = new.username);
UPDATE Time SET timeend = CURRENT_TIME(),timeworking = (TIMEDIFF(times,timee)+timet) WHERE username = new.username;
END IF;
END$$
According to this trigger. Every time someone switches from 'not active' to 'active' the Time table will get the current time of that switch as timestart.When someone switches to 'not active' the Time table will get the current time as timeend and it will add the difference between timestart and timeend to timeworking. The trigger shows the following error :
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE #times time;
DECLARE #timee time;
DECLARE #timet time;
BEGIN
IF OLD.state =' at line 5
Don't know what's wrong with it. Any ideas?

Well I found a way to solve this problem although it wasn't as I expected it. I made some changes in the table structure as follows :
Create table people (
username VARCHAR(255) NOT NULL,
Primary Key (username)
);
Create table characteristics (
username VARCHAR(255) NOT NULL,
state VARCHAR(25) NOT NULL DEFAULT 'not active',
timestart TIME,
timestop TIME,
timetotal TIME NOT NULL DEFAULT '00:00:00',
FOREIGN KEY(username) REFERENCES people(username)
);
As for the trigger to get the time difference it's as follows :
Delimiter $$
CREATE TRIGGER time_calculation
BEFORE Update
ON characteristics
FOR EACH ROW BEGIN
IF OLD.state = 'not active' && NEW.state != 'not active' THEN
SET NEW.timestart = CURRENT_TIME();
END IF;
IF NEW.state = 'not active' && OLD.state != 'not active' THEN
SET NEW.timestop = CURRENT_TIME();
SET NEW.timetotal = ADDTIME(OLD.timetotal,TIMEDIFF(NEW.timestop, OLD.timestart));
END IF;
END$$
Delimiter ;
Hope this helps anyone having the same problem with me !

Related

Changing the value of a field from a table when another is changed using triggers

Good morning everyone.
I have a table like this in mysql:
CREATE TABLE NC
(
ID INTEGER AUTO_INCREMENT NOT null,
reportingDate DATE,
closingDate DATE,
State VARCHAR(20),
PRIMARY KEY(CodNC)
)
What I need is to automatically changing the value of the field "ClosingDate" up to today everytime the value of the field "State" is changed with "Closed" using TRIGGERS.
Thanks
This looks like a simple before update trigger:
delimiter //
create trigger trg_nc_before_state_update
before update on nc
for each row
begin
if new.state = 'Closed' and not old.state <=> 'Closed' then
set new.closingdate = current_date;
end if;
end
//
delimiter ;

How to get column's DEFAULT in trigger?

I'd like one column's value to be forced to DEFAULT in trigger by some conditions.
In example below, if b IS NULL, a should be set with defined DEFAULT (that is 'SYSTEM').
CREATE TABLE `t_default` (
`n` VARCHAR(50) NOT NULL
, `a` VARCHAR(50) NOT NULL DEFAULT 'SYSTEM'
, `b` VARCHAR(50) NULL DEFAULT NULL
-- To moderators: please do not edit my formatting :)
);
DELIMITER //
CREATE TRIGGER `TRG_t_default_BeforeInsert` BEFORE INSERT ON `t_default` FOR EACH ROW BEGIN
IF NEW.b IS NULL THEN
SET NEW.a = DEFAULT(a);
END IF;
END
//
DELIMITER ;
Unfortunately, this gives error message "Unknown column 'a' in 'field list'" on insert:
INSERT INTO t_default (n) VALUES ('1');
I've found similar question, however providing table name or db + table name does not work as well:
CREATE TRIGGER `TRG_t_default_BeforeInsert` BEFORE INSERT ON `t_default` FOR EACH ROW BEGIN
IF NEW.b IS NULL THEN
SET NEW.a = DEFAULT(t_default.a);
-- SET NEW.a = DEFAULT(test.t_default.a);
END IF;
END
In this case message is slightly different: "Unknown table 't_default' in field list"
Also, I tried to use back quotes around column name with no success.
So, is it possible to get column DEFAULT in trigger at all? I'm using MySQL 5.7.
Thanks.
P.S. Sure, I know that I can do SET NEW.a = 'SYSTEM';
You can try
IF NEW.b IS NULL THEN
SELECT COLUMN_DEFAULT INTO #def FROM information_schema.COLUMNS
WHERE table_schema = 'database_name'
AND table_name = 't_default'
AND column_name = 'a';
SET NEW.a = #def;
END IF;

Check if a column is null or not before inserting another record in SQL

I need to check first if the EndTime column in my table is null or not before I can insert another record. If the Endtime column is not null than a new record can be inserted else an error must be thrown. I'm not sure how to create the error in SQL.
This is what I tried but it doesn't work
ALTER PROCEDURE [dbo].[AddDowntimeEventStartByDepartmentID]
(#DepartmentId int,
#CategoryId int,
#StartTime datetime,
#Comment varchar(100) = NULL)
AS
BEGIN TRY
PRINT N'Starting execution'
SET #StartTime = COALESCE(#StartTime, CURRENT_TIMESTAMP);
INSERT INTO DowntimeEvent(DepartmentId, CategoryId, StartTime, EndTime, Comment)
WHERE EndTime = NULL
OUTPUT
inserted.EventId, inserted.DepartmentId,
inserted.CategoryId, inserted.StartTime,
inserted.EndTime, inserted.Comment
VALUES(#DepartmentId, #CategoryId, #StartTime, NULL, #Comment)
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER(),ERROR_MESSAGE()
END CATCH
Here is my table:
CREATE TABLE [dbo].[DowntimeEvent](
[EventId] [int] IDENTITY(0,1) NOT NULL,
[DepartmentId] [int] NOT NULL,
[CategoryId] [int] NOT NULL,
[StartTime] [datetime] NOT NULL,
[EndTime] [datetime] NULL,
[Comment] [varchar](100) NULL,
)
You could use the INSERT...SELECT syntax instead of INSERT...VALUES to be able to use a WHERE clause (with a different condition to the one you tried to use, see below), then check the number of affected rows and raise an error if it is 0:
...
BEGIN TRY
...
INSERT INTO DowntimeEvent
...
SELECT #DepartmentId, #CategoryId, #StartTime, NULL, #Comment
WHERE NOT EXISTS (
SELECT *
FROM dbo.DowntimeEvent
WHERE DepartmentId = #DepartmentId
AND CategoryId = #CategoryId
AND EndTime IS NULL
);
IF ##ROWCOUNT = 0
RAISERROR ('A NULL row already exists!', 16, 1)
;
END TRY
BEGIN CATCH
...
END CATCH;
(Of course, you will need to omit your WHERE clause as invalid Transact-SQL.)
If you want a prevention mechanism at the database level rather than just in your stored procedure, so as to be able to prevent invalid additions from any caller, you may want to consider a trigger.
A FOR INSERT trigger like this would check if new rows violate the rule "Do not add rows newer than the existing NULL row" (as well as "Do not add older rows with empty EndTime") and roll back the transaction if they do:
CREATE TRIGGER DowntimeEvent_CheckNew
ON dbo.DowntimeEvent
FOR INSERT, UPDATE
-- do nothing if EndTime is not affected
IF NOT UPDATE(EndTime)
RETURN
;
-- raise an error if there is an inserted NULL row
-- older than another existing or inserted row
IF EXISTS (
SELECT *
FROM dbo.DowntimeEvent AS t
WHERE t.EndTime IS NULL
AND EXISTS (
SELECT *
FROM inserted AS i
WHERE i.DepartmentId = t.DepartmentId
AND i.CategoryId = t.CategoryId
AND i.StartTime >= t.StartTime
)
)
BEGIN
RAISERROR ("An attempt to insert an older NULL row!", 16, 1);
ROLLBACK TRANSACTION;
END;
-- raise an error if there is an inserted row newer
-- than the existing NULL row or an inserted NULL row
IF EXISTS (
SELECT *
FROM inserted AS i
WHERE i.EndTime IS NULL
AND EXISTS (
SELECT *
FROM dbo.DowntimeEvent AS t
WHERE t.DepartmentId = i.DepartmentId
AND t.CategoryId = i.CategoryId
AND t.StartTime >= i.StartTime
)
)
BEGIN
RAISERROR ("An older NULL row exists!", 16, 1);
ROLLBACK TRANSACTION;
END;
Note that merely issuing ROLLBACK TRANSACTION in a trigger already implies raising a level 16 error like this:
Msg 3609, Level 16, State 1, Line nnn
The transaction ended in the trigger. The batch has been aborted.
so, you may not need your own. There would be a difference in the meaning of Line nnn between the message above and the one brought by your own RAISERROR, however: the line number in the former would refer to the location of the triggering statement, whereas the line number in the latter would refer to a position in your trigger.

mysql stored function simple login, extract int from set

this is my first mySQL stored-function 'cause i've always managed such things with php.
I want a function that checks if a user can log in my client-area.
I wrote the code above:
DELIMITER $$
CREATE FUNCTION `A05`.`login` (user VARCHAR(64),pass VARCHAR(64)) RETURNS INT
BEGIN
declare num_rows int;
declare id int;
SELECT (#num_rows:=COUNT(*)),(#id:=`Credential_id`) FROM `A05`.`Credentials` where `Credential_UserName` = user;
if num_rows = 0 then
-- user not present
return (-1);
end if;
-- user present, checking password
SELECT (#num_rows:=COUNT(*)),(#id:=`Credential_id`) FROM `A05`.`Credentials` where `Credential_id` = id AND `Credential_PASSWORD` = SHA1(pass);
if num_rows = 0 then
-- user presente, password incorrect
INSERT INTO `a05`.`Events` (
`Event_id` ,
`Event_RegistrationDate` ,
`Event_VariationDate` ,
`Event_State`,
`Event_Notes`,
`Event_SenderId`,
`Event_Type`
)
VALUES (
NULL , NOW(), NOW(), 'wp', NULL, id, 1
);
return (-2);
end if;
-- user present, password correct
UPDATE `A05`.`Credentials` SET `Credential_LastAccess`=NOW() where `Credential_id` = id;
INSERT INTO `a05`.`Events` (
`Event_id` ,
`Event_RegistrationDate` ,
`Event_VariationDate` ,
`Event_State`,
`Event_Notes`,
`Event_SenderId`,
`Event_Type`
)
VALUES (
NULL , NOW(), NOW(), 'ok', NULL, id, 0
);
return id;
END
I think that the syntax should be right except for the last statement return id cause i return a set instead of an integer.
The problem is that when i try to store this function on mysql i get this error:
Error 1415: Not allowed to return a result set from a function
Then i changed the last statement from 'return id' to 'return 0' for testing purpose but i keep getting the same error.
Probably is a newbie error cause it's the very first time for me on sql "advanced" scripting.
Thank you very much in advance!

Compare dates in MySQL trigger

I have two tables, round and event. One round has many events.
create table round (
round_id INTEGER AUTO_INCREMENT NOT NULL,
round_start_date DATETIME NOT NULL,
round_end_date DATETIME NOT NULL,
CONSTRAINT round_pk PRIMARY KEY (round_id)
);
create table event (
event_id INTEGER AUTO_INCREMENT NOT NULL,
round_id INTEGER NOT NULL,
event_date DATETIME NOT NULL,
CONSTRAINT event_pk PRIMARY KEY (event_id),
CONSTRAINT round_fk FOREIGN KEY (round_id) REFERENCES round (round_id),
);
When a row is inserted into the event table, I want to use a trigger to compare the event_date field of the newly inserted row to the round_start_date and round_end_date fields in its corresponding entry in the round table. If event_date is earlier than round_start_date, round_start_date should be updated with the new event_date. If event_date is after round_end_date, round_end_date should be updated with the new event_date.
This is my trigger. It does not work, and I do not understand why. I cannot find anywhere on the web where anyone else has tried to use a datetime type in a trigger, so I have no frame of reference for where I am going wrong.
create trigger update_round_date
after insert on event for each row
begin
declare curSdate datetime;
declare curEdate datetime;
set curSdate = (select round_start_date from round where round_id = NEW.round_id);
set curEdate = (select round_end_date from round where round_id = NEW.round_id);
if (NEW.event_date < curSdate) then
update round set round_start_date = NEW.event_date where round_id = NEW.round_id;
else if (NEW.event_date > curEdate) then
update round set round_end_date = NEW.event_date where round_id = NEW.round_id;
end if;
end;
EDIT: I simply can't create the trigger. phpMyAdmin gives me this error:
"#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 4"
EDIT 2: Updated with a delimiter set
DELIMITER $$
create trigger update_round_date
after insert on event for each row
begin
declare curSdate datetime;
declare curEdate datetime;
set curSdate = (select round_start_date from round where round_id = NEW.round_id);
set curEdate = (select round_end_date from round where round_id = NEW.round_id);
if (NEW.event_date < curSdate) then
update round set round_start_date = NEW.event_date where round_id = NEW.round_id;
else if (NEW.event_date > curEdate) then
update round set round_end_date = NEW.event_date where round_id = NEW.round_id;
end if;
end$$
This returns the error: "#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 13"
MySQL is probably stopping at the first ';', interpreting your command as:
create trigger update_round_date
after insert on event for each row
begin
declare curSdate datetime;
You have to set your delimiter to something else first, then terminate the create trigger command with that delimiter instead (and put the delimiter back at the end):
delimiter ^
create trigger update_round_date
after insert on event for each row
begin
...
end;
^
delimiter ;
I believe the last semicolon after end may be necessary.
There may be a problem with delimiters in phpmyadmin, try to use this trigger -
CREATE TRIGGER trigger1
AFTER INSERT
ON event
FOR EACH ROW
UPDATE
round
SET
round_start_date =
IF(NEW.event_date < round_start_date, NEW.event_date, round_start_date),
round_end_date =
IF(NEW.event_date > round_end_date, NEW.event_date, round_end_date)
WHERE
round_id = NEW.round_id;