MYSQL calculate volume from raw material units on raw materials in stock - mysql
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
Related
Mysql query to get hotel room availability
I'm implementing a booking platform I have 3 tables: "hotel" - to hold the hotel information "hotel_room" - to hold room info per hotel "hotel_room_price" - have the availability by date, number of rooms available and price I want to search by start date and end date, local and number of rooms (each room have the number of adults and number of child) Here is some example of my tables: CREATE TABLE `hotel` ( `id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `rating` smallint(6) NOT NULL DEFAULT '3' COMMENT '0 - Not Rated | 1 - One Star | 2 - Two Stars | 3 - Three Stars | 4 - Four Stars | 5 - Five Stars', `local` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `hotel_room` ( `id` int(11) NOT NULL, `hotel_id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `max_capacity_adult` smallint(6) NOT NULL, `max_capacity_child` smallint(6) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `hotel_room` ADD CONSTRAINT `fk_hotel_room_hotel_id` FOREIGN KEY (`hotel_id`) REFERENCES `hotel` (`id`) ON DELETE CASCADE; CREATE TABLE `hotel_room_price` ( `id` int(11) NOT NULL, `hotel_room_id` int(11) NOT NULL, `price_adult` decimal(20,2) DEFAULT NULL, `price_child` decimal(20,2) DEFAULT NULL, `quantity` int(11) NOT NULL DEFAULT '1' COMMENT 'available rooms, 0 if there is no more available', `date` date NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `hotel_room_price` ADD CONSTRAINT `fk_hotel_room_price_hotel_room_id` FOREIGN KEY (`hotel_room_id`) REFERENCES `hotel_room` (`id`) ON DELETE CASCADE; What is the better approach get the available rooms when a user search, one note it is possible to search for multiple rooms for example: start_date = 2019-06-25 end_date = 2019-06-29 local = "Tomar" Room=[ [ nr_adults = 2, nr_children=1 ], [ nr_adults = 4, nr_children=0 ] ] I think first thing to do it check only hotels from the right local then check if the room can hold the number of adults and children if yes check for availability. I'm with lot of problems to create a query or multiple queries to handle this in the right way. You can check and example of my database here http://sqlfiddle.com/#!9/458be2c
Here is query that selects all room ID's available in a given time frame. In this instance I picked June 26- June 28. This should be a good starting point for the rest of the query. SELECT hotel_room_id FROM hotel_room_price WHERE date between '2019-06-26' AND '2019-06-28' AND quantity > 0 GROUP BY hotel_room_id HAVING COUNT(*) > DATEDIFF('2019-06-28', '2019-06-26') Here is a somewhat hacky query to get some of the information about the rooms. Note there is not functionality for searching for multiple rooms in this sample: SELECT h.name AS Name, h.rating AS Rating, sq.name AS Type FROM hotel h INNER JOIN (SELECT * FROM hotel_room WHERE hotel_room.id IN (SELECT hotel_room_id FROM hotel_room_price WHERE date between '[START DATE]' AND '[END DATE]' AND quantity > 0 GROUP BY hotel_room_id HAVING COUNT(*) > DATEDIFF('[END DATE]', '[START DATE]')) AND max_capacity_child >= [CHILD COUNT] AND max_capacity_adult >= [ADULT COUNT]) sq ON h.id = sq.hotel_id WHERE h.local = "[LOCATION]"
MySQL: JOIN and WHERE with multiple matches
I would like to select products from products table with the attributes with id 2 and 5 using the following query: SELECT `products`.`title`, `products`.`price` FROM `products` LEFT JOIN `products_attributes_mapping` ON `products`.`id` = `products_attributes_mapping`.`product_id` WHERE `products_attributes_mapping`.`attribute_value_id` IN (2) AND `products_attributes_mapping`.`attribute_value_id` IN (5) GROUP BY `products`.`id` I expect the product 'Example product 1, blue, size 1' to be returned. But I don't get any result, even though the product with id 1 has attribute_value_id 2 and 5 assigned in the products_attributes_mapping table. I use IN because I would like to be able to provide multiple attributes, I simplified it only for the example. SQL fiddle: http://sqlfiddle.com/#!9/2fd94f2/1/0 Schema CREATE TABLE `products` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(255) CHARACTER SET utf8 NOT NULL, `price` double NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4; CREATE TABLE `products_attributes` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; CREATE TABLE `products_attributes_mapping` ( `product_id` int(11) NOT NULL, `attribute_value_id` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `products_attributes_values` ( `id` int(11) NOT NULL AUTO_INCREMENT, `attribute_id` int(11) NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4; Data INSERT INTO `products` VALUES (1,'Example product 1, blue, size 1',10), (2,'Example product 2, yellow, size 1',10), (3,'Example product 3, black, size 2',15), (4,'Example product 4, red, size 2',15); INSERT INTO `products_attributes` VALUES (1,'Color'), (2,'Size'); INSERT INTO `products_attributes_mapping` VALUES (1,2), (1,5), (2,4), (2,5), (3,3), (3,6), (4,1), (4,6); INSERT INTO `products_attributes_values` VALUES (1,1,'red'), (2,1,'blue'), (3,1,'black'), (4,1,'yellow'), (5,2,'1'), (6,2,'2'), (7,2,'3'), (8,2,'4');
Using aggregation could indeed be a solution. You can use a HAVING clause to ensure that a products has certain attribute values: SELECT p.title, p.price FROM products p INNER JOIN products_attributes_mapping pm ON p.id = pm.product_id GROUP BY p.id, p.title, p.price HAVING MAX(pm.attribute_value_id = 2) = 1 AND MAX(pm.attribute_value_id = 5) = 1 In your DB fiddle, this query returns: title | price ---------------------------------|------- Example product 1, blue, size 1 | 10 You can easily extend the expression by adding more AND MAX(...) = 1 conditions. Another option would be to use a series of WHERE EXISTS conditions with correlated subqueries to search the attributes tables. This is just as good, but will expand as a longer query if you need to add many conditions.
Mysql Multiple statements different conditions
I have a mysql table: CREATE TABLE `templates_assignments` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `type` int(11) NOT NULL DEFAULT '2' `assignment_id` int(11) NOT NULL DEFAULT '1', `template_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `template_id` (`template_id`), ) ENGINE=InnoDB AUTO_INCREMENT=122 DEFAULT CHARSET=latin1; /*Data for the table `templates_assignments` */ insert into `templates_assignments` (`id`,`type`,`assignment_id`,`template_id`) values (15,1,1,1), (16,1,1,2), (19,1,1,6), (54,2,30,6), (55,1,5,11), (56,1,5,15), (57,1,5,22); I want to select the template that qualifies for both conditions: type=2 AND assignment_id=30 type=1 AND assignment_id=1 the only template_id that should come back is 6, but i keep getting all or none. My query condition was something like: WHERE ( (templatesAssignments.type=2 AND templatesAssignments.assignment_id=30) AND (templatesAssignments.type=1 AND templatesAssignments.assignment_id=1) ) But no luck...what am i missing?
SELECT ta1.template_id FROM templatesAssignments ta1 INNER JOIN templatesAssignments ta2 ON ta1.template_id = ta2.template_id WHERE (ta1.type=1 AND ta1.assignment_id=1) AND (ta2.type=2 AND ta2.assignment_id=30)
select template_id from templatesAssignments group by template_id having sum(type=2 AND assignment_id=30) > 0 and sum(type=1 AND assignment_id=1) > 0
Optimize tables MySQL
I have a query that is executed in 35s, which is waaaaay too long. Here are the 3 tables concerned by the query (each table is approx. 13000 lines long, and should be much longer in the future) : Table 1 : Domains CREATE TABLE IF NOT EXISTS `domain` ( `id_domain` int(11) NOT NULL AUTO_INCREMENT, `domain_domain` varchar(255) NOT NULL, `projet_domain` int(11) NOT NULL, `date_crea_domain` int(11) NOT NULL, `date_expi_domain` int(11) NOT NULL, `active_domain` tinyint(1) NOT NULL, `remarques_domain` text NOT NULL, PRIMARY KEY (`id_domain`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; Table 2 : Keywords CREATE TABLE IF NOT EXISTS `kw` ( `id_kw` int(11) NOT NULL AUTO_INCREMENT, `kw_kw` varchar(255) NOT NULL, `clics_kw` int(11) NOT NULL, `cpc_kw` float(11,3) NOT NULL, `date_kw` int(11) NOT NULL, PRIMARY KEY (`id_kw`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; Table 3 : Linking between domain and keyword CREATE TABLE IF NOT EXISTS `kw_domain` ( `id_kd` int(11) NOT NULL AUTO_INCREMENT, `kw_kd` int(11) NOT NULL, `domain_kd` int(11) NOT NULL, `selected_kd` tinyint(1) NOT NULL, PRIMARY KEY (`id_kd`), KEY `kw_to_domain` (`kw_kd`,`domain_kd`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; The query is as follows : SELECT ng.*, kd.*, kg.* FROM domain ng LEFT JOIN kw_domain kd ON kd.domain_kd = ng.id_domain LEFT JOIN kw kg ON kg.id_kw = kd.kw_kd GROUP BY ng.id_domain ORDER BY kd.selected_kd DESC, kd.id_kd DESC Basically, it selects all domains, with, for each one of these domains, the last associated keyword. Does anyone have an idea on how to optimize the tables or the query ?
The following will get the last keyword, according to your logic: select ng.*, (select kw_kd from kw_domain kd where kd.domain_kd = ng.id_domain and kd.selected_kd = 1 order by kd.id_kd desc limit 1 ) as kw_kd from domain ng; For performance, you want an index on kw_domain(domain_kd, selected_kd, kw_kd). In this case, the order of the fields matters. You can use this as a subquery to get more information about the keyword: select ng.*, kg.* from (select ng.*, (select kw_kd from kw_domain kd where kd.domain_kd = ng.id_domain and kd.selected_kd = 1 order by kd.id_kd desc limit 1 ) as kw_kd from domain ng ) ng left join kw kg on kg.id_kw = ng.kw_kd; In MySQL, group by can have poor performance, so this might work better, particularly with the right indexes.
mySql subtract row of different table
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!