How to embed an aggregate function in an event in SQL - mysql

I've recently begun learning SQL and currently working on a project tracking the progress of the vaccination rollout. I'm trying to make an event which will automatically tally up the number of patients who have received both vaccine doses at a certain time each day.
This is what I've got so far. The event should return the timestamp and total number of second doses given (as defined in the patient_vaccine_history table), and add these entries to the vaccinated_tally table.
Workbench is kindly telling me that "COUNT" is not valid in line 17.
SET GLOBAL event_scheduler = ON;
CREATE TABLE vaccinated_tally(
ID INT NOT NULL AUTO_INCREMENT,
last_update TIMESTAMP,
fully_vaccinated_tally INT,
PRIMARY KEY (ID));
DELIMITER //
CREATE EVENT daily_tally
ON SCHEDULE AT NOW() + INTERVAL 1 SECOND
DO BEGIN
INSERT INTO vaccinated_tally(last_update)
VALUES (NOW());
INSERT INTO vaccinated_tally(fully_vaccinated_tally)
VALUES COUNT(pvh.nhs_number) -- this is the problem line
FROM patient_vaccine_history pvh
WHERE pvh.dose = 2
);
END//
DELIMITER ;

You would seem to want insert . . . select:
INSERT INTO vaccinated_tally (fully_vaccinated_tally)
SELECT COUNT(pvh.nhs_number) -- this is the problem line
FROM patient_vaccine_history pvh
WHERE pvh.dose = 2;
You are not inserting the timestamp. Perhaps you have a trigger or other mechanism for setting it.

Related

How to update sql column based on previous column values

I have a table and its columns are id(int), order_id(int), partner_id(int)
Now I want to increment order_id based on partner_id ,
For Example
for partner 1, order_id's are 1,2,3
then if partner 2 is placing the order, the order_ids should start again from 1 and not from 4.
What is the best way to do so in mysql ? Should I read the last order id for a particular partner from db every time I want to create a new order ?
I am using mysql and sequelize
In a similar scenario for my case I 1st created a column on the order table called order_number.
Then created a mysql function to get the expected order_number base on parameter of the function and the order table.
Then on insert I just called the mysql function with appropriate parameter.
Here is what the mysql function looked like in my case:
Creating the function:
DELIMITER $$
CREATE FUNCTION `get_order_number`(partner_id VARCHAR(255)) RETURNS int(10)
BEGIN
DECLARE getCount INT(10);
SET getCount = (
SELECT COUNT(id)
FROM order_table
WHERE `partner_id` = partner_id
) + 1;
RETURN getCount;
END$$
DELIMITER ;
.
.
Implementation:
INSERT INTO (
id,
order_id,
pertner_id
) VALUES (
NULL,
get_order_number(10), -- 10 is partner_id
10 -- 10 is partner_id
);
.
.
Explanation of the function
1st pass partner_id as parameter for the function.
2nd inside the function I created a select query to get the number of how many orders have this partner created.
Finally adding one (1) with the returned number to get the last value.
.
.
Declaimer:
I have been using this function for over 3 years and have not got any issues yet. But this function has a weak point: When I submit the form simultaneously in two form/page at the same time (in millisecond) I get a deadlock error error for calling same function at the same time. Since I have moderate users I have not faced this error yet in real life situation.
I am using this function in plain mysql driver with node js, have not tested with sequelize.
Hope this helps.

MySQL Auto Update Date when any row value is updated

