I'm trying to figure out the calculation for finding the stock required and also display the volume output if it cant reach the max tresshold in this case volume '1000'.
I will try my best to explain it as plain as possible. (tables and columns are in dutch)
Example:
To produce a volume of 1000 I need multiple raw materials. The raw materials have different totals to go in the 'cooking pot' (and to reach 1000, aqua is added but thats a side note).
1000 is the base for calculating the numbers in the table receptgrondstoffen
First I have the table with the recipe called 'naam' (name)
table: recepten
CREATE TABLE `recepten` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`administratieid` int(11) NOT NULL DEFAULT 0,
`omzetgroepid` int(11) NOT NULL DEFAULT 0,
`artikelgroepid` int(11) NOT NULL DEFAULT 0,
`artikelnummer` int(11) NOT NULL DEFAULT 0 COMMENT 'gevuld vanuit snelstart',
`factornummer` varchar(20) NOT NULL,
`eannummer` varchar(20) NOT NULL,
`naam` varchar(255) NOT NULL,
`notitie` mediumtext NOT NULL,
`volume` decimal(10,5) NOT NULL DEFAULT 0.00000,
`onderzoek` int(1) NOT NULL DEFAULT 0,
`viscositeit` varchar(50) NOT NULL,
`phwaarde` varchar(50) NOT NULL,
`dichtheid` varchar(50) NOT NULL,
`thtmaanden` int(11) NOT NULL DEFAULT 0,
`voorraadcontrole` int(1) NOT NULL DEFAULT 0,
`drempelwaarde` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
INSERT INTO `recepten` (`id`,`administratieid`,`omzetgroepid`,`artikelgroepid`,`artikelnummer`,`factornummer`,`eannummer`,`naam`,`notitie`,`volume`,`onderzoek`,`viscositeit`,`phwaarde`,`dichtheid`,`thtmaanden`,`voorraadcontrole`,`drempelwaarde`) VALUES (1,0,0,702,300001,'122','','test','test',1000.00000,1,'1','2','3',36,0,1);
INSERT INTO `recepten` (`id`,`administratieid`,`omzetgroepid`,`artikelgroepid`,`artikelnummer`,`factornummer`,`eannummer`,`naam`,`notitie`,`volume`,`onderzoek`,`viscositeit`,`phwaarde`,`dichtheid`,`thtmaanden`,`voorraadcontrole`,`drempelwaarde`) VALUES (2,0,0,704,300002,'1234','','test1','test',1000.00000,1,'1','2','3',36,0,100);
Second tables are the recipe items that go into the 'cooking pot'. There are 2 raw material lines. Both have a number of 100 so for each volume of 1000, 100 units from both are required. If I change the volume to 100 then 10 units from both are required.
table: receptgrondstoffen
CREATE TABLE `receptgrondstoffen` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`receptid` int(11) NOT NULL DEFAULT 0,
`grondstofid` int(11) NOT NULL DEFAULT 0,
`aantal` decimal(10,5) NOT NULL DEFAULT 0.00000,
`percentage` decimal(10,5) NOT NULL DEFAULT 0.00000,
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
INSERT INTO `receptgrondstoffen` (`id`,`receptid`,`grondstofid`,`aantal`,`percentage`) VALUES (2,1,1,100.00000,10.00000);
INSERT INTO `receptgrondstoffen` (`id`,`receptid`,`grondstofid`,`aantal`,`percentage`) VALUES (3,1,2,100.00000,10.00000);
The 'grondstofbatch' tables is the raw material quantity that's been bought
Table: grondstofbatch
CREATE TABLE `grondstofbatch` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`grondstofid` int(11) NOT NULL DEFAULT 0,
`leveranciersid` int(11) NOT NULL DEFAULT 0,
`batchnummer` varchar(50) NOT NULL,
`datum` int(10) NOT NULL DEFAULT 0,
`thtdatum` int(10) NOT NULL DEFAULT 0,
`voorraad` int(11) NOT NULL DEFAULT 0,
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
INSERT INTO `grondstofbatch` (`id`,`grondstofid`,`leveranciersid`,`batchnummer`,`datum`,`thtdatum`,`voorraad`) VALUES (1,1,4,'1224-4',1662626077,1665266400,100);
INSERT INTO `grondstofbatch` (`id`,`grondstofid`,`leveranciersid`,`batchnummer`,`datum`,`thtdatum`,`voorraad`) VALUES (2,1,3,'#34423',1662626904,1663970400,300);
INSERT INTO `grondstofbatch` (`id`,`grondstofid`,`leveranciersid`,`batchnummer`,`datum`,`thtdatum`,`voorraad`) VALUES (3,2,3,'#00931',1662626904,1663970400,200);
Volume is the kicker.
What i want, if I use volume amount lets say in this case its 3000 I get a return that the max volume be created is X because not all raw materials are present.
The base is 1000 = 100 so for 3000 it is 300 and only 1 raw material has the required stock. So that means it will be max 2000 volume. And if there is no volume that can be produced then 0.
results;
CREATE TABLE `results` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`volume` int(11) NOT NULL,
`quantity_needed` mediumtext NOT NULL,
`stock` mediumtext NOT NULL,
`result` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
INSERT INTO `results` (`id`,`volume`,`quantity_needed`,`stock`,`result`) VALUES (1,1000,'100,100','400,200',1000);
INSERT INTO `results` (`id`,`volume`,`quantity_needed`,`stock`,`result`) VALUES (2,400,'100,100','400,200',2000);
INSERT INTO `results` (`id`,`volume`,`quantity_needed`,`stock`,`result`) VALUES (3,3000,'100,100','400,200',2000);
INSERT INTO `results` (`id`,`volume`,`quantity_needed`,`stock`,`result`) VALUES (4,500,'100,100','400,200',500);
INSERT INTO `results` (`id`,`volume`,`quantity_needed`,`stock`,`result`) VALUES (5,500,'50,75','400,200',500);
INSERT INTO `results` (`id`,`volume`,`quantity_needed`,`stock`,`result`) VALUES (6,500,'50,75','25,75',250);
INSERT INTO `results` (`id`,`volume`,`quantity_needed`,`stock`,`result`) VALUES (7,500,'30,30','25,75',416);
Hope there is a SQL wizard that can help me out.
I think you are looking for something like
SELECT r.id,
r.volume,
group_concat(rg.aantal ORDER BY rg.id SEPARATOR ',') quantity_needed,
group_concat(g.voorraad ORDER BY rg.id SEPARATOR ',') stock,
min(floor(g.voorraad/rg.aantal)) * r.volume result
FROM recepten r INNER JOIN receptgrondstoffen rg
on r.id = rg.receptid
inner join grondstofbatch g
on g.id = rg.grondstofid
GROUP BY r.id, r.volume
You can see it with your sample data in this fiddle.
(I am possibly misunderstanding your problem since your result data doesn't seem to match your sample data).
The idea here is that, for each recepten, calculate min(floor(grondstofbatch.voorraad/receptgrondstoffen.aantal)). Floor since we want an integer (e.g. if we need 100 units for the recipe and actually have 250 units, floor(250/100) = 2 possible batches). Min since we want the limiting factor (e.g. it doesn't matter if one item in the recipe has enough for 20 batches if another item only has enough for 1 batch).
Hopefully this is in the ballpark of what you're looking for.
Edited: To handle the case where to sum the available quantities I have changed the alias g to be a subquery using the grondstofid table (rather than just the straight grondstofid table). I also realize I was likely joining the grondstofid table incorrectly above (g.id = rg.grondstofid rather than g.grondstofid = rg.grondstofid).
SELECT r.id,
r.volume,
group_concat(rg.aantal ORDER BY rg.id SEPARATOR ',') quantity_needed,
group_concat(g.voorraad ORDER BY rg.id SEPARATOR ',') stock,
min(floor(g.voorraad/rg.aantal)) * r.volume result
FROM recepten r INNER JOIN receptgrondstoffen rg
on r.id = rg.receptid
inner join (SELECT grondstofid, sum(voorraad) voorraad FROM grondstofbatch GROUP BY grondstofid) g
on g.grondstofid = rg.grondstofid
GROUP BY r.id, r.volume
Please take a look at a fiddle of this version
I have 4 tables which look like....
CREATE TABLE `Faculty` (
`FID` varchar(10) NOT NULL,
`Name` varchar(30) NOT NULL,
`DOB` date NOT NULL,
`Sem` varchar(1) NOT NULL,
`Section` varchar(1) NOT NULL,
`Subject Code` varchar(6) NOT NULL,
`Dep` varchar(3) NOT NULL,
`Hours Taken` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Student` (
`USN` varchar(10) NOT NULL,
`DOB` date NOT NULL,
`Dep` varchar(3) NOT NULL,
`SEM` int(1) NOT NULL,
`Class` varchar(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Student Attendance` (
`USN` varchar(10) NOT NULL,
`Subject Code` varchar(6) NOT NULL,
`Attendance` int(11) DEFAULT '0',
`Absent Days` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Subjects` (
`Subject` varchar(40) NOT NULL,
`Subject Code` varchar(6) NOT NULL,
`Dep` varchar(3) NOT NULL,
`Sem` int(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Now I needed to make sure that the number of hours that a student (Student Attendance.Attendance) has attended aren't greater than the number of hours the faculty member has taken (Faculty.Hours Taken) for that particular subject (Subject Code). So I wrote a trigger to check to check on insertion and updation if the above condition is satisfied.
If the number of hours(Student Attendance.Attendance) are greater then I have set the Student Attendance.Attendance to some String. I'm hoping that this will work as an assertion and give me an error.
CREATE TRIGGER `HoursCheckonInsert` BEFORE INSERT ON `Student
Attendance`
FOR EACH ROW SET NEW.Attendance= IF(
( Select k.USN, s.`Subject Code`,f.`Hours Taken`,s.Attendance
From Faculty f, `Student Attendance` s, Student k
where s.`Subject Code` = f.`Subject Code` AND k.SEM = f.Sem AND k.Class=f.Section AND s.USN=k.USN AND
NEW.Attendance < f.`Hours Taken` OR NEW.Attendance = f.`Hours Taken`
),
NEW.Attendance,
'abcdef'
)
CREATE TRIGGER `HoursCheckonUpdate` BEFORE UPDATE ON `Student Attendance
FOR EACH ROW SET NEW.Attendance= IF(
(Select k.USN, s.`Subject Code`,f.`Hours Taken`,s.Attendance
From Faculty f, `Student Attendance` s, Student k
where NEW.`Subject Code`=f.`Subject Code` AND NEW.USN=k.USN AND
NEW.USN=s.USN AND k.SEM=f.Sem AND k.Class=f.Section AND k.DEP=f.DEP
AND (NEW.Attendance < f.`Hours Taken` OR NEW.Attendance = f.`Hours Taken`)
),
NEW.Attendance,
'abcdef'
)
When I try to insert a value into the Student Attendance, I get this error:
INSERT INTO `Student Attendance` (`USN`, `Subject Code`, `Attendance`, `Absent Days`) VALUES ('1KS15BT001', '15BT44', '0', '0');
Error:
#1241 - Operand should contain 1 column(s)
I'm new to triggers in MySQL.
MySQL's IF() function expects a scalar expression as its first argument. A scalar expression means it must be one column, and at most one row.
The subquery you use has four columns, and some unknown number of rows.
From your comments, it sounds like you're just interested in whether there are zero or more than zero rows matching the condition. You're right, it doesn't matter what columns you select—if you're using an EXISTS predicate to test the subquery instead of returning the result of the subquery in a scalar expression.
You should use the EXISTS predicate for what you want to do. See https://dev.mysql.com/doc/refman/5.7/en/exists-and-not-exists-subqueries.html
CREATE TRIGGER `HoursCheckonInsert`
BEFORE INSERT ON `Student Attendance`
FOR EACH ROW
SET NEW.Attendance = IF(
EXISTS(
SELECT *
FROM Faculty AS f
JOIN `Student Attendance` AS s ON s.`Subject Code` = f.`Subject Code`
JOIN Student AS k ON k.SEM = f.Sem AND k.Class=f.Section AND k.uSN = s.USN
WHERE NEW.Attendance <= f.`Hours Taken`),
NEW.Attendance,
'abcdef');
Then it really doesn't matter what columns you put in the select-list. I show using SELECT * above. It doesn't matter, because EXISTS is only checking for the presence of any matching rows, and it won't have a result set at all—only the boolean for whether there are zero or more than zero rows.
You should also use JOIN syntax instead of the obsolete comma-style syntax for joins. I show that in the example above.
Hello this is my structure for tbl_patient:
CREATE TABLE tbl_patient
(
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
idPatient varchar(15) DEFAULT NULL,
namePatient varchar(40) NOT NULL,
age int NOT NULL,
birthDate date,
gender varchar(15) NOT NULL,
status varchar(15) NOT NULL,
address varchar(255) NOT NULL,
work varchar(25) NOT NULL,
phone varchar(15) NOT NULL
)
ENGINE=InnoDb;
I want to put 2 triggers on tbl_patient:
CREATE TRIGGER patientTrigger
BEFORE INSERT ON tbl_patient
FOR EACH ROW
SET NEW.idPatient = CONCAT("PAS-",COALESCE((SELECT MAX(id)+1 from tbl_patient),1));
CREATE TRIGGER ageTrigger
BEFORE INSERT ON tbl_pasien
FOR EACH ROW
SET NEW.age = YEAR(CURDATE()) - YEAR(birthDate);
But I got some error with :
1235 - This version of MariaDB doesn't yet support 'multiple triggers with the same action time and event for one table'
How can I implement that 2 triggers on my table?
You could use computed columns instead:
CREATE TABLE tbl_patient
(
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
idPatient varchar(15) AS (CONCAT("PAS-", id)),
namePatient varchar(40) NOT NULL,
age int AS (YEAR(CURDATE()) - YEAR(birthDate)),
birthDate date,
gender varchar(15) NOT NULL,
status varchar(15) NOT NULL,
address varchar(255) NOT NULL,
work varchar(25) NOT NULL,
phone varchar(15) NOT NULL
)
ENGINE=InnoDb;
Remarks:
1) Note that your code to calculate age is not correct, for example (2015-12-31 and 2016-01-01).
Better way to calcualte age:
SELECT YEAR(NOW()) - YEAR(birthDate) -
(DATE_FORMAT(birthDate, '%m%d') > DATE_FORMAT(NOW(), '%m%d')) AS age
2)
CREATE TRIGGER patientTrigger
BEFORE INSERT ON tbl_patient
FOR EACH ROW
SET NEW.idPatient = CONCAT("PAS-",COALESCE((SELECT MAX(id)+1 from tbl_patient),1));
is poor solution when multiple concurrent INSERTs occur.
EDIT
Using triggers you have update age when user update birthDate (yes it may happen).
Other possible solution is to simply create view.
CREATE VIEW vw_patient
AS
SELECT `id`, `namePatient`, `birthDate`,
`gender`, `status`, `address`, `work`, `phone`,
CONCAT("PAS-", id) AS `idPatient`,
YEAR(NOW()) - YEAR(birthDate) -
(DATE_FORMAT(birthDate, '%m%d') > DATE_FORMAT(NOW(), '%m%d')) AS `age`
FROM `tbl_patient`
SqlFiddleDemo
Try to merge body of both triggers into single one, something like this:
CREATE TRIGGER patientTrigger BEFORE INSERT ON tbl_patient FOR EACH ROW
BEGIN
SET NEW.idPatient = CONCAT("PAS-",COALESCE((SELECT MAX(id)+1 from tbl_patient),1));
SET NEW.age = YEAR(CURDATE()) - YEAR(birthDate);
END
I have a large table with 80 million rows and a trigger that updates two other tables, all of which are TokuDB. The server is running Percona 5.6
CREATE TABLE `main` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ip_addr` varchar(50) NOT NULL DEFAULT '',
`username` varchar(255) NOT NULL DEFAULT ''.
PRIMARY KEY (`id`),
) ENGINE=TokuDB;
The trigger code is
if NEW.ip_addr <> "" THEN
-- get the current oldest date
-- set #maxdate := now();
set #maxdate := (select lastseen from uniq_ip where data = NEW.ip_addr);
INSERT INTO uniq_ip (`data`, `total`, `lastseen`)
VALUES (NEW.ip_addr, 1, NEW.timestamp, #subnet)
ON DUPLICATE KEY UPDATE total = total + 1, lastseen = latest_date(NEW.timestamp, #maxdate);
end if;
-- get all values in one go, its indexed some query comes from index.
if NEW.username <> "" THEN
-- get the current oldest date
set #maxdate := (select lastseen from uniq_username where data = NEW.username);
INSERT INTO uniq_username (`data`, `total`, `lastseen`)
VALUES (NEW.username, 1, NEW.timestamp)
ON DUPLICATE KEY UPDATE total = total + 1, lastseen = latest_date(NEW.timestamp, #maxdate);
end if;
and the uniq_username and uniq_ip are
CREATE TABLE `uniq_ip` (
`data` varchar(42) NOT NULL,
`total` mediumint(4) unsigned NOT NULL DEFAULT '0',
`lastseen` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`data`),
KEY `idx_lastseen` (`data`,`lastseen`)
) ENGINE=TokuDB DEFAULT CHARSET=ascii;
CREATE TABLE `uniq_username` (
`data` varchar(255) CHARACTER SET latin1 NOT NULL,
`total` mediumint(4) unsigned NOT NULL DEFAULT '0',
`lastseen` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`data`),
KEY `idx_data_time` (`data`,`lastseen`)
) ENGINE=TokuDB DEFAULT CHARSET=utf8;
The problem is that a bulk insert under load works perfectly on the username part of the trigger when calculating the timeseen value. However the first part of the function to do the same on the uniq_ip table drops insert rate from 800/s to 30/s when it processes
set #maxdate := (select lastseen from uniq_ip where data = NEW.ip_addr);
If you set to the get now(), its fast (but not the correct result). uniq_username and uniq_ip have the same structure and indexes and the trigger slows right down regardless of which you process first (username or ip) but its only the above statement that slows the trigger down.
The problem below if the uniq_ip table is either TokuDB or InnoDB and the default charset makes no difference, neither does the insert statement being active or commented out. Latest_date() is a tiny function that returns the most recent datetime
Any ideas or tips?
Thanks
main.ip_addr is a latin1 and uniq_ip.data was casting as utf. Changing uniq_ip.data to latin1 improved the insert rate from 50 inserts/sec to 1000 inserts/sec. I guess that latin1->utf8 cast is a cpu killer
I want to subtract between two rows of different table:
I have created a view called leave_taken and table called leave_balance.
I want this result from both table:
leave_taken.COUNT(*) - leave_balance.balance
and group by leave_type_id_leave_type
Code of both table
-----------------View Leave_Taken-----------
CREATE ALGORITHM = UNDEFINED DEFINER=`1`#`localhost` SQL SECURITY DEFINER
VIEW `leave_taken`
AS
select
`leave`.`staff_leave_application_staff_id_staff` AS `staff_leave_application_staff_id_staff`,
`leave`.`leave_type_id_leave_type` AS `leave_type_id_leave_type`,
count(0) AS `COUNT(*)`
from
(
`leave`
join `staff` on((`staff`.`id_staff` = `leave`.`staff_leave_application_staff_id_staff`))
)
where (`leave`.`active` = 1)
group by `leave`.`leave_type_id_leave_type`;
----------------Table leave_balance----------
CREATE TABLE IF NOT EXISTS `leave_balance` (
`id_leave_balance` int(11) NOT NULL AUTO_INCREMENT,
`staff_id_staff` int(11) NOT NULL,
`leave_type_id_leave_type` int(11) NOT NULL,
`balance` int(3) NOT NULL,
`date_added` date NOT NULL,
PRIMARY KEY (`id_leave_balance`),
UNIQUE KEY `id_leave_balance_UNIQUE` (`id_leave_balance`),
KEY `fk_leave_balance_staff1` (`staff_id_staff`),
KEY `fk_leave_balance_leave_type1` (`leave_type_id_leave_type`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
------- Table leave ----------
CREATE TABLE IF NOT EXISTS `leave` (
`id_leave` int(11) NOT NULL AUTO_INCREMENT,
`staff_leave_application_id_staff_leave_application` int(11) NOT NULL,
`staff_leave_application_staff_id_staff` int(11) NOT NULL,
`leave_type_id_leave_type` int(11) NOT NULL,
`date` date NOT NULL,
`active` int(11) NOT NULL DEFAULT '1',
`date_updated` date NOT NULL,
PRIMARY KEY (`id_leave`,`staff_leave_application_id_staff_leave_application`,`staff_leave_application_staff_id_staff`),
KEY `fk_table1_leave_type1` (`leave_type_id_leave_type`),
KEY `fk_table1_staff_leave_application1` (`staff_leave_application_id_staff_leave_application`,`staff_leave_application_staff_id_staff`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=32 ;
Well, I still don't think you've provided enough information. It would be very helpful to have some sample data and your expected output (in tabular format). That said, I may have something you can start working with. This query finds all staff members, calculates their current leave (grouped by type), and determines the difference between that and their balance by leave type. Take a look at it, and more importantly (perhaps) the sqlfiddle here that I used which has the sample data in it (very important to determining if this is the correct path for your data).
SELECT
staff.id_staff,
staff.name,
COUNT(`leave`.id_leave) AS leave_count,
leave_balance.balance,
(COUNT(`leave`.id_leave) - leave_balance.balance) AS leave_difference,
`leave`.leave_type_id_leave_type AS leave_type
FROM
staff
JOIN `leave` ON staff.id_staff = `leave`.staff_leave_application_staff_id_staff
JOIN leave_balance ON
(
staff.id_staff = leave_balance.staff_id_staff
AND `leave`.leave_type_id_leave_type = leave_balance.leave_type_id_leave_type
)
WHERE
`leave`.active = 1
GROUP BY
staff.id_staff, leave_type;
Good luck!