I have the following procedure:
DROP PROCEDURE IF EXISTS insertCapacityIntoADay;
DELIMITER //
CREATE PROCEDURE insertCapacityIntoADay(startTime TIME, endTime TIME, capacity INT)
BEGIN
INSERT INTO Capacities VALUES(UUID(), startTime, endTime, capacity;
END //
DELIMITER ;
So I can use it like this:
CALL insertCapacityIntoADay('00:00:00', '08:00:00', 120);
My question is, how can I define my procedure, so that when I call it with multiple startTime, endTime, capacity, it adds the value multiple times? i.e. if I do:
CALL insertCapacityIntoADay('00:00:00', '08:00:00', 120, '08:00:00', '20:00:00', 200, '20:00:00', '24:00:00', 150);
it should be equivalent to:
CALL insertCapacityIntoADay('00:00:00', '08:00:00', 120);
CALL insertCapacityIntoADay('08:00:00', '20:00:00', 200);
CALL insertCapacityIntoADay('20:00:00', '24:00:00', 150);
You can emulate array parameters with a CSV string, kind of
CREATE PROCEDURE insertCapacityIntoADay(startTimes varchar(1000), endTimes varchar(1000), capacity varchar(1000))
...
CALL insertCapacityIntoADay('00:00:00, 08:00:00, 20:00:00', '08:00:00, 20:00:00, 24:00:00', '120, 200, 150')
In the proc create a loop which parses the parameters, and issues INSERT accordingly.
I worked out a solution:
DROP PROCEDURE IF EXISTS insertCapacityIntoADay;
DELIMITER //
CREATE PROCEDURE insertCapacityIntoADay(capacities TEXT)
BEGIN
DECLARE startTime VARCHAR(90);
DECLARE endTime VARCHAR(90);
DECLARE capacity VARCHAR(90);
DECLARE maxIterations INT;
DECLARE i INT;
SET maxIterations = 3;
SET i = 1;
-- use a loop to insert values 3 times
loop_label: LOOP
IF i > maxIterations THEN
LEAVE loop_label;
END IF;
-- separate values using comma
SET startTime = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(capacities, ",", i), ',', -1));
SET endTime = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(capacities, ",", i + 1), ',', -1));
SET capacity = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(capacities, ",", i + 2), ',', -1));
INSERT INTO Capacities VALUES(UUID(), startTime, endTime, capacity);
SET i = i + 3;
ITERATE loop_label;
END LOOP loop_label;
END//
DELIMITER ;
SET #capacity1= '00:00:00, 08:00:00, 80, 08:00:00, 15:00:00, 200, 15:00:00, 20:00:00, 150';
CALL insertCapacityIntoADay(#capacity1);
Related
I'm trying to insert multiple rows in a MySQL database. First, I create insert function for address, which connects to another table that only contains the formatted address, so I make inserts for it in another function.
Then in a third function I'm trying to populate a third table with first generating dummy addresses for it. For some reason even the first lower level function (address_insert()) fails to execute due to SQL syntax error and I cannot figure out what it could be. Any ideas?
Thanks!
First function:
DROP PROCEDURE IF EXISTS address_insert;
CREATE PROCEDURE address_insert()
BEGIN
DECLARE i INT DEFAULT (SELECT COUNT(*) FROM hoop.address) + 1;
DECLARE counter INT DEFAULT 0;
WHILE (counter < 5) DO
INSERT INTO hoop.address (adr_id, address, city, country, created_at, lat, lng, updated_at, zip)
VALUES (i, CONCAT("Address-", i), "City", "United States", CURRENT_TIMESTAMP, RAND(35, 50), RAND(80, 120), CURRENT_TIMESTAMP, "ZIPCODE");
SET i = i + 1;
SET counter = counter + 1;
END WHILE;
END;
Second function:
DROP PROCEDURE IF EXISTS address_shop_insert;
CREATE PROCEDURE address_shop_insert()
BEGIN
DECLARE i INT DEFAULT (SELECT COUNT(*) FROM hoop.address_shop) + 1;
DECLARE counter INT DEFAULT 0;
WHILE (counter <= SELECT COUNT(*) FROM hoop.address) DO
INSERT INTO hoop.address_shop_insert (formatted_address, adr_id)
VALUES ("Formatted Address", i);
SET i = i + 1;
SET counter = counter + 1;
END WHILE;
END;
Final function:
DROP PROCEDURE IF EXISTS merchant_shop_insert;
CREATE PROCEDURE merchant_shop_insert()
BEGIN
address_insert()
address_shop_insert()
DECLARE i INT DEFAULT (SELECT COUNT(*) FROM hoop.merchant_shop) + 1;
DECLARE merchant_accounts INT DEFAULT (SELECT COUNT(*) FROM hoop.merchant_account) + 1;
DECLARE shop_address_id DEFAULT (SELECT COUNT(*) FROM hoop.address_shop) - 5;
DECLARE counter INT DEFAULT 1;
DECLARE account_mod INT DEFAULT 0;
DECLARE account_id INT DEFAULT 1;
WHILE (i < merchant_accounts) DO
IF
SELECT MOD(account_mod, 5) = 0
SET account_id = account_id + 1;
END IF;
IF counter = 5
SET shop_address_id = shop_address_id + 1;
SET counter = 1;
END IF;
INSERT INTO hoop.merchant_shop (shop_id, contact_name, contact_phone, created_at, shop_name, status, updated_at, acc_id, shop_adr_id)
VALUES (i, "Strawberry Peach", "+361/789-6544", CURRENT_TIMESTAMP, CONCAT("Shop", i), 1, CURRENT_TIMESTAMP, account_id, shop_address_id)
SET i = i + 1;
SET account_mod = account_mod + 1;
SET counter = counter + 1;
END WHILE;
END;
Since the semi-column ; ends a command, you need to define a specific one for the stored procedure creation, otherwise, MySQL won't know what is the delimiter that tells the end of the declaration of the procedure.
DROP PROCEDURE IF EXISTS address_insert;
-- Set the delimiter to $$
DELIMITER $$
-- Notice that every instructions IN the procedure will be ended by the regular delimiter ;
CREATE PROCEDURE address_insert()
BEGIN
DECLARE i INT DEFAULT (SELECT COUNT(*) FROM hoop.address) + 1;
DECLARE counter INT DEFAULT 0;
WHILE (counter < 5) DO
INSERT INTO hoop.address (adr_id, address, city, country, created_at, lat, lng, updated_at, zip)
VALUES (i, CONCAT("Address-", i), "City", "United States", CURRENT_TIMESTAMP, RAND(35, 50), RAND(80, 120), CURRENT_TIMESTAMP, "ZIPCODE");
SET i = i + 1;
SET counter = counter + 1;
END WHILE;
-- vv------------------------ Notice this
END$$
-- Set it back to ;
DELIMITER ;
In one of my tables fields I contain age ranges. They would be in a format such as
27-51,18-28,10-17
37-55,60-70
1-5,11-16,30-32,60-90
etc
I'm trying to build a SELECT statement where I can search if my given age falls into any of the ranges... something such as
SELECT * from table where age IN (1-5,11-16,30-32,60-90)
However it would search within the given ranges
I can do it if I only have one range using something like...
WHERE age
BETWEEN
SUBSTRING_INDEX(personsAge,"-",1) + 0 AND
SUBSTRING_INDEX(personsAge,"-",-1) + 0
but how can I accomplish this if I have multiple ranges?
This is an answer extending my comment above.
I'm assuming that you can create a function:
Attention: This is for Sql Anywhere. Please adjust the syntax for MySql (especially the locate-function where the parameters are switched). The code is not production ready and I left out some validity checks. I'm assuming that the values in the column are all well formatted.
Attention 2: This is one of those cases where someone dumps a terrible database design on you and demands that you solve the problem. Please avoid creating the need for solutions like this.
Function:
CREATE FUNCTION "DBA"."is_in_range"(age_range varchar(255), age int)
returns int
begin
declare pos int;
declare pos2 int;
declare strPart varchar(50);
declare strFrom varchar(10);
declare strTo varchar(10);
declare iFrom int;
declare iTo int;
while 1 = 1 loop
set pos = locate(age_range, ',');
if pos = 0 then
-- no comma found in the rest of the column value -> just take the whole string
set strPart = age_range;
else
-- take part of the sting until next comma
set strPart = substr(age_range, 1, pos - 1);
end if;
-- we are parsing the min-max part and do some casting to compare with an int
set pos2 = locate(strPart, '-');
set strFrom = substr(strPart, 1, pos2 - 1);
set strTo = substr(strPart, pos2 + 1);
set iFrom = cast(strFrom as int);
set iTo = cast(strTo as int);
if age between iFrom and iTo then
return 1;
end if;
-- if at end of age_range then quit
if pos = 0 then
return 0;
end if;
-- get the next part of the string after the comma
set age_range = substr(age_range, pos + 1);
set pos = locate(age_range, ',', pos);
end loop;
return 0;
end;
Test data:
create local temporary table #tmpRanges (ident int, age_range varchar(255));
insert into #tmpRanges (ident, age_range) values (1, '27-51,18-28,10-17');
insert into #tmpRanges (ident, age_range) values (2, '37-55,60-70');
insert into #tmpRanges (ident, age_range) values (3, '1-5,11-16,30-32,60-90');
insert into #tmpRanges (ident, age_range) values (4, '1-50');
Call:
select * from #tmpRanges where is_in_range(age_range, 51) = 1;
select * from #tmpRanges where is_in_range(age_range, 10) = 1;
select * from #tmpRanges where is_in_range(age_range, 9) = 1;
etc...
This one is a little squirrely!
I have built a stored procedure that works fine. However, when I put it into a Laravel (eloquent) migration, the migration builds the stored procedure but when it is called it gives me an error 1292 (Truncated incorrect DOUBLE value). From mysql workbench, I then right click on the stored procedure built by the migration, copy the create statement, delete the stored procedure and rebuild it from its own create statement.... AND..... EVERYTHING WORKS FINE!
I am open to any and all guesses. I obviously want this stored procedure to be part of my migrations and not have it be something that needs to be built manually. Thanks in advance:
DELIMITER $$
CREATE PROCEDURE sp_cancelTasksOnApplicationCancel(IN loanAppId INT(20))
BEGIN
SELECT loanAppId;
#SET #taskids := 0;
update tasks set task_status = 26, task_status_note = 'Application Cancelled or Denied'
where loan_app_id = loanAppId AND task_status IN (22, 23, 24) AND ( SELECT #taskids := CONCAT_WS(',', id, #taskids) );
#SELECT #taskids;
SET #cleanids = SUBSTRING(#taskids, 1, CHAR_LENGTH(#taskids) -1);
#SELECT #cleanids;
SET #pos = 0;
SET #len = 0;
WHILE LOCATE(',', #cleanids, #pos+1) > 0 DO
SET #len = LOCATE(',', #cleanids,#pos+1) - #pos;
SET #id = SUBSTRING(#cleanids, #pos, #len);
#select (#id);
IF (#id <> 0) THEN
insert into status_historys
(loan_app_id, `type`, type_recid, type_keyfield,type_status_field, type_status_date_field, type_status_note_field, type_status_userid_field, `status`, status_date, status_note, status_userid, created_at, updated_at)
values(loanAppId, 'tasks', #id, 'id', 'task status', 'tasks status datetime', 'tasks status note', 'tasks status userid', 26, now(), 'Application Cancelled or Denied', 1, now(), now());
END IF;
SET #pos = LOCATE(',', #taskids, #pos+#len) + 1;
END WHILE;
END $$
DELIMITER ;
############## UPDATE
The problem lies in the following two lines. Even from the command line it gives a warning about the incorrect double value.
SET #taskids := '0';
update tasks set task_status = 22, task_status_note = 'Just Testing'
where loan_app_id = loanAppId AND task_status IN (22, 23, 24) AND ( SELECT
#taskids := CONCAT_WS(',', cast(id as char), #taskids) );
OK - The main problem was the select keyword in the update where clause. I believe it kept re-declaring the #taskids user variable and mysql gave warnings if the stored procedure was built from the workbench, but failed if the stored procedure was built by a migration.
So the correct code was
#snip#
#Note string '0' rather than 0
SET #taskids := '0';
#note casting of id and removing the select keyword before the first #taskids
update tasks set task_status = 22, task_status_note = 'Just Testing'
where loan_app_id = loanAppId AND task_status IN (22, 23, 24) AND (
#taskids := CONCAT_WS(',', cast(id as char), #taskids) );
#snip#
(SELECT CONCAT(#I, '_Delta') FROM table WHERE id_tb = #ID)
I'm using the above statement as part of an INSERT. The problem is the whole line is being translated to the value of #I (this is an index, values are i.e. 0, 1, 2, ...). So, instead of getting the output of the SELECT I'm getting 0, 1, 2, ...
The expected value of the CONCAT is like "0_Delta", then "1_Delta", etc. Replacing the CONCAT by one of this works.
Any comments will be appreciated. Thanks!
[code]
DROP TABLE IF EXISTS xxx_tb;
CREATE TABLE xxx_tb
(
i_Validity INT,
Delta INT
);
DROP TRIGGER IF EXISTS AFTER_INSERT_ON_tb_in;
DELIMITER $$
CREATE TRIGGER AFTER_INSERT_ON_tb_in
AFTER INSERT ON tb_in
FOR EACH ROW
BEGIN
SET #ID = NEW.id_tb;
SET #TYPE = (SELECT Type FROM header WHERE id_tb = #ID);
IF #TYPE = 'abcd' THEN
SET #SAMPLES = (SELECT SampleNumber FROM table WHERE id_tb = #ID);
IF(#SAMPLES > 1) THEN
SET #I = 0;
WHILE(#I < #SAMPLES) DO
INSERT INTO xxx_tb
(
i_Validity,
Delta
)
VALUES
(
(SELECT 0_Validity FROM table WHERE id_tb = #ID),
(SELECT CONCAT(#I, '_Delta') FROM table WHERE id_tb = #ID)
);
SET #I = #I + 1;
END WHILE;
END IF;
END IF;
END$$
DELIMITER ;
[code]
delta is declared as an integer. You are getting a silent conversion from the string value. Because #i is at the beginning, that is the value you are getting.
You can try declaring it as varchar(255) if you want a string value.
Your insert can be written more easily as an insert . . . select:
INSERT INTO xxx_tb(i_Validity, Delta)
SELECT `0_Validity`, CONCAT(#I, '_Delta')
FROM table WHERE id_tb = #ID);
Multivalue insert example - it works manually but NOT in mySQL stored procedure.
INSERT INTO input_data1(mobile) VALUES (9619825525),(9619825255),(9324198256),(9013000002),(9999999450),(9999999876) ;
i am getting syntax error near "str" word in below proc, Can any one let me know how to implement this multi value INSERT work in procedure?
DELIMITER |
DROP PROCEDURE IF EXISTS mobile_series1;
CREATE PROCEDURE mobile_series1(IN str text)
LANGUAGE SQL READS SQL DATA
BEGIN
DROP TABLE IF EXISTS input_data1 ;
CREATE TEMPORARY TABLE input_data1 (mobile varchar(1000)) engine=memory;
INSERT INTO input_data1(mobile) VALUES str;
END |
DELIMITER ;
Thanks in Advance.
I don't have a MySQL server so there's probably syntax errors and +1 errors (i.e. may not be capturing the last on the list, may not progress past the first item etc, problems fixed by putting a +1 in the code), but you basically want to replace your INSERT statement with something this.
DECLARE INT _CURSOR 0;
DECLARE INT _TOKENLENGTH 0;
DECLARE VARCHAR _TOKEN NULL;
SELECT LOCATE(str, ",", _CURSOR) - _CURSOR INTO _TOKENLENGTH;
LOOP
IF _TOKENLENGTH <= 0 THEN
SELECT RIGHT(str, _CURSOR) INTO _TOKEN;
INSERT INTO input_data1(mobile) VALUE _TOKEN;
LEAVE;
END IF;
SELECT SUBSTRING(str, _CURSOR, _TOKENLENGTH) INTO _TOKEN;
INSERT INTO input_data1(mobile) VALUE _TOKEN;
SELECT _CURSOR + _TOKENLENGTH + 1 INTO _CURSOR;
SELECT LOCATE(str, ",", _CURSOR + 1) - _CURSOR INTO _TOKENLENGTH;
END LOOP;
Your function call would then be something like
EXEC mobile_series1('9619825525,9619825255,9324198256')