I have an existing table with values already filled in. Last column is of type date.
My requirement is that if any value is updated, that row's corresponding date column should be automatically updated to the current date.
For example, if the table is as follows:
and if I change any of the values of col1 (78) /col2 (nvvb) /col3 (566) of say row no. 2 on date 18Nov2018, then the col4 of row 2 should automatically update to today's date of 18Nov2018 from existing 13-Oct-17.
I've checked existing topics by could not find an answer.
The closest I can find is this code, but it is not working for the above requirement:
create table if not exists my_table (
index1 char(32) not null primary key,
title varchar(50),
my_timestamp timestamp not null default current_timestamp on update current_timestamp
)
Any help will be greatly appreciated.
Thanks
First, you need to create code blocks for whatever code you are displaying in your question/answer by highlighting the lines of code and pressing ctrl-k
You will need a trigger that changes the date value in the row to the current date using the CURDATE() function. BEFORE UPDATE simply means apply the current block enclosed in "BEGIN... END" before the actual update call is made by MySQL. You access the row to be updated with NEW.<field>. To store a value in a particular tuple, use the SELECT ... INTO .... statement
DELIMITER $$
CREATE TRIGGER trigger_name
BEFORE UPDATE ON table_name
FOR EACH ROW
BEGIN
SELECT CURDATE() INTO NEW.`col4(date)`;
END $$
DELIMITER ;
For more info, see: https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html
https://dev.mysql.com/doc/refman/8.0/en/ansi-diff-select-into-table.html
UPDATE: YOU CANNOT UPDATE WITHIN AN UPDATE TRIGGER, MySQL does not prevent an update trigger from being called by itself, nor does it prevent deadlock issues, so you cannot use this. Must use the method below.
Using the method described in the comments (if your version supports it):
CREATE TABLE last_mod (
last_modified_date DATE NOT NULL,
PRIMARY KEY (last_modified_date)
)
CREATE TABLE IF NOT EXISTS my_table (
my_date DATETIME NOT NULL,
INDEX par (my_date),
FOREIGN KEY (my_date) REFERENCES last_mod(mast_modified_date) ON UPDATE CASCADE
);
DELIMITER $$
CREATE TRIGGER trigger_name
BEFORE UPDATE ON my_table
FOR EACH ROW
BEGIN
UPDATE last_mod SET last_modified_date = CURDATE();
END $$
DELIMITER ;

Auto increment column by condition

I have a MySQL table like the following
ID - Project - Entry - Value
int int int varchar
ID is AUTO_INCREMENT. Now I want Entry to be AUTO_INCREMENT according to Project. Meaning for each record/row with Project=1, ENTRY is auto-incrementing. The requirement is that ENTRY is always + 1 according to the project.
Any ideas?
You need to write an BEFORE INSERT trigger to manage auto increment for project id, e.g.:
DELIMITER //
CREATE TRIGGER entry_trigger
BEFORE INSERT
ON table FOR EACH ROW
BEGIN
DECLARE entry int;
SELECT IFNULL(MAX(Entry), 0) + 1 INTO entry WHERE project = NEW.project;
SET NEW.Entry = entry;
END; //
DELIMITER ;
You can use mysql Triggers. A trigger is basically an SQL code executes just before or just after an INSERT, UPDATE or DELETE event occurs on a database table.
Using triggers, you can update the Entry field based on the value of the Project field.

MYSQL event on every 1 minute not working

