I have a database in SQL which contains information about students and books they are borrowing.
I have a table of loans which includes the book_id, the student_id, the day the book was taken and the number of days the book can be kept.
I want to auto-generate the return_date by adding the days at the date in which the book was taken. How can I do this using MySQL Workbench?
You can use MySQL datetime function date_add() to compute the target return_date. Assuming that the date when the book was loaned is called loan_date and that the duration is stored in loan_duration, expressed in days:
select
book_id,
student_id,
loan_date,
date_add(loan_date, interval loan_duration day) return_date
from loan_table
In an update statement:
update loan_table
set return_date = date_add(loan_date, interval loan_duration day)
where return_date is null -- only compute the return_dates that were not yet computed
Edit
If you want that value to be auto-generated, an option is to use a generated stored (or virtual) column (available since MySQL 5.7). This works by defining a computation rule, that MySQL will automatically apply when a record is inserted or updated.
Demo on DB Fiddle:
-- set up
create table loan_table(
id int primary key auto_increment,
book_id int,
student_id int,
loan_date date,
loan_duration int,
-- here is the generated column
return_date date
as (date_add(loan_date, interval loan_duration day)) stored
);
-- perform an insert
insert into loan_table(book_id, student_id, loan_date, loan_duration)
values(1, 1, '2019-11-02', 3)
-- check the computed value
select * from loan_table;
id | book_id | student_id | loan_date | loan_duration | return_date
-: | ------: | ---------: | :--------- | ------------: | :----------
1 | 1 | 1 | 2019-11-02 | 3 | 2019-11-05
You can use a combination of interval and + :
select book_id
, student_id
, date_taken
, number_od_days
, (date_taken + interval number_od_days day) as return_date
from loans;
Here is the example in the DEMO
If you want this column to be auto incremented I suggest you create a trigger in your table:
CREATE TRIGGER rentcheck
BEFORE INSERT ON loans
FOR EACH ROW
begin
if new.number_od_days < 0 or new.number_od_days is null then
signal sqlstate '45000';
else
set new.return_date = (new.date_taken + interval new.number_od_days day);
end if;
end;
With it you can control if the column number_of_days will be negative or not entered because that would result with non logical data. Here is the demo for the trigger: DEMO
you can use variables in mysql:
set #days = 10;
set #mydate := DATE_ADD("2019-11-01", INTERVAL #days DAY);
select #mydate;
and then use your insert statement:
Insert into Loan(... ,[return_date])
values (...., #mydate)
Hope it helps
Related
I want to compare the sum of values in table one with the limit value in table 2 . I tried with constraint or triggers but did not work any help
As in the photo below there are 2 table
table 1 has 2 columns reserve and available
The in these columns come from users and the it must not excees more than the limit on second table ..
As example user can enter any amount where sum of all the column must be less than the limit
As of table one i can enter value in booked column that can be between 0 to the limit in table 2 but if the limit is reached then it cannot add more rows for that element
create table booked (room_id foreign key (room_id),
booked datetime, booked_seats int, remaining_seats);
create table rooms ( room_id primary key
room_size int);
booked seats are done by user, i have seperate trigger for that to ensure user enters corrected,
in want a trigger or something similar that will check the sum of all seats booked in the firsts table and compare with the room_size in second table.
if the sum is less that the size it will edit or else return error
create trigger test after insert on booked
begin
if sum of all (new.booked ) where the id is same > table2.limit then
....
end
For a quick and good answer you need more things than you provided a functioning example data for example
This Trigger will block any attempts to insert if the room_size is smaller than the the sumed up seats.
Please read the end there i explain, where you must put some work in
DELIMITER $$
CREATE TRIGGER check_roomsize_Before_Insert BEFORE insert on booked
FOR EACH ROW
begin
if (SELECT SUM(booked_seats) + NEW.booked_seats FROM booked WHERE room_id = NEW.room_id AND booked = NEW.booked GROUP BY room_id)
> (select room_size from rooms where rooms.room_id= new.room_id) then
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Roomsize too smal!';
end if;
END$$
DELIMITER ;
Schema (MySQL v5.7)
create table rooms ( room_id int primary key,
room_size int);
create table booked (room_id int,
booked datetime, booked_seats int, remaining_seats int, CONSTRAINT fk_category
FOREIGN KEY (room_id)
REFERENCES rooms(room_id));
INSERT INTO rooms VALUES ( 1,5);
DELIMITER $$
CREATE TRIGGER check_roomsize_Before_Insert BEFORE insert on booked
FOR EACH ROW
begin
if (SELECT SUM(booked_seats) + NEW.booked_seats FROM booked WHERE room_id = NEW.room_id AND booked = NEW.booked GROUP BY room_id)
> (select room_size from rooms where rooms.room_id= new.room_id) then
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Roomsize too smal!';
end if;
END$$
DELIMITER ;
INSERT INTO booked VALUES (1,now(),3,2);
#INSERT INTO booked VALUES (1,now(),3,0);
Query #1
SELECT * FROM booked;
| room_id | booked | booked_seats | remaining_seats |
| ------- | ------------------- | ------------ | --------------- |
| 1 | 2020-04-19 20:04:07 | 3 | 2 |
View on DB Fiddle
As you can see in the example 1 row is inserted and the second, gives an exception.
you need to improve the part where i sum up the booked seats there i make a
AND booked = NEW.booked
Because i don't know absolutely what time criteria will count to sum the complete seats number. The now timestamp makes no sense put to test the trigger i need some date.
I am using Below code to add a first day of the month column to the table with an expression to update automatically. But it's throwing a syntax error. someone pls do help.
ALTER TABLE `abc`.`t1`
ADD COLUMN `First_Day` DATE NULL DEFAULT select DATE_ADD(DATE_ADD(LAST_DAY(report_date),
INTERVAL 1 DAY),
INTERVAL - 1 MONTH) AFTER `Totals`;
mysql won't allow to use expressions for setting default values.
you can create trigger for this purpose.
delimiter $$
CREATE TRIGGER test_trigger BEFORE INSERT ON `product`
FOR EACH ROW SET
NEW.myCol= DATE_ADD(DATE_ADD(LAST_DAY(new.report_date),
INTERVAL 1 DAY),
INTERVAL - 1 MONTH);
END$$
delimiter;
I would question why you would wish to store this but if you must a generated column might do.
drop table if exists t;
create table t(id int, report_date date);
ALTER TABLE t
ADD COLUMN `First_Day` DATE as
(date_add(date_add(last_day(date(report_date)),interval 1 day),interval -1 month));
insert into t (id,report_date) values
(1,'2018-01-08'),(2,'2018-02-09');
select * from t;
+------+-------------+------------+
| id | report_date | First_Day |
+------+-------------+------------+
| 1 | 2018-01-08 | 2018-01-01 |
| 2 | 2018-02-09 | 2018-02-01 |
+------+-------------+------------+
2 rows in set (0.00 sec)
I you choose to go this way or via a trigger you will have to write a one off update to populate this column for existing data.
I am using MySQL. I want to insert value's result from groupby of datetime to specific column (using where, maybe). Let say:
I have two tables (a, b). In table a, I want to get how many total records during a hour (which I have datetime column), then the result will insert into table b, but in specific ID (there is already exist ID's value).
This is my error code:
INSERT INTO b(value)
WHERE ID=15
SELECT DAY COUNT(*)
FROM a
WHERE date >= '2015-09-19 00:00:00' AND date < '2015-09-19 00:59:59'
GROUP BY DAY(date),HOUR(date);";
Is that possible I make a query from this case?
Thank you very much for any reply!
Schema
create table tA
( id int auto_increment primary key,
theDate datetime not null,
-- other stuff
key(theDate) -- make it snappy fast
);
create table tB
( myId int primary key, -- by definition PK is not null
someCol int not null
);
-- truncate table tA;
-- truncate table tB;
insert tA(theDate) values
('2015-09-19'),
('2015-09-19 00:24:21'),
('2015-09-19 07:24:21'),
('2015-09-20 00:00:00');
insert tB(myId,someCol) values (15,-1); -- (-1) just for the heck of it
insert tB(myId,someCol) values (16,-1); -- (-1) just for the heck of it
The Query
update tB
set someCol=(select count(*) from tA where theDate between '2015-09-19 00:00:00' and '2015-09-19 00:59:59')
where tB.myId=15;
The Results
select * from tB;
+------+---------+
| myId | someCol |
+------+---------+
| 15 | 2 |
| 16 | -1 |
+------+---------+
only myId=15 is touched.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I make a row generator in mysql
generate_series equivalent in mysql
I've got a trouble with creating mysql Query.
My PHP script executes this query on each run:
INSERT INTO Executes SET UserIp='%s' (%s is user IP)
Executes table is:
ExecuteId UNSIGNED BIGINT AI PRIMARY
Date TIMESTAMP DEFAULT CURRENT_TIMESTAMP INDEX
UserIp CHAR(24) ... | Some Columns
I want to retrive number of Executes in each hour. The most obvious solution would be:
SELECT COUNT(*) as ExecutesNum, DATE(Date) as D, HOUR(Date) as H GROUP BY D, H
And it works, BUT it does not create rows for hours where there were no executes.
What should I modify to get result like:
1 | 2012-09-01 | 14
**0 | 2012-09-01 | 15**
11 | 2012-09-01 | 16
1 | 2012-09-01 | 17
This is a rather common problem, which I usually solve by creating a temporary table containing all the hours, like this:
DROP TABLE IF EXISTS hours;
CREATE TABLE hours (hour VARCHAR(13) PRIMARY KEY);
DROP PROCEDURE IF EXISTS fill_hours;
DELIMITER |
CREATE PROCEDURE fill_hours(start_time DATETIME, end_time DATETIME)
BEGIN
DECLARE crt_time DATETIME;
SET crt_time=DATE_SUB(start_time, INTERVAL DATE_FORMAT(start_time, '%i:%s') MINUTE_SECOND);
WHILE crt_time < end_time DO
INSERT INTO hours VALUES(DATE_FORMAT(crt_time, '%Y-%m-%d-%H'));
SET crt_time = DATE_ADD(crt_time, INTERVAL 1 HOUR);
END WHILE;
END |
CALL fill_hours( (SELECT MIN(Date) FROM Executes), (SELECT MAX(Date) FROM Executes) );
You can then join this table to the original one to get what you want:
SELECT
h.hour,
COUNT(e.ExecuteId)
FROM hours h
LEFT JOIN Executes e ON DATE_FORMAT(e.Date, "%Y-%m-%d-%H") = h.hour
GROUP BY h.hour
Is it possible to move rows that are 3 days old into an other table called "Table_Archive" automatically in mysql ones a week?
tableA ex:
ID | stringvalue | Timestamp
1 | abc | 2011-10-01
2 | abc2 | 2011-10-02
3 | abc3 | 2011-10-05
4 | abc4 | 2011-10-10
5 | abc5 | 2011-10-11
After the move
tableA:
ID | stringvalue | Timestamp
4 | abc4 | 2011-10-10
5 | abc5 | 2011-10-11
Table_Archive:
ID | stringvalue | Timestamp
1 | abc | 2011-10-01
2 | abc2 | 2011-10-02
3 | abc3 | 2011-10-05
And when new input comes into tableA it wont be any problems with ID (PK) in the next move?
What Ive got:
CREATE PROCEDURE clean_tables ()
BEGIN
BEGIN TRANSACTION;
DECLARE _now DATETIME;
SET _now := NOW();
INSERT
INTO Table_Archive
SELECT *
FROM TableA
WHERE timestamp < _now - 3;
FOR UPDATE;
DELETE
FROM TableA
WHERE timestamp < _now - 3;
COMMIT;
END
How do I change _now to be the date 3 days ago?
Personally, I would make use of the MySQL Event Scheduler. This is a built in event scheduler rather like CRON in Linux.
You can specify it to call a procedure, procedures or functions or run a bit of SQL at designated intervals.
Read the MySQL docs but an example would be:
CREATE EVENT mydatabase.myevent
ON SCHEDULE EVERY 1 WEEK STARTS CURRENT_TIMESTAMP + INTERVAL 10 MINUTE
DO
call clean_tables();
So this is saying "call clean_tables() once a week and make the first call in 10 minutes' time"
One gotcha is that the event scheduler is (I think) disabled by default. To turn it on run:
SET GLOBAL event_scheduler = ON;
You can then run:
SHOW PROCESSLIST;
To see whether the event scheduler thread is running.
As for preserving your Table A ID column (if you must). I would keep the ID on Table_Archive as unique to that table i.e make it the primary key & auto_increment and then have a 'Original_TableA_ID' column in which to store the TableA ID. You can put a unique index on this if you want.
So Table_Archive would be like:
create table `Table_Archive` (
ID int unsigned primary key auto_increment, -- < primary key auto increment
tableAId unsigned int not null, -- < id column from TableA
stringValue varchar(100),
timestamp datetime,
UNIQUE KEY `archiveUidx1` (`tableAId`) -- < maintain uniqueness of TableA.ID column in Archive table
);
Nobody seems to have answered your original question "How do I change _now to be the date 3 days ago?". You do that using INTERVAL:
DELIMITER $
CREATE PROCEDURE clean_tables ()
BEGIN
BEGIN TRANSACTION;
DECLARE _now DATETIME;
SET _now := NOW();
INSERT
INTO Table_Archive
SELECT *
FROM TableA
WHERE timestamp < _now - interval 3 day;
FOR UPDATE;
DELETE
FROM TableA
WHERE timestamp < _now - interval 3 day;
COMMIT;
END$
DELIMITER ;
One final point is that you should consider creating an index on the timestamp column on TableA to improve the performance of you clean_tables() procedure.
You may need to have a look into cron jobs if you want that script/query to be executed automatically.
If you are using cpanel have a look into http://www.siteground.com/tutorials/cpanel/cron_jobs.htm
Adding to the best answer (imo) by Tom Mac regarding the event scheduler - be aware that when backing up the schema, you have to specify that you want the events backed up with it via the --events=TRUE flag.
If you're exporting manually in the workbench, the latest version has a checkbox on the main 'Export To Disk' tab - older versions hide it away in the Advanced Export Options tab.
It is possible, MySQL will execute query automatically at specific time using MySQL Event Scheduler. Check this link for more details.
https://dev.mysql.com/doc/refman/5.7/en/event-scheduler.html