First, this is OpenCart
I have two tables:
1. oc_product (product_id, model, price, event_start, event_end and etc.)
2. oc_product_to_category (product_id, category_id)
Every product has Start Date and End Date. I created MYSQL event that catch every product with expired date (event_end < NOW()) to store it in category "Archive" with id = 68
Here is the code of MYSQL EVENT
CREATE EVENT move_to_archive_category
ON SCHEDULE EVERY 1 MINUTE
STARTS NOW()
DO
INSERT INTO `oc_product_to_category` (product_id, category_id)
SELECT product_id, 68 as category_id
FROM oc_product p WHERE p.event_end < NOW() AND p.event_end <> '0000-00-00';
When event starts it works properly! BUT, when I got to administration and publish new product with expired date I'm waiting 1 minute to see the product in "Archive" category but nothing happens.
I saw in "SHOW PROCESSLIST" and everything is OK:
event_scheduler localhost NULL Daemon 67 Waiting for next activation NULL
and also "SHOW EVENTS" looks good
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation
events move_to_archive_category root#localhost SYSTEM RECURRING NULL 1 MINUTE 2016-08-15 13:37:54 NULL ENABLED 1 utf8 utf8_general_ci utf8_general_ci
I'm working locally, not live
Any ideas?
Thanks in advance! :)
I suggest turning on the sonar. I have 3 event links hanging off my profile page. So I created a few helper tables (that can also be seen in those links) to assist is turning on the sonar to see what is up in your events. Note you can expand on it for performance tracking as I did in those links.
Remember that Events succeed or fail (in your mind) based on the data and they do so silently. But tracking what is going on, you can vastly increase you happiness level when developing in them.
Event:
DROP EVENT IF EXISTS move_to_archive_category;
DELIMITER $$
CREATE EVENT move_to_archive_category
ON SCHEDULE EVERY 1 MINUTE STARTS '2015-09-01 00:00:00'
ON COMPLETION PRESERVE
DO
BEGIN
DECLARE incarnationId int default 0;
DECLARE evtAlias varchar(20);
SET evtAlias:='move_2_archive';
INSERT incarnations(usedBy) VALUES (evtAlias);
SELECT LAST_INSERT_ID() INTO incarnationId;
INSERT EvtsLog(incarnationId,evtName,step,debugMsg,dtWhenLogged)
SELECT incarnationId,evtAlias,1,'Event Fired, begin looking',now();
INSERT INTO `oc_product_to_category` (product_id, category_id)
SELECT product_id, 68 as category_id
FROM oc_product p WHERE p.event_end < NOW() AND p.event_end <> '0000-00-00';
-- perhaps collect metrics for above insert and use that in debugMsg below
-- perhaps with a CONCAT into a msg
INSERT EvtsLog(incarnationId,evtName,step,debugMsg,dtWhenLogged)
SELECT incarnationId,evtAlias,10,'INSERT finished',now();
-- pretend there is more stuff
-- ...
-- ...
INSERT EvtsLog(incarnationId,evtName,step,debugMsg,dtWhenLogged)
SELECT incarnationId,evtAlias,99,'Event Finished',now();
END $$
DELIMITER ;
Tables:
create table oc_product_to_category
( product_id INT not null,
category_id INT not null
);
create table oc_product
( product_id INT not null,
event_end datetime not null
);
drop table if exists incarnations;
create table incarnations
( -- NoteA
-- a control table used to feed incarnation id's to events that want performance reporting.
-- The long an short of it, insert a row here merely to acquire an auto_increment id
id int auto_increment primary key,
usedBy varchar(50) not null
-- could use other columns perhaps, like how used or a datetime
-- but mainly it feeds back an auto_increment
-- the usedBy column is like a dummy column just to be fed a last_insert_id()
-- but the insert has to insert something, so we use usedBy
);
drop table if exists EvtsLog;
create table EvtsLog
( id int auto_increment primary key,
incarnationId int not null, -- See NoteA (above)
evtName varchar(20) not null, -- allows for use of this table by multiple events
step int not null, -- facilitates reporting on event level performance
debugMsg varchar(1000) not null,
dtWhenLogged datetime not null
-- tweak this with whatever indexes your can bear to have
-- run maintenance on this table to rid it of unwanted rows periodically
-- as it impacts performance. So, dog the rows out to an archive table or whatever.
);
Turn on Events:
show variables where variable_name='event_scheduler'; -- OFF currently
SET GLOBAL event_scheduler = ON; -- turn her on
SHOW EVENTS in so_gibberish; -- confirm
Confirm Evt is firing:
SELECT * FROM EvtsLog WHERE step=1 ORDER BY id DESC; -- verify with our sonar
For more details of those helper tables, visit those links off my profile page for Events. Pretty much just the one link for Performance Tracking and Reporting.
You will also note that it is of no concern at the moment of having any data in the actual tables that you were originally focusing on. That can come later, and can be reported on in the evt log table by doing a custom string CONCAT into a string variable (for the counts etc). And reporting that in a step # like step 10 or 20.
The point is, you are completely blind without something like this as to know what is going on.
So,
I saw in mysqlog the following errors
160816 10:18:00 [ERROR] Event Scheduler: [root#localhost][events.move_to_archive_category] Duplicate entry '29-68' for key 'PRIMARY'
160816 10:18:00 [Note] Event Scheduler: [root#localhost].[events.move_to_archive_category] event execution failed.
and I just add INGORE in SQL INSERT... so the finally result is
INSERT IGNORE INTO `oc_product_to_category` (product_id, category_id)

How to display result and or any message in trigger body?

Hi I want to create trigger if it's condition satisfy then it's body should be executed and I want to display some message or any data that should be displayed if trigger body executed.
I want that if quantity of product went less then 50 then it should display message or some data.
Is it possible to display message ?
Here testdata is table name.
Code :
delimiter //
create trigger trigger2 before update on test.testdata
for each row
begin
if new.qty < 50 then
**display here some message that quantity is less**
end if;
end;
//
delimiter ;
You cannot do it, there is no place to output them in MySQL. As a work around you can add your message to the table, and then read this table.
Short example -
CREATE TABLE table1 (
column1 VARCHAR(255) DEFAULT NULL
);
CREATE TABLE messages (
id INT(11) NOT NULL AUTO_INCREMENT,
message VARCHAR(255) DEFAULT NULL,
time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
DELIMITER $$
CREATE TRIGGER trigger1
AFTER INSERT
ON table1
FOR EACH ROW
BEGIN
INSERT INTO messages(message) VALUES ('new action');
END
$$
DELIMITER ;
Also, you could use UDF function write your logic.
More information - CREATE FUNCTION Syntax for User-Defined Functions.
For Quick and plain answering: You cannot Display Messages From Triggers. You may Only Throw errors.
You are propably not knowing the reason u use triggers in databases assuming from your question. We all have passed that level so dont worry. U have understood the syntax when we use triggers but not how and what they can do and not.
A trigger will do (for your case BEFORE an UPDATE) something concerning the database and only that.
That means the trigger cannot display any message on your screen. You can only handle database staff and not all of the actions are allowed for that too or some actions arent even recommended!.
That is for the theory part.
To give you a solution to your problem now.
The only thing you can do to know when the trigger has worked (that means when the new.qua<50) or basically check anything with any other trigger is the following. (Just a small fast solution):
You need to create a Table that will handle all logging of the
triggers.
Add in it an ID field, a descr field that will hold the action of
the triggerex. BefUpdate, BefInsert etc. another field for the
propably the condition that triggered the logging and antyhing else
u want displayed later in the application.
Then inside the if condition u are using write and insert
statemement to fill the info in the new (logging) table.
in your app later select that logging table to see the messages.
That is a useful and fast way to log, not only triggers but also functions (stored procs).
Judt for reference i give you s sample code with the CREATE, and the INSERT statement for your trigger.
CREATE TABLE LOGGER (
ID BIGINT PRIMARY KEY AUTO_INCREMENT,
DESCR_ACTIVITY VARCHAR(10),
ACTIVITY VARCHAR(50),
DT TIMESTAMP,
CONDITIONVALUE VARCHAR(50)
)
In the IF of your code now make it as :
if new.qty < 50 then
INSERT INTO LOGGER VALUES ('BEFINS','CHECKING QUA',NULL,'QUANTITY IS LOWER THAN 50')
end if;
And even from the workbench or from your application u can just :
SELECT * FROM LOGGER
to see the loggings.
But if i am confused from the reading and you want just to throw an error u can read the Mysql Documentation concerning throwing errors:
enter link description here
What u can do is in your if condition write something like:
if new.qty < 50 then
SIGNAL SQLSTATE '01000' SET MESSAGE_TEXT = 'Lower than 50', MYSQL_ERRNO = 1000;
endif;
What u should always NOT DO is alter the same table that a trigger is assigned and use only small portion of not so complex code in the trigger.
Hope i helped a bit.
Also, you can display any message using the select command.
IF (NEW.qty < 50) THEN
SELECT "message that quantity is less" AS Output;
END IF
Place above code inside the trigger. It will print